/*++ Microsoft Windows Copyright (C) Microsoft Corporation, 1981 - 1999 Module Name: sendprogress.cpp Abstract: Author: Rahul Thombre (RahulTh) 4/30/1998 Revision History: 4/30/1998 RahulTh Created this module. --*/ // SendProgress.cpp : implementation file // #include "precomp.hxx" #include "winsock.h" #define MAX_FILENAME_DISPLAY 35 #define DIALOG_DISPLAY_DURATION 800 //the minimum amount of time a dialog //should be displayed (in milliseconds) #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CSendProgress dialog CSendProgress::CSendProgress(LPTSTR lpszFileList /*=NULL*/, int iCharCount /*=0*/, CWnd* pParent /*=NULL*/) { m_dwMagicID = MAGIC_ID; //an id used to validate CSendProgress pointers //received over an RPC interface. m_lpszFileList = lpszFileList; m_iCharCount = iCharCount; m_lSelectedDeviceID = errIRFTP_SELECTIONCANCELLED; m_lpszSelectedDeviceName[0] = '\0'; m_fSendDone = FALSE; m_fTimerExpired = FALSE; m_ptl = NULL; m_fCancelled = FALSE; appController->PostMessage (WM_APP_KILL_TIMER); InterlockedIncrement (&g_lUIComponentCount); Create(IDD, appController); //{{AFX_DATA_INIT(CSendProgress) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } void CSendProgress::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSendProgress) DDX_Control(pDX, IDC_XFER_PERCENTAGE, m_xferPercentage); DDX_Control(pDX, IDC_CONNECTIONTEXT, m_connectedTo); DDX_Control(pDX, IDC_FILESEND_PROGRESS, m_transferProgress); DDX_Control(pDX, IDC_FILESEND_ANIM, m_transferAnim); DDX_Control(pDX, IDC_FILE_NAME, m_fileName); DDX_Control(pDX, IDC_SENDING_TITLE, m_sndTitle); DDX_Control(pDX, IDCANCEL, m_btnCancel); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CSendProgress, CDialog) //{{AFX_MSG_MAP(CSendProgress) ON_WM_COPYDATA() ON_WM_TIMER() ON_MESSAGE(WM_APP_UPDATE_PROGRESS, OnUpdateProgress) ON_MESSAGE(WM_APP_SEND_COMPLETE, OnSendComplete) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSendProgress message handlers ULONG GetLocationDescription( FAILURE_LOCATION Location ) { switch (Location) { case locStartup: return IDS_LOC_STARTUP; case locConnect: return IDS_LOC_CONNECT; case locFileOpen: return IDS_LOC_FILEOPEN; case locFileRead: return IDS_LOC_FILEREAD; case locFileSend: return IDS_LOC_FILESEND; case locFileRecv: return IDS_LOC_FILERECV; case locFileWrite: return IDS_LOC_FILEWRITE; case locUnknown: default: return IDS_LOC_UNKNOWN; } } BOOL CSendProgress::OnInitDialog() { TCHAR lpszConnectionInfo[100]; TCHAR lpszDeviceName[50]; TCHAR lpszConnecting[50]; error_status_t err; error_status_t sendError; LONG lDeviceID; FAILURE_LOCATION location=locUnknown; CError error; CError xferError (this); CString szConnect; CString szErrorDesc; CString szLocDesc; HRESULT hr = E_FAIL; OBEX_DEVICE_TYPE DeviceType; CDialog::OnInitDialog(); ::LoadString (g_hInstance, IDS_CONNECTEDTO, lpszConnectionInfo, 100); ::LoadString (g_hInstance, IDS_CONNECTING, lpszConnecting, 50); szConnect = lpszConnecting; m_transferProgress.SetPos(0); m_transferAnim.Open (IDR_TRANSFER_AVI); m_transferAnim.Play(0, -1, -1); m_fileName.SetWindowText (L""); m_connectedTo.SetWindowText ((LPCTSTR) szConnect); m_xferPercentage.SetWindowText(TEXT("0%")); if (!g_deviceList.GetDeviceCount()) { error.ShowMessage (IDS_NODEVICES_ERROR); DestroyWindow(); return TRUE; } lDeviceID = g_deviceList.SelectDevice(this, lpszDeviceName); switch(lDeviceID) { case errIRFTP_NODEVICE: error.ShowMessage (IDS_MISSING_RECIPIENT); case errIRFTP_SELECTIONCANCELLED: DestroyWindow(); return TRUE; case errIRFTP_MULTDEVICES: //there were multiple devices in range, one of which was selected if (g_deviceList.GetDeviceType(m_lSelectedDeviceID,&DeviceType)) { SendFiles (g_hIrRpcHandle, (COOKIE)this, TEXT(""), m_lpszFileList, m_iCharCount, m_lSelectedDeviceID, DeviceType, &sendError, &location); lstrcat (lpszConnectionInfo, m_lpszSelectedDeviceName); m_connectedTo.SetWindowText(lpszConnectionInfo); } else { error.ShowMessage (IDS_MISSING_RECIPIENT); DestroyWindow(); return TRUE; } break; default: //there was only one device in range if (g_deviceList.GetDeviceType(lDeviceID,&DeviceType)) { SendFiles (g_hIrRpcHandle, (COOKIE)this, TEXT(""), m_lpszFileList, m_iCharCount, lDeviceID, DeviceType, &sendError, &location); lstrcat (lpszConnectionInfo, lpszDeviceName); m_connectedTo.SetWindowText(lpszConnectionInfo); } else { error.ShowMessage (IDS_MISSING_RECIPIENT); DestroyWindow(); return TRUE; } break; } if (sendError) { LPVOID lpMessageBuffer; TCHAR ErrDesc [ERROR_DESCRIPTION_LENGTH]; CString ErrorDescription; if (!FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, // ignored sendError, 0, // try default language ids (LPTSTR) &lpMessageBuffer, 0, NULL // ignored )) { wsprintf(ErrDesc, g_Strings.ErrorNoDescription, sendError); //using the overloaded CString assignment operator. It is //essentially a string copy, but MFC takes care of allocating //and freeing the destination buffer. ErrorDescription = ErrDesc; } else { //Note: this is not a pointer assignment. We are using the //overloaded CString assignment operator which essentially //does a string copy. (see comments above) ErrorDescription = (TCHAR *) lpMessageBuffer; LocalFree (lpMessageBuffer); } szLocDesc.LoadString (GetLocationDescription(location)); xferError.ShowMessage (IDS_XFER_ERROR, (LPCTSTR) szLocDesc, (LPCTSTR) ErrorDescription); DestroyWindow(); } //there were no errors. //first put up a taskbar button for the dialog //Initialize the taskbar list interface hr = CoInitialize(NULL); if (SUCCEEDED (hr)) hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList, (LPVOID*)&m_ptl); if (SUCCEEDED(hr)) { hr = m_ptl->HrInit(); } else { m_ptl = NULL; } if (m_ptl) { if (SUCCEEDED(hr)) m_ptl->AddTab(m_hWnd); else { m_ptl->Release(); m_ptl = NULL; } } //Also set a timer to go off in half a second //this timer is used to ensure that this dialog is displayed for //at least half a second so that users can see that the file has been //sent //if we fail to get a timer, we simply treat this condition as if the //timer has expired m_fTimerExpired = SetTimer (1, DIALOG_DISPLAY_DURATION, NULL)?FALSE:TRUE; return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CSendProgress::PostNcDestroy() { //do some cleanup if (m_lpszFileList) delete [] m_lpszFileList; BOOL fNoUIComponents = (0 == InterlockedDecrement (&g_lUIComponentCount)); if (fNoUIComponents && ! g_deviceList.GetDeviceCount()) { //there are no UI components displayed and there are no devices in //range. Start the timer. If the timer expires, the app. will quit. appController->PostMessage (WM_APP_START_TIMER); } delete this; } //removes the taskbar button if one had been put up BOOL CSendProgress::DestroyWindow() { //stop the animation and close the file. m_transferAnim.Stop(); m_transferAnim.Close(); //if a taskbar button had been put up, remove that first if (m_ptl) { m_ptl->DeleteTab(m_hWnd); m_ptl->Release(); m_ptl = NULL; } //then destroy the window return CWnd::DestroyWindow(); } void CSendProgress::OnCancel() { TCHAR lpszCancel[MAX_PATH]; CancelSend(g_hIrRpcHandle, (COOKIE)this); //important: do not destroy the window here. //must wait until confirmation of the cancel //is received from the other machine. after that //irxfer will call sendcomplete. That is when the //window should get destroyed. otherwise, CWnd pointer //for this window might get reused and messages from //irxfer meant for this window will go to another window //which could cause it to get destroyed without the transmission //getting interrupted. // //but make the user aware that an attempt to cancel the send is being made m_fCancelled = TRUE; m_btnCancel.EnableWindow(FALSE); m_sndTitle.SetWindowText (TEXT("")); m_fileName.SetWindowText (TEXT("")); lpszCancel[0] = '\0'; ::LoadString (g_hInstance, IDS_SENDCANCEL, lpszCancel, MAX_PATH); m_fileName.SetWindowText (lpszCancel); } void CSendProgress::OnTimer (UINT nTimerID) { m_fTimerExpired = TRUE; //we don't need the timer any more. it needs to go off only once KillTimer(1); //half a second has elapsed since we started //if the send is already complete, then it is this routine's //responsibility to destroy the window since the SEND_COMPLETE //notification has already been received if (m_fSendDone) DestroyWindow(); } BOOL CSendProgress::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); SEND_FAILURE_DATA * pData = (SEND_FAILURE_DATA *) (pCopyDataStruct->lpData); LPVOID lpMessageBuffer; TCHAR ErrDesc [ERROR_DESCRIPTION_LENGTH]; CString ErrorDescription; CString szErrorDesc; CString szLocDesc; CError error (this); if (m_fCancelled) { return TRUE; } if (!FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, // ignored pData->Error, 0, // try default language ids (LPTSTR) &lpMessageBuffer, 0, NULL // ignored )) { wsprintf(ErrDesc, g_Strings.ErrorNoDescription, pData->Error); //using the overloaded CString assignment operator. It is //essentially a string copy, but MFC takes care of allocating //and freeing the destination buffer. ErrorDescription = ErrDesc; } else { //Note: this is not a pointer assignment. We are using the //overloaded CString assignment operator which essentially //does a string copy. (see comments above) ErrorDescription = (TCHAR *) lpMessageBuffer; LocalFree (lpMessageBuffer); } szLocDesc.LoadString (GetLocationDescription(pData->Location)); error.ShowMessage (IDS_SEND_FAILURE, pData->FileName, (LPCTSTR) szLocDesc, (LPCTSTR) ErrorDescription); return TRUE; } void CSendProgress::OnUpdateProgress (WPARAM wParam, LPARAM lParam) { TCHAR lpszXferPercentage [50]; wsprintf(lpszXferPercentage, TEXT("%d%%"), (int)lParam); m_xferPercentage.SetWindowText (lpszXferPercentage); m_transferProgress.SetPos((int)lParam); } void CSendProgress::OnSendComplete (WPARAM wParam, LPARAM lParam) { m_fSendDone = TRUE; //make sure that the dialog is displayed for at least half a second if (m_fTimerExpired) DestroyWindow(); } //+-------------------------------------------------------------------------- // // Member: SetCurrentFileName // // Synopsis: displays on the progress dialog the name of the file that is // currently being transmitted // // Arguments: [in] pwszCurrFile : name of the file being transmitted // // Returns: nothing // // History: 12/14/1998 RahulTh created // // Notes: // //--------------------------------------------------------------------------- void CSendProgress::SetCurrentFileName (wchar_t * pwszCurrFile) { WCHAR * pwszSource; WCHAR * pwszDest; WCHAR * pwszTemp; int len; int cbStart; int cbEnd; int i; const int cbCenter = 5; if (m_fCancelled) { //the user has already hit cancel on this dialog and we are just //waiting for confirmation from irxfer. Hence we do not change //the file name as that control is now being used to indicate //that we are trying to abort the file transfer return; } if (!pwszCurrFile) { m_fileName.SetWindowText (L""); return; } len = wcslen (pwszCurrFile); if (len > MAX_FILENAME_DISPLAY) { cbStart = MAX_FILENAME_DISPLAY/2 - cbCenter; cbEnd = MAX_FILENAME_DISPLAY - cbStart - cbCenter; pwszTemp = pwszCurrFile + cbStart; pwszTemp [0] = L' '; pwszTemp[1] = pwszTemp[2] = pwszTemp[3] = L'.'; pwszTemp[4] = L' '; for (i = 0, pwszSource = pwszCurrFile + len - cbEnd, pwszDest = pwszTemp + cbCenter; i <= cbEnd; /*account for the terminating NULL too*/ i++, pwszSource++, pwszDest++) *pwszDest = *pwszSource; } m_fileName.SetWindowText (pwszCurrFile); return; }