/*++ Microsoft Windows Copyright (C) Microsoft Corporation, 1981 - 1999 Module Name: utils.cpp Abstract: Author: Rahul Thombre (RahulTh) 4/30/1998 Revision History: 4/30/1998 RahulTh Created this module. 10/12/98 RahulTh Better error handling capabilities added : CError etc. --*/ #include "precomp.hxx" #define VALIDATE_SEND_COOKIE(cookie) \ { \ __try \ { \ *pStatus = ERROR_INVALID_DATA; \ if (MAGIC_ID != ((CSendProgress *)cookie)->m_dwMagicID) \ return; \ *pStatus = ERROR_SUCCESS; \ } \ __except (EXCEPTION_EXECUTE_HANDLER) \ { \ return; \ } \ } //////////////////////////////////////////////////////////////////////// // //RPC Functions // /////////////////////////////////////////////////////////////////////// void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t Size) { return new char[Size]; } void __RPC_USER MIDL_user_free( void __RPC_FAR * buf) { delete [] buf; } // // wireless link specific errors // ERROR_TO_STRING_ID g_ErrorToStringId[] = { {ERROR_IRTRANP_OUT_OF_MEMORY, IDS_ERROR_NO_MEMORY}, {ERROR_IRTRANP_DISK_FULL, IDS_ERROR_DISK_FULL}, {ERROR_SCEP_CANT_CREATE_FILE, IDS_ERROR_DISK_FULL}, {ERROR_SCEP_ABORT, IDS_ERROR_ABORTED}, {ERROR_SCEP_INVALID_PROTOCOL, IDS_ERROR_PROTOCOL}, {ERROR_SCEP_PDU_TOO_LARGE, IDS_ERROR_PROTOCOL}, {ERROR_BFTP_INVALID_PROTOCOL, IDS_ERROR_PROTOCOL}, {ERROR_BFTP_NO_MORE_FRAGMENTS, IDS_ERROR_PROTOCOL}, {ERROR_SUCCESS, -1} }; //////////////////////////////////////////////////////////////////// // //Miscellaneous useful functions // /////////////////////////////////////////////////////////////////// int ParseFileNames (TCHAR* pszInString, TCHAR* pszFilesList, int& iCharCount) { ASSERT(pszFilesList != NULL); ASSERT(pszInString != NULL); BOOL fInQuotes = FALSE; BOOL fIgnoreSpaces = FALSE; TCHAR* pszSource = pszInString; TCHAR* pszTarget = pszFilesList; int iFileCount = 0; TCHAR curr; //ignore leading whitespaces while(' ' == *pszSource || '\t' == *pszSource) pszSource++; iCharCount = 0; *pszTarget = '\0'; //precautionary measures if ('\0' == *pszSource) //special case : if this was an empty string, return 0 return iFileCount; //parse the string to get filenames while(curr = *pszSource) { if('\"' == curr) { fInQuotes = fInQuotes?FALSE:TRUE; } else if(' ' == curr && !fInQuotes) { if(!fIgnoreSpaces) { *pszTarget++ = 0; iFileCount++; iCharCount++; fIgnoreSpaces = TRUE; } } else { *pszTarget++ = curr; iCharCount++; fIgnoreSpaces = FALSE; } pszSource++; } if(' ' != *(pszSource-1)) //if there was no trailing space { *pszTarget++ = '\0'; //then the last file was not accounted for. iCharCount++; //so we do it here iFileCount++; } *pszTarget++ = '\0'; //should have 2 terminating nulls iCharCount++; return iFileCount; } //+-------------------------------------------------------------------------- // // Function: GetIRRegVal // // Synopsis: gets the specified registry value from the IR subtree in HKCU // // Arguments: [in] szValName : the name of the value. // [in] dwDefVal : the default value to be returned if the read // from the registry fails or if the value is // missing. // // Returns: the actual value stored in the registry or the default value // if the read fails. // // History: 10/27/1999 RahulTh created // // Notes: // //--------------------------------------------------------------------------- DWORD GetIRRegVal (LPCTSTR szValName, DWORD dwDefVal) { HKEY hftKey = NULL; DWORD iSize = sizeof(DWORD); DWORD data = 0; DWORD Status; RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Control Panel\\Infrared\\File Transfer"), 0, KEY_READ, &hftKey); if (!hftKey) return dwDefVal; Status = RegQueryValueEx (hftKey, szValName, NULL, NULL, (LPBYTE)&data, &iSize); if (ERROR_SUCCESS != Status) data = dwDefVal; RegCloseKey (hftKey); return data; } TCHAR* GetFullPathnames (TCHAR* pszPath, //directory in which the files are located const TCHAR* pszFilesList, //NULL separated list of filenames int iFileCount, //number of files in pszFilesList int& iCharCount //number of characters in pszFilesList. also returns the number of chars in the return string ) { int iChars; int iPathLen = lstrlen(pszPath); if (pszPath[iPathLen - 1] != '\\') //append a '\' character to the path if not already present { pszPath[iPathLen++] = '\\'; pszPath[iPathLen] = '\0'; } int iSize = (iChars = iFileCount*iPathLen + iCharCount); TCHAR* pszFilePathList = new TCHAR[iSize]; TCHAR* pszTemp = pszFilePathList; int iLen; while(*pszFilesList) { lstrcpy(pszTemp, pszPath); pszTemp += iPathLen; lstrcpy(pszTemp, pszFilesList); iLen = lstrlen(pszFilesList); pszFilesList += iLen + 1; pszTemp += iLen + 1; } *pszTemp = '\0'; //should be terminated by 2 null characters iCharCount = (int)(pszTemp - pszFilePathList) + 1; //return the actual char count of this string return pszFilePathList; } TCHAR* ProcessOneFile (TCHAR* pszPath, //directory in which the files are located const TCHAR* pszFilesList, //NULL separated list of filenames int iFileCount, //number of files in pszFilesList int& iCharCount //number of characters in pszFilesList. also returns the number of characters in the return string ) { int iFileLen, iPathLen; TCHAR* pszFullFileName; iFileLen = lstrlen (pszFilesList); iPathLen = lstrlen (pszPath); ASSERT (iFileLen); ASSERT (iPathLen); if(':' == pszFilesList[1] //this is an absolute path starting with the drive letter; || ('\\' == pszFilesList[0] && '\\' == pszFilesList[1]) //UNC path ) { pszFullFileName = new TCHAR [iFileLen + 2]; lstrcpy (pszFullFileName, pszFilesList); pszFullFileName[iFileLen + 1] = '\0'; //we need to have 2 terminating nulls iCharCount = iFileLen + 2; } else if('\\' == pszFilesList[0]) //path relative to the root { iCharCount = iFileLen + 2 /*drive letter and colon*/ + 2 /*terminating nulls*/; pszFullFileName = new TCHAR [iCharCount]; pszFullFileName[0] = pszPath[0]; pszFullFileName[1] = pszPath[1]; lstrcpy (pszFullFileName + 2, pszFilesList); pszFullFileName[iCharCount - 1] = '\0'; //we need to have 2 terminating nulls } else //ordinary file name { iCharCount = iPathLen + iFileLen + 2; //2 terminating nulls iCharCount += ('\\' == pszPath[iPathLen - 1])?0:1; //sometimes the path does not have a \ at the end, so we need to add these ourselves pszFullFileName = new TCHAR [iCharCount]; lstrcpy (pszFullFileName, pszPath); if('\\' != pszPath[iPathLen - 1]) //we need to add the \ ourselves { pszFullFileName[iPathLen] = '\\'; lstrcpy(pszFullFileName + iPathLen + 1, pszFilesList); } else lstrcpy (pszFullFileName + iPathLen, pszFilesList); pszFullFileName[iCharCount - 1] = '\0'; //2 terminating nulls } return pszFullFileName; } //+-------------------------------------------------------------------------- // // Function: GetPrimaryAppWindow // // Synopsis: gets the handle to the main window of an existing instance of // irftp // // Arguments: none. // // Returns: handle to the window if it finds one. otherwise NULL. // // History: 6/30/1999 RahulTh created // // Notes: // //--------------------------------------------------------------------------- HWND GetPrimaryAppWindow (void) { HWND hwnd = NULL; int i = 1; //try to find the window for 5 seconds. do { hwnd = FindWindow (L"#32770", //the dialog class MAIN_WINDOW_TITLE); if (hwnd) break; Sleep (500); } while ( i++ <= 10 ); return hwnd; } ///////////////////////////////////////////////////////////////////////// // Initialize the RPC server //////////////////////////////////////////////////////////////////////// BOOL InitRPCServer (void) { DWORD Status; Status = RpcServerRegisterIf( _IrNotifications_v1_0_s_ifspec, 0, 0); if (Status) { return FALSE; } Status = RpcServerUseAllProtseqsIf( RPC_C_PROTSEQ_MAX_REQS_DEFAULT, _IrNotifications_v1_0_s_ifspec, 0); if (Status) { return FALSE; } Status = RpcServerListen( 1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE); if (Status) { return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////////////////// //Connect to the RPC server running on the first instance of the app //this function is called only if this is not the first instance of the app ////////////////////////////////////////////////////////////////////////////////// RPC_BINDING_HANDLE GetRpcHandle (void) { DWORD Status; RPC_BINDING_HANDLE Binding; Status = RpcBindingFromStringBinding(L"ncalrpc:", &Binding); if (Status) return NULL; else return Binding; } /////////////////////////////////////////////////////////////////////////////////////// // RPC Server functions ////////////////////////////////////////////////////////////////////////////////////// void _PopupUI (handle_t Binding) { int nResponse; appController->PostMessage(WM_APP_TRIGGER_UI); return; } void _InitiateFileTransfer (handle_t Binding, ULONG lSize, wchar_t __RPC_FAR lpszFilesList[]) { COPYDATASTRUCT cStruct; cStruct.dwData = lSize; cStruct.cbData = lSize * sizeof(wchar_t); cStruct.lpData = (LPVOID)(lpszFilesList); appController->SendMessage(WM_COPYDATA, (WPARAM)NULL, (LPARAM)(&cStruct)); } void _DisplaySettings (handle_t Binding) { appController->PostMessage(WM_APP_TRIGGER_SETTINGS); } void _UpdateSendProgress ( handle_t RpcBinding, COOKIE Cookie, wchar_t CurrentFile[], __int64 BytesInTransfer, __int64 BytesTransferred, error_status_t* pStatus ) { VALIDATE_SEND_COOKIE (Cookie) CSendProgress* progressDlg = (CSendProgress*)Cookie; int percentComplete; if (BytesInTransfer) { percentComplete = (int)((BytesTransferred*100.0)/BytesInTransfer); } else { percentComplete = 100; } progressDlg->PostMessage(WM_APP_UPDATE_PROGRESS, (WPARAM) 0, (LPARAM) percentComplete); if (100 > percentComplete) { progressDlg->SetCurrentFileName (CurrentFile); } *pStatus = 0; } void _OneSendFileFailed( handle_t RpcBinding, COOKIE Cookie, wchar_t FileName[], error_status_t ErrorCode, FAILURE_LOCATION Location, error_status_t * pStatus ) { VALIDATE_SEND_COOKIE (Cookie) struct SEND_FAILURE_DATA Data; COPYDATASTRUCT cStruct; CWnd* progressDlg = (CWnd*)Cookie; lstrcpy(Data.FileName, FileName); Data.Location = Location; Data.Error = ErrorCode; cStruct.cbData = sizeof(SEND_FAILURE_DATA); cStruct.lpData = &Data; progressDlg->SendMessage(WM_COPYDATA, (WPARAM) 0, (LPARAM)(&cStruct)); *pStatus = 0; } void _SendComplete( handle_t RpcBinding, COOKIE Cookie, __int64 BytesTransferred, error_status_t* pStatus ) { VALIDATE_SEND_COOKIE (Cookie) CWnd* progressDlg = (CWnd*)Cookie; progressDlg->PostMessage(WM_APP_SEND_COMPLETE); *pStatus = 0; } error_status_t _ReceiveInProgress( handle_t RpcBinding, wchar_t MachineName[], COOKIE * pCookie, boolean bSuppressRecvConf ) { struct MSG_RECEIVE_IN_PROGRESS msg; msg.MachineName = MachineName; msg.pCookie = pCookie; msg.bSuppressRecvConf = bSuppressRecvConf; msg.status = ~0UL; appController->SendMessage( WM_APP_RECV_IN_PROGRESS, (WPARAM) &msg ); return msg.status; } error_status_t _GetPermission( handle_t RpcBinding, COOKIE Cookie, wchar_t Name[], boolean fDirectory ) { struct MSG_GET_PERMISSION msg; msg.Cookie = Cookie; msg.Name = Name; msg.fDirectory = fDirectory; msg.status = ~0UL; appController->SendMessage( WM_APP_GET_PERMISSION, (WPARAM) &msg ); return msg.status; } error_status_t _ReceiveFinished( handle_t RpcBinding, COOKIE Cookie, error_status_t Status ) { struct MSG_RECEIVE_FINISHED msg; msg.Cookie = Cookie; msg.ReceiveStatus = Status; msg.status = ~0UL; appController->SendMessage( WM_APP_RECV_FINISHED, (WPARAM) &msg ); return msg.status; } void _DeviceInRange( handle_t RpcBinding, POBEX_DEVICE_LIST device, error_status_t* pStatus ) { appController->PostMessage (WM_APP_KILL_TIMER); BOOL fLinkOnDesktop = (0 != InterlockedIncrement(&g_lLinkOnDesktop)); if(!fLinkOnDesktop) CreateLinks(); else InterlockedDecrement(&g_lLinkOnDesktop); //don't allow the value to exceed 0 g_deviceList = device; *pStatus = 0; } void _NoDeviceInRange( handle_t RpcBinding, error_status_t* pStatus ) { RemoveLinks(); InterlockedDecrement(&g_lLinkOnDesktop); g_deviceList = NULL; if (0 == g_lUIComponentCount) appController->PostMessage (WM_APP_START_TIMER); *pStatus = 0; } void _Message( handle_t RpcBinding, wchar_t String[] ) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); CString szTitle; szTitle.LoadString (IDS_CAPTION); InterlockedIncrement (&g_lUIComponentCount); ::MessageBox (NULL, String, (LPCTSTR) szTitle, MB_OK); BOOL fNoUIComponents = (0 == InterlockedDecrement (&g_lUIComponentCount)); if (appController && 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); } } error_status_t _ShutdownUi(handle_t RpcBinding) { appController->PostMessage( WM_CLOSE ); return 0; } error_status_t _ShutdownRequested( handle_t RpcBinding, boolean * pAnswer ) { WCHAR pwszCaption [50]; WCHAR pwszMessage [MAX_PATH]; *pAnswer = TRUE; if (appController) { appController->PostMessage (WM_APP_KILL_TIMER); } if (! ::LoadString ( g_hInstance, IDS_CAPTION, pwszCaption, 50)) { return ERROR_NOT_ENOUGH_MEMORY; } if (! ::LoadString ( g_hInstance, IDS_SHUTDOWN_MESSAGE, pwszMessage, MAX_PATH)) { return ERROR_NOT_ENOUGH_MEMORY; } //display a message box with YES / NO buttons if (IDYES == ::MessageBox (appController->m_hWnd, pwszMessage, pwszCaption, MB_ICONEXCLAMATION | MB_YESNO | MB_SYSTEMMODAL | MB_SETFOREGROUND)) { *pAnswer = TRUE; } else { *pAnswer = FALSE; } return 0; } ////////////////////////////////////////////////////////////////////////////// //Create links on the desktop and in the Send To menu to this executable file void CreateLinks(void) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); TCHAR lpszFullExeName [MAX_PATH]; TCHAR lpszShortcutName[MAX_PATH]; CString szDesc; szDesc.LoadString (IDS_SHTCUT_DESC); //create the desktop link if (GetShortcutInfo(lpszShortcutName, lpszFullExeName)) CreateShortcut (lpszFullExeName, lpszShortcutName, (LPCTSTR) szDesc); //create the send to link if (GetSendToInfo(lpszShortcutName, lpszFullExeName)) CreateShortcut (lpszFullExeName, lpszShortcutName, (LPCTSTR) szDesc); } ////////////////////////////////////////////////////////////////////////////// // CreateShortcut - uses the shell's IShellLink and IPersistFile interfaces // to create and store a shortcut to the specified object. HRESULT CreateShortcut (LPCTSTR lpszExe, LPCTSTR lpszLink, LPCTSTR lpszDesc) { HRESULT hres; IShellLink* psl; hres = CoInitialize(NULL); if (FAILED(hres)) return hres; // Get a pointer to the IShellLink interface. hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (SUCCEEDED(hres)) { IPersistFile* ppf; // Set the path to the shortcut target and add the // description. psl->SetPath(lpszExe); psl->SetDescription(lpszDesc); // Query IShellLink for the IPersistFile interface for saving the // shortcut in persistent storage. hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) { // Save the link by calling IPersistFile::Save. hres = ppf->Save(lpszLink, TRUE); ppf->Release(); } psl->Release(); } return SUCCEEDED(hres)?S_OK:E_FAIL; } void RemoveLinks (void) { TCHAR lpszShortcutName[2 * MAX_PATH]; TCHAR lpszFullExeName[2 * MAX_PATH]; //delete the desktop shortcut if(GetShortcutInfo (lpszShortcutName, lpszFullExeName)) DeleteFile (lpszShortcutName); //delete the send to shortcut if (GetSendToInfo (lpszShortcutName, lpszFullExeName)) DeleteFile (lpszShortcutName); } BOOL GetShortcutInfo (LPTSTR lpszShortcutName, LPTSTR lpszFullExeName) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); *lpszShortcutName = '\0'; //precautionary measures *lpszFullExeName = '\0'; CString szExe; CString szShtCut; int len; szExe.LoadString (IDS_EXE); szShtCut.LoadString (IDS_DESKTOP_SHTCUT); len = GetSystemDirectory (lpszFullExeName, MAX_PATH); if(0 == len) return FALSE; lstrcat(lpszFullExeName, LPCTSTR (szExe)); if('\0' == g_lpszDesktopFolder[0]) //try once again if we had failed before, or maybe this is the first time { if (FAILED(SHGetSpecialFolderPath(NULL, g_lpszDesktopFolder, CSIDL_DESKTOPDIRECTORY, 0))) { g_lpszDesktopFolder[0] = '\0'; //we failed so give up. return FALSE; } } lstrcpy (lpszShortcutName, g_lpszDesktopFolder); lstrcat (lpszShortcutName, (LPCTSTR) szShtCut); return TRUE; } BOOL GetSendToInfo (LPTSTR lpszSendToName, LPTSTR lpszFullExeName) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); *lpszSendToName = '\0'; //precautionary measures *lpszFullExeName = '\0'; CString szExe; CString szSendTo; int len; szExe.LoadString (IDS_EXE); szSendTo.LoadString (IDS_SENDTO_SHTCUT); len = GetSystemDirectory (lpszFullExeName, MAX_PATH); if (0 == len) return FALSE; lstrcat (lpszFullExeName, (LPCTSTR) szExe); if ('\0' == g_lpszSendToFolder[0]) //try once again if we had failed before, or maybe this is the first time { if (FAILED(SHGetSpecialFolderPath(NULL, g_lpszSendToFolder, CSIDL_SENDTO, 0))) { g_lpszSendToFolder[0] = '\0'; return FALSE; } } lstrcpy (lpszSendToName, g_lpszSendToFolder); lstrcat (lpszSendToName, (LPCTSTR) szSendTo); return TRUE; } //+-------------------------------------------------------------------------- // // Member: CError::ConstructMessage // // Synopsis: this is an internal helper function that constructs a message // from the available error codes it is called by both ShowMessage // // Arguments: [in] argList : the va_list of arguments // [out] szErrMsg : the formatted error message // // Returns: nothing // // History: 10/2/1998 RahulTh created // // Notes: // //--------------------------------------------------------------------------- void CError::ConstructMessage (va_list argList, CString& szErrMsg) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); TCHAR lpszMessage[2048]; szErrMsg.LoadString (m_msgID); wvsprintf (lpszMessage, (LPCTSTR) szErrMsg, argList); szErrMsg = lpszMessage; if (ERROR_SUCCESS != m_winErr) { LPVOID lpMsgBuf; DWORD dwRet; dwRet = ::FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, m_winErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); if (dwRet) { szErrMsg += TEXT("\n\n"); szErrMsg += (LPCTSTR) lpMsgBuf; LocalFree (lpMsgBuf); } } } //+-------------------------------------------------------------------------- // // Member: CError::ShowMessage // // Synopsis: displays an error message in a message box based on the // members of the object // // Arguments: message id for the error + more // // Returns: the return value of the message box // // History: 10/1/1998 RahulTh created // // Notes: if the resultant message is longer than 2048 characters // then result is unpredictable and may also cause AVs. // but this is a limitation of wvsprintf. However, this is not // so bad since we can make sure that we do not have any error // message that exceed this self imposed limit // //--------------------------------------------------------------------------- int CError::ShowMessage (UINT errID, ...) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); va_list argList; CString szErrMsg; CString szTitle; m_msgID = errID; //update the message ID with the new one szTitle.LoadString (m_titleID); va_start (argList, errID); ConstructMessage (argList, szErrMsg); va_end (argList); return ::MessageBox (m_hWndParent, (LPCTSTR)szErrMsg, (LPCTSTR) szTitle, m_nStyle); }