// // shapi.cpp: client shell util functions // // Copyright(C) Microsoft Corporation 1999-2000 // Author: Nadim Abdo (nadima) // #include "stdafx.h" #define TRC_GROUP TRC_GROUP_UI #define TRC_FILE "shapi.cpp" #include #include "sh.h" #include "aboutdlg.h" #include "commctrl.h" #include "autocmpl.h" #include "shlobj.h" #include "browsedlg.h" #include "commdlg.h" #define DEFAULT_RDP_FILE TEXT("Default.rdp") #define CHANNEL_SUBKEY_NAME TEXT("Addins") #define CHANNEL_NAME_KEY TEXT("Name") // // It is necessary to define multimon.h here so that // the multimon functions in this file go through the // multimon stubs (COMPILE_MULTIMON_STUBS) is defined // in contwnd.cpp // #ifdef OS_WINNT #include "multimon.h" #endif TCHAR CSH::_szBrowseForMore[SH_DISPLAY_STRING_MAX_LENGTH]; CSH::CSH() : _Ut() { DC_MEMSET(&_SH, 0, sizeof(_SH)); _tcscpy(_szFileName, TEXT("")); _tcscpy(_szAppName, TEXT("")); _hAppIcon = NULL; _fFileForConnect = FALSE; _fFileForEdit = FALSE; _fMigrateOnly = FALSE; _hInstance = NULL; _fConnectToConsole = FALSE; _fRegSessionSpecified = FALSE; _hModHHCTRL = NULL; _pFnHtmlHelp = NULL; _hUxTheme = NULL; _pFnEnableThemeDialogTexture = NULL; _fFailedToGetThemeDll = FALSE; } CSH::~CSH() { DC_BEGIN_FN("~CSH"); TRC_ASSERT(_hModHHCTRL == NULL, (TB, _T("HtmlHelp was not cleaned up on exit"))); TRC_ASSERT(_hUxTheme == NULL, (TB, _T("uxtheme was not cleaned up on exit"))); DC_END_FN(); } // // Init shell utilities // DCBOOL CSH::SH_Init(HINSTANCE hInstance) { DC_BEGIN_FN("SH_Init"); DC_TSTRCPY(_SH.regSession, _T("")); _SH.fRegDefault = TRUE; _SH.connectedStringID = UI_IDS_FRAME_TITLE_CONNECTED_DEFAULT; _SH.disconnectedStringID = UI_IDS_APP_NAME; _hInstance = hInstance; // // Load Frequently used resource strings // if (!LoadString( hInstance, UI_IDS_BROWSE_FOR_COMPUTERS, _szBrowseForMore, SH_DISPLAY_STRING_MAX_LENGTH)) { TRC_ERR((TB, _T("Failed to load UI_IDS_BROWSE_FOR_COMPUTERS"))); return FALSE; } if(!LoadString(hInstance, UI_IDS_APP_NAME, _szAppName, SIZECHAR(_szAppName))) { TRC_ERR((TB,_T("LoadString UI_IDS_APP_NAME failed"))); } if (LoadString( hInstance, _SH.disconnectedStringID, _frameTitleStr, SH_FRAME_TITLE_RESOURCE_MAX_LENGTH ) != 0) { // // Successfully loaded the string. Now include the registry // session name. // TRC_DBG((TB, _T("UI frame title loaded OK."))); if (_SH.fRegDefault) { TRC_DBG((TB, _T("Default session"))); DC_TSPRINTF(_fullFrameTitleStr, _frameTitleStr); } else { TRC_DBG((TB, _T("Named session"))); DC_TSPRINTF(_fullFrameTitleStr, _frameTitleStr, _SH.regSession); } } else { TRC_ERR((TB,_T("Failed to find UI frame title"))); _fullFrameTitleStr[0] = (DCTCHAR) 0; } _hAppIcon = NULL; #if defined(OS_WIN32) && !defined(OS_WINCE) _Ut.UT_ReadRegistryString(_SH.regSession, SH_ICON_FILE, _T(""), _SH.szIconFile, MAX_PATH); _SH.iconIndex = _Ut.UT_ReadRegistryInt(_SH.regSession, SH_ICON_INDEX, 0); _hAppIcon = ::ExtractIcon(hInstance, _SH.szIconFile, _SH.iconIndex); if(NULL == _hAppIcon) { _hAppIcon = LoadIcon(hInstance, MAKEINTRESOURCE(UI_IDI_ICON)); } #else _hAppIcon = LoadIcon(hInstance, MAKEINTRESOURCE(UI_IDI_ICON)); #endif DC_END_FN(); return TRUE; } /****************************************************************************/ /* Name: SH_ParseCmdParam /* /* Purpose: Parses the supplied cmdline /* /* Params: IN - lpszCmdParam - cmd line to parse /* /* Returns: Parsing status code /* /* SH_PARSECMD_OK - parsed successfully /* SH_PARSECMD_ERR_INVALID_CMD_LINE - generic parse error /* SH_PARSECMD_ERR_INVALID_CONNECTION_PARAM - invalid connect param /* /****************************************************************************/ DWORD CSH::SH_ParseCmdParam(LPTSTR lpszCmdParam) { DWORD dwRet = SH_PARSECMD_ERR_INVALID_CMD_LINE; DC_BEGIN_FN("SHParseCmdParam"); DC_TSTRCPY(_SH.regSession, SH_DEFAULT_REG_SESSION); if(!lpszCmdParam) { dwRet = SH_PARSECMD_ERR_INVALID_CMD_LINE; DC_QUIT; } while (*lpszCmdParam) { while (*lpszCmdParam == _T(' ')) lpszCmdParam++; switch (*lpszCmdParam) { case _T('\0'): break; case _T('-'): case _T('/'): lpszCmdParam = SHGetSwitch(++lpszCmdParam); if(!lpszCmdParam) { dwRet = SH_PARSECMD_ERR_INVALID_CMD_LINE; DC_QUIT; } break; default: lpszCmdParam = SHGetSession(lpszCmdParam); break; } } SHValidateParsedCmdParam(); // // Figure out if the connection param specified is a file // or a reg key // if (ParseFileOrRegConnectionParam()) { dwRet = SH_PARSECMD_OK; } else { dwRet = SH_PARSECMD_ERR_INVALID_CONNECTION_PARAM; } DC_END_FN(); DC_EXIT_POINT: return dwRet; } DCBOOL CSH::SH_ValidateParams(CTscSettings* pTscSet) { HRESULT hr; BOOL fRet = FALSE; DC_BEGIN_FN("SH_ValidateParams"); // // If the Address is empty, the params are invalid // if(pTscSet) { if (CRdpConnectionString::ValidateServerPart( pTscSet->GetFlatConnectString())) { fRet = TRUE; } } DC_END_FN(); return fRet; } DCVOID CSH::SetServer(PDCTCHAR szServer) { DC_BEGIN_FN("SetServer"); TRC_ASSERT(szServer, (TB,_T("szServer not set"))); if(szServer) { DC_TSTRNCPY( _SH.szServer, szServer, sizeof(_SH.szServer)/sizeof(DCTCHAR)); } DC_END_FN(); } HICON CSH::GetAppIcon() { DC_BEGIN_FN("GetAppIcon"); return _hAppIcon; DC_END_FN(); } // // Read the control version string/cipher strength and store in _SH // DCBOOL CSH::SH_ReadControlVer(IMsRdpClient* pTsControl) { HRESULT hr = E_FAIL; BSTR bsVer; LONG cipher; USES_CONVERSION; DC_BEGIN_FN("SH_ReadControlVer"); TRC_ASSERT(pTsControl, (TB, _T("Null TS CTL\n"))); if(!pTsControl) { return FALSE; } TRACE_HR(pTsControl->get_CipherStrength(&cipher)); if(SUCCEEDED(hr)) { _SH.cipherStrength = (DCINT)cipher; TRACE_HR(pTsControl->get_Version(&bsVer)); if(SUCCEEDED(hr)) { if(bsVer) { LPTSTR szVer = OLE2T(bsVer); _tcsncpy(_SH.szControlVer, szVer, SIZECHAR(_SH.szControlVer)); SysFreeString(bsVer); } else { _tcscpy(_SH.szControlVer, _T("")); } } else { return FALSE; } } else { return FALSE; } DC_END_FN(); return TRUE; } // // Overide the _SH settings with params read in from // the command line // this should be called right after GetRegConfig is // called // // hwnd is the window we are being called for (used to figure // out which multimon screen we are on.) // DCVOID CSH::SH_ApplyCmdLineSettings(CTscSettings* pTscSet, HWND hwnd) { DC_BEGIN_FN("SH_ApplyCmdLineSettings"); #ifdef OS_WINNT HMONITOR hMonitor; MONITORINFO monInfo; #endif // OS_WINNT TRC_ASSERT(pTscSet,(TB,_T("pTscSet is NULL"))); PDCTCHAR szCmdLineServer = GetCmdLineServer(); if(szCmdLineServer[0] != 0) { pTscSet->SetConnectString(szCmdLineServer); // // If a command line server is specified // it means autoconnect // SetAutoConnect(TRUE); } if (_SH.fCommandStartFullScreen) { pTscSet->SetStartFullScreen(TRUE); } DCUINT desktopWidth = DEFAULT_DESKTOP_WIDTH; DCUINT desktopHeight = DEFAULT_DESKTOP_HEIGHT; if (SH_IsScreenResSpecifiedOnCmdLine()) { // // User has specified start size on command line // desktopWidth = GetCmdLineDesktopWidth(); desktopHeight= GetCmdLineDesktopHeight(); if(GetCmdLineStartFullScreen()) { // // StartFullScreen is specified // if(!desktopWidth || !desktopHeight) { // // set the desktop width/height // to the screen size // #ifdef OS_WINNT if (GetSystemMetrics(SM_CMONITORS)) { hMonitor = MonitorFromWindow( hwnd, MONITOR_DEFAULTTONULL); if (hMonitor != NULL) { monInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfo(hMonitor, &monInfo)) { desktopWidth = monInfo.rcMonitor.right - monInfo.rcMonitor.left; desktopHeight = monInfo.rcMonitor.bottom - monInfo.rcMonitor.top; } } } #else desktopWidth = GetSystemMetrics(SM_CXSCREEN); desktopHeight = GetSystemMetrics(SM_CYSCREEN); #endif // OS_WINNT } } if (desktopWidth && desktopHeight) { pTscSet->SetDesktopWidth(desktopWidth); pTscSet->SetDesktopHeight(desktopHeight); if (!_SH.fCommandStartFullScreen) { //If command line w/h specified and fullscreen //not explicitliy stated then disable fullscreen pTscSet->SetStartFullScreen(FALSE); } } } if (_fConnectToConsole) { // Without it we leave it however it was specified in the .rdp file pTscSet->SetConnectToConsole(_fConnectToConsole); } DC_END_FN(); } // // Return true if the screen res was specified // on the command line // DCBOOL CSH::SH_IsScreenResSpecifiedOnCmdLine() { return (_SH.fCommandStartFullScreen || (_SH.commandLineHeight && _SH.commandLineWidth)); } DCBOOL CSH::SH_CanonicalizeServerName(PDCTCHAR szServer) { // Remove leading spaces int strLength = DC_TSTRBYTELEN(szServer); while (_T(' ') == szServer[0]) { strLength -= sizeof(DCTCHAR); memmove(&szServer[0], &szServer[1], strLength); } // Remove trailing spaces -- allow for DBCS strings. // At this stage, the string cannot consist entirely // of spaces. It must have at least one character, // followed by zero or more spaces. int numChars = _tcslen(szServer); while ((numChars != 0) && (_T(' ') == szServer[numChars - 1]) #ifndef UNICODE && (!IsDBCSLeadByte(szServer[numChars - 2])) #endif ) { numChars--; szServer[numChars] = _T('\0'); } //check for "\\" before the server address and remove it and //store the server address without the "\\" into szServer if((szServer[0] == _T('\\')) && (szServer[1]== _T('\\'))) { strLength = DC_TSTRBYTELEN(szServer) - 2*sizeof(DCTCHAR); memmove(&szServer[0], &szServer[2], strLength); } return TRUE; } // // Initializes the combo (hwndSrvCombo) for autocompletion // with the MRU server names in the pTscSet collection. // void CSH::InitServerAutoCmplCombo(CTscSettings* pTscSet, HWND hwndSrvCombo) { DC_BEGIN_FN("InitServerComboEx"); if(pTscSet && hwndSrvCombo) { SendMessage(hwndSrvCombo, CB_LIMITTEXT, SH_MAX_ADDRESS_LENGTH-1, 0); // // This call can be used to re-intialize a combo // so delete any items first // #ifndef OS_WINCE INT ret = 1; while(ret && ret != CB_ERR) { ret = SendMessage(hwndSrvCombo, CBEM_DELETEITEM, 0,0); } #else SendMessage(hwndSrvCombo, CB_RESETCONTENT, 0, 0); #endif for (int i=0; i<=9;++i) { if( _tcsncmp(pTscSet->GetMRUServer(i),_T(""), TSC_MAX_ADDRESS_LENGTH) ) { #ifndef OS_WINCE COMBOBOXEXITEM cbItem; cbItem.mask = CBEIF_TEXT; cbItem.pszText = (PDCTCHAR)pTscSet->GetMRUServer(i); cbItem.iItem = -1; //append #endif if(-1 == SendMessage(hwndSrvCombo, #ifdef OS_WINCE CB_ADDSTRING, 0, (LPARAM)(LPCSTR)(PDCTCHAR)pTscSet->GetMRUServer(i))) #else CBEM_INSERTITEM, 0,(LPARAM)&cbItem)) #endif { TRC_ERR((TB,(_T("Error appending to server dialog box")))); } } } // // Add browse for more option to server combo // #ifndef OS_WINCE COMBOBOXEXITEM cbItem; cbItem.mask = CBEIF_TEXT; cbItem.pszText = CSH::_szBrowseForMore; cbItem.iItem = -1; //append #endif if(-1 == SendMessage(hwndSrvCombo, #ifdef OS_WINCE CB_ADDSTRING, 0,(LPARAM)CSH::_szBrowseForMore)) #else CBEM_INSERTITEM, 0,(LPARAM)&cbItem)) #endif { TRC_ERR((TB,(_T("Error appending to server dialog box")))); } // // Never select the browse for server's item // int numItems = SendMessage(hwndSrvCombo, CB_GETCOUNT, 0,0); if(numItems != 1) { SendMessage( hwndSrvCombo, CB_SETCURSEL, (WPARAM)0,0); } #ifndef OS_WINCE SendMessage( hwndSrvCombo, CBEM_SETEXTENDEDSTYLE, (WPARAM)0, CBES_EX_NOEDITIMAGE ); // // Enable autocomplete // HWND hwndEdit = (HWND)SendMessage( hwndSrvCombo, CBEM_GETEDITCONTROL, 0, 0); CAutoCompl::EnableServerAutoComplete( pTscSet, hwndEdit); #endif #ifdef OS_WINCE //This is to avoid WinCE quirk(bug??) //When the "Browse for more" entry is selected in the combo //and the name of the selected server is programmatically set //in the edit control with SetWindowText in the CBN_SELCHANGE handler //the text is cleared internally because the corresponding entry isnt //present in the list box. This is done only if the CBS_HASSTRINGS flag //is set. But the CBS_HASSTRINGS is always added when the combo box is //created. I am removing the style here so the text isnt cleared by default. SetWindowLong(hwndSrvCombo, GWL_STYLE, GetWindowLong(hwndSrvCombo, GWL_STYLE) & ~CBS_HASSTRINGS); #endif } DC_END_FN(); } // // Return the filename that defines connection // settings. This may be a temp file that has been // automigrated from a reg session. // LPTSTR CSH::GetCmdLineFileName() { return _szFileName; } // // Return path to default.rdp file // BOOL CSH::SH_GetPathToDefaultFile(LPTSTR szPath, UINT nLen) { DC_BEGIN_FN("SH_GetPathToDefaultFile"); if(nLen >= MAX_PATH) { if(SH_GetRemoteDesktopFolderPath(szPath, nLen)) { HRESULT hr = StringCchCat(szPath, nLen, DEFAULT_RDP_FILE); if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } return TRUE; } else { return FALSE; } } else { return FALSE; } DC_END_FN(); } // // Get the path to the remote desktop folder // params: // szPath - receives path // nLen - length of szPath // Returns: // Success flag // // Logic: // 1) Try reg key lookup of the path (first EXPAND_SZ and then SZ) // 2) Ask shell for location of MyDocuments and slap on suffix path // 3) If all else fails try current directory as root + suffix path // // BOOL CSH::SH_GetRemoteDesktopFolderPath(LPTSTR szPath, UINT nLen) { DC_BEGIN_FN("SH_GetRemoteDesktopFolderPath"); HRESULT hr; BOOL fGotPathToMyDocs = FALSE; INT cch; if(nLen >= MAX_PATH) { // // First see if there is a path specified in the registry // LPTSTR szRegPath = NULL; INT len = (INT)nLen; _Ut.UT_ReadRegistryExpandSZ(SH_DEFAULT_REG_SESSION, REMOTEDESKTOPFOLDER_REGKEY, &szRegPath, &len); if(szRegPath) { int cchLen = 0; // User provided a reg key to override default // path so use that hr = StringCchCopy(szPath, nLen - 2, szRegPath); //Free returned buffer LocalFree( szRegPath ); // Check if the string copy was successful. if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } cchLen = _tcslen(szPath); if(szPath[cchLen-1] != _T('\\')) { hr = StringCchCat(szPath, nLen, _T("\\")); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } TRC_NRM((TB,_T("Using path from registry %s"), szPath)); return TRUE; } //Next try non expando key _Ut.UT_ReadRegistryString(SH_DEFAULT_REG_SESSION, REMOTEDESKTOPFOLDER_REGKEY, _T(""), szPath, nLen-2); if(szPath[0] != 0) { int cchLen = 0; cchLen = _tcslen(szPath); if(szPath[cchLen-1] != _T('\\')) { hr = StringCchCat(szPath, nLen, _T("\\")); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } TRC_NRM((TB,_T("Using path from registry %s"), szPath)); return TRUE; } // // Not in registry, fallback on shell // #ifndef OS_WINCE // // It would be cool to use the nice and simple // SHGetFolderPath api but that doesn't work with // all versions of shell32.dll (i.e if you don't have the // IE desktop update you don't get SHGetFolderPath). // // blah. // // LPITEMIDLIST ppidl = NULL; hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &ppidl); if(SUCCEEDED(hr) && ppidl) { hr = SHGetPathFromIDList(ppidl, szPath); TRC_ASSERT(SUCCEEDED(hr), (TB,_T("SHGetPathFromIDList failed: %d"),hr)); if(SUCCEEDED(hr)) { fGotPathToMyDocs = TRUE; } IMalloc* pMalloc; hr = SHGetMalloc(&pMalloc); TRC_ASSERT(SUCCEEDED(hr), (TB,_T("SHGetMalloc failed: %d"),hr)); if(SUCCEEDED(hr)) { pMalloc->Free(ppidl); pMalloc->Release(); } } else { TRC_ERR((TB,_T("SHGetSpecialFolderLocation failed 0x%x"), hr)); } if(!fGotPathToMyDocs) { TRC_ERR((TB,_T("Get path to my docs failed."), _T("Root folder in current directory."))); #ifndef OS_WINCE //Oh well as a last resort, root the folder //in the current directory. Necessary because some early //versions of win95 didn't have a MyDocuments folder if(!GetCurrentDirectory( nLen, szPath)) { TRC_ERR((TB,_T("GetCurrentDirectory failed - 0x%x"), GetLastError())); return FALSE; } #endif } #else TRC_NRM((TB,_T("Using \\Windows directory 0x%x"))); _stprintf(szPath,_T("\\windows")); #endif // // Terminate the path // cch = _tcslen(szPath); if (cch >= 1 && szPath[cch-1] != _T('\\')) { hr = StringCchCat(szPath, nLen, _T("\\")); if (FAILED(hr)) { TRC_ERR((TB, _T("String copy failed: hr = 0x%x"), hr)); return FALSE; } } return TRUE; } else { return FALSE; } DC_END_FN(); } // // A worker function to make life easier in SH_GetPluginDllList. This function // takes a source string, cats it to a destination string, and then appends // a comma. // HRESULT StringCchCatComma(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc) { HRESULT hr; DC_BEGIN_FN("StringCchCatComma"); hr = StringCchCat(pszDest, cchDest, pszSrc); if (FAILED(hr)) { DC_QUIT; } hr = StringCchCat(pszDest, cchDest, _T(",")); if (FAILED(hr)) { DC_QUIT; } DC_EXIT_POINT: DC_END_FN(); return hr; } // // Creates a plugindlls list by enumerating all plugin dlls // in szSession reg entry // // Note, entries are APPENDED to szPlugins // BOOL CSH::SH_GetPluginDllList(LPTSTR szSession, LPTSTR szPlugins, size_t cchSzPlugins) { USES_CONVERSION; DC_BEGIN_FN("GetPluginDllList"); TCHAR subKey[UT_MAX_SUBKEY]; TCHAR sectKey[UT_MAX_SUBKEY]; TCHAR enumKey[UT_MAX_SUBKEY]; TCHAR DLLName[UT_MAX_WORKINGDIR_LENGTH]; BOOL rc; DWORD i; INT enumKeySize; CUT ut; HRESULT hr; TRC_ASSERT(szSession && szPlugins, (TB,_T("Invalid param(s)"))); hr = StringCchPrintf(subKey, SIZECHAR(subKey), _T("%s\\%s"), szSession, CHANNEL_SUBKEY_NAME); if (FAILED(hr)) { TRC_ERR((TB, _T("String printf failed: hr = 0x%x"), hr)); return FALSE; } // // Enumerate the registered DLLs // for (i = 0; ; i++) { enumKeySize = UT_MAX_SUBKEY; rc = ut.UT_EnumRegistry(subKey, i, enumKey, &enumKeySize); // If a section name is returned, read the DLL name from it if (rc) { TRC_NRM((TB, _T("Section name %s found"), enumKey)); hr = StringCchPrintf(sectKey, SIZECHAR(sectKey), _T("%s\\%s"), subKey, enumKey); if (FAILED(hr)) { TRC_ERR((TB, _T("String printf failed: hr = 0x%x"), hr)); return FALSE; } TRC_NRM((TB, _T("Section to read: %s"), sectKey)); // // First try to read as an expandable // string (i.e REG_EXPAND_SZ) // LPTSTR szExpandedName = NULL; INT expandedNameLen=0; if(ut.UT_ReadRegistryExpandSZ(sectKey, CHANNEL_NAME_KEY, &szExpandedName, &expandedNameLen)) { TRC_NRM((TB, _T("Expanded DLL Name read %s"), szExpandedName)); // If a DLL name is returned, append it to the list if (szExpandedName && szExpandedName[0] != 0) { hr = StringCchCatComma(szPlugins, cchSzPlugins, szExpandedName); //Must free returned buffer LocalFree( szExpandedName ); // Check if the string concatenation failed. if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } } } else { memset(DLLName, 0, sizeof(DLLName)); ut.UT_ReadRegistryString(sectKey, CHANNEL_NAME_KEY, TEXT(""), DLLName, UT_MAX_WORKINGDIR_LENGTH); TRC_NRM((TB, _T("DLL Name read %s"), DLLName)); // If a DLL name is returned, append it to the list if (DLLName[0] != 0) { //FIXFIX finite size of szPlugins hr = StringCchCatComma(szPlugins, cchSzPlugins, DLLName); if (FAILED(hr)) { TRC_ERR((TB, _T("String concatenation failed: hr = 0x%x"), hr)); return FALSE; } } } } else { // // No DLL name returned - end of enumeration // break; } } TRC_NRM((TB, _T("Passing list of plugins to load: %s"), szPlugins)); return TRUE; } // // Handle the server combo box drop down // for browse for more... functionality. // this fn is broken out into sh to avoid code duplication // because it is used in both the maindlg and propgeneral // BOOL CSH::HandleServerComboChange(HWND hwndCombo, HWND hwndDlg, HINSTANCE hInst, LPTSTR szPrevText) { int numItems = SendMessage(hwndCombo, CB_GETCOUNT, 0,0); int curSel = SendMessage(hwndCombo, CB_GETCURSEL, 0,0); // // If last item is selected // if(curSel == numItems-1) { INT_PTR nResult = IDCANCEL; SendMessage( hwndCombo, CB_SETCURSEL, (WPARAM)-1,0); CBrowseDlg browseDlg( hwndDlg, hInst); nResult = browseDlg.DoModal(); if (IDOK == nResult) { SetWindowText( hwndCombo, browseDlg.GetServer()); } else { // // Revert to initial // SetWindowText( hwndCombo, szPrevText); } } return TRUE; } // // Fill in certain settings in pTsc with system // defaults. // // E.g if the username is blank, fill that in // with the current username // BOOL CSH::SH_AutoFillBlankSettings(CTscSettings* pTsc) { DC_BEGIN_FN("SH_AutoFillBlankSettings"); TRC_ASSERT(pTsc,(TB,_T("pTsc is null"))); #ifndef OS_WINCE // // TODO: update with UPN user name when // server limit of 20 chars is fixed // if(!_tcscmp(pTsc->GetLogonUserName(), TEXT(""))) { TCHAR szUserName[TSC_MAX_USERNAME_LENGTH]; DWORD dwLen = SIZECHAR(szUserName); if(::GetUserName(szUserName, &dwLen)) { pTsc->SetLogonUserName( szUserName); } else { TRC_ERR((TB,_T("GetUserName failed: %d"), GetLastError())); return FALSE; } } #endif DC_END_FN(); return TRUE; } // // Return TRUE if szFileName exists // BOOL CSH::SH_FileExists(LPTSTR szFileName) { BOOL fExist = FALSE; if(szFileName) { HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile != INVALID_HANDLE_VALUE) { fExist = TRUE; } CloseHandle(hFile); return fExist; } else { return FALSE; } } // // Return TRUE if the settings reg key exists // under HK{CU|LM}\Software\Microsoft\Terminal Server Client\{szKeyName} // BOOL CSH::SH_TSSettingsRegKeyExists(LPTSTR szKeyName) { BOOL fRet = FALSE; HKEY hRootKey; LONG rc; TCHAR szFullKeyName[MAX_PATH]; DC_BEGIN_FN("SH_TSSettingsRegKeyExists"); if (_tcslen(szKeyName) + SIZECHAR(TSC_SETTINGS_REG_ROOT) +1 >= SIZECHAR(szFullKeyName)) { TRC_ERR((TB,_T("szKeyName invalid length"))); fRet = FALSE; DC_QUIT; } // // String lengths are pre-validated // _tcscpy(szFullKeyName,TSC_SETTINGS_REG_ROOT); _tcscat(szFullKeyName, szKeyName); // // First try HKLM // rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szFullKeyName, 0, KEY_READ, &hRootKey); if (ERROR_SUCCESS == rc && hRootKey) { // // Key exists undler HKLM // RegCloseKey(hRootKey); fRet = TRUE; } else { // // Try HKCU // rc = RegOpenKeyEx(HKEY_CURRENT_USER, szFullKeyName, 0, KEY_READ, &hRootKey); if (ERROR_SUCCESS == rc && hRootKey) { RegCloseKey(hRootKey); fRet = TRUE; } } DC_END_FN(); DC_EXIT_POINT: return fRet; } BOOL CSH::SH_DisplayErrorBox(HWND hwndParent, INT errStringID) { DC_BEGIN_FN("SH_DisplayErrorBox"); return SH_DisplayMsgBox(hwndParent, errStringID, MB_ICONERROR | MB_OK); DC_END_FN(); } BOOL CSH::SH_DisplayMsgBox(HWND hwndParent, INT errStringID, INT flags) { DC_BEGIN_FN("SH_DisplayMsgBox"); TCHAR szErr[MAX_PATH]; if (LoadString(_hInstance, errStringID, szErr, SIZECHAR(szErr)) != 0) { MessageBox(hwndParent, szErr, _szAppName, flags); return TRUE; } else { return FALSE; } DC_END_FN(); } BOOL CSH::SH_DisplayErrorBox(HWND hwndParent, INT errStringID, LPTSTR szParam) { DC_BEGIN_FN("SH_DisplayErrorBox"); TRC_ASSERT(szParam,(TB,_T("szParam is null"))); if(!szParam) { return FALSE; } TCHAR szErr[MAX_PATH]; if (LoadString(_hInstance, errStringID, szErr, SIZECHAR(szErr)) != 0) { TCHAR szFormatedErr[MAX_PATH*2]; _stprintf(szFormatedErr, szErr, szParam); MessageBox(hwndParent, szFormatedErr, _szAppName, MB_ICONERROR | MB_OK); return TRUE; } else { return FALSE; } DC_END_FN(); } BOOL CSH::SH_GetNameFromPath(LPTSTR szPath, LPTSTR szName, UINT nameLen) { DC_BEGIN_FN("SH_GetNameFromPath"); #ifndef OS_WINCE if(szPath && szName && nameLen) { short ret = GetFileTitle(szPath, szName, (WORD)nameLen); if(ret != 0) { TRC_ERR((TB,_T("SH_GetNameFromPath failed: %d"),GetLastError())); szName[0] = 0; return FALSE; } else { //Strip out the extension int len = _tcslen(szName); LPTSTR szEnd = &szName[len-1]; while(szEnd >= szName) { if(*szEnd == L'.') { *szEnd = 0; } szEnd--; } return TRUE; } } else { return FALSE; } #else // no GetFileTitle on CE so just cheat _tcsncpy( szName, szPath, nameLen - 1); szName[nameLen-1] = 0; return TRUE; #endif DC_END_FN(); } #ifndef OS_WINCE // // Compute and return the disaplay name of the My Documents folder // BOOL CSH::SH_GetMyDocumentsDisplayName(LPTSTR szName, UINT nLen) { IShellFolder* pshf = NULL; LPITEMIDLIST pidl = NULL; LPITEMIDLIST pidlDocFiles = NULL; HRESULT hr = E_FAIL; ULONG chEaten; STRRET strret; DC_BEGIN_FN("SH_GetMyDocumentsDisplayName"); TRC_ASSERT((szName && nLen),(TB,_T("NULL param(s)"))); if(!szName || !nLen) { return FALSE; } //On failure null string szName[0] = NULL; // // First try the powerful shell way // which will return the correctly localized // name. If this fails (due to shell issues) // then fall back on a technique that is guaranteed // to work but may in some cases give the physical // path instead. // hr = SHGetDesktopFolder( &pshf ); TRC_ASSERT(SUCCEEDED(hr), (TB,_T("SHGetDesktopFolder failed"))); if(FAILED(hr) || !pshf) { DC_QUIT; } // // GUID to MyDocuments folder taken from // MSDN "shell basics - managing the filesystem -" // "my documents and my pictures folder" // hr = pshf->ParseDisplayName( NULL, NULL, L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", &chEaten, &pidlDocFiles, NULL ); if(SUCCEEDED(hr)) { hr = pshf->GetDisplayNameOf( pidlDocFiles, SHGDN_INFOLDER, &strret ); if(SUCCEEDED(hr)) { LPTSTR sz; hr = XStrRetToStrW(&strret, pidl, &sz); if(SUCCEEDED(hr)) { _tcsncpy(szName, sz, nLen); szName[nLen-1] = NULL; CoTaskMemFree(sz); pshf->Release(); return TRUE; } else { TRC_ERR((TB,_T("XStrRetToStrW failed :%d"), hr)); DC_QUIT; } } else { TRC_ERR((TB,_T("GetDisplayNameOf failed :%d"), hr)); //Don't quit, fall back and try the other method } } else { TRC_ERR((TB,_T("ParseDisplayName failed :%d"), hr)); //Don't quit, fall back and try the other method } hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl); if(SUCCEEDED(hr) && pidl) { hr = pshf->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret); if(SUCCEEDED(hr)) { LPTSTR sz; hr = XStrRetToStrW(&strret, pidl, &sz); if(SUCCEEDED(hr)) { _tcsncpy(szName, sz, nLen); szName[nLen-1] = NULL; CoTaskMemFree(sz); pshf->Release(); return TRUE; } else { TRC_ERR((TB,_T("XStrRetToStrW failed :%d"), hr)); DC_QUIT; } } else { TRC_ERR((TB,_T("GetDisplayNameOf failed :%d"), hr)); DC_QUIT; } } else { TRC_ERR((TB,_T("SHGetSpecialFolderLocation failed 0x%x"), hr)); DC_QUIT; } DC_EXIT_POINT: if(pshf) { pshf->Release(); pshf = NULL; } DC_END_FN(); TRC_ERR((TB,_T("failed to get display name"))); return FALSE; } #endif //OS_WINCE // // On demand loads HTML help and displays the client help // if the HTMLHELP is not available, pop a message box to // the user. // SH_Cleanup cleans up HTML help (unloads lib) // on exit // // Return HWND to help window or NULL on failure // // HWND CSH::SH_DisplayClientHelp(HWND hwndOwner, INT helpCommand) { BOOL fHtmlHelpAvailable = FALSE; DC_BEGIN_FN("SH_DisplayClientHelp"); #ifndef OS_WINCE if(!_hModHHCTRL) { _hModHHCTRL = (HMODULE)LoadLibrary(_T("hhctrl.ocx")); if(_hModHHCTRL) { // // Use ANSI version of HTML Help so it always works // on downlevel platforms without uniwrap // _pFnHtmlHelp = (PFNHtmlHelp)GetProcAddress(_hModHHCTRL, "HtmlHelpA"); if(_pFnHtmlHelp) { fHtmlHelpAvailable = TRUE; } else { TRC_ERR((TB,_T("GetProcAddress failed for HtmlHelpA: 0x%x"), GetLastError())); } } else { TRC_ERR((TB,_T("LoadLibrary failed for hhctrl.ocx: 0x%x"), GetLastError())); } } else if (_pFnHtmlHelp) { fHtmlHelpAvailable = TRUE; } if(fHtmlHelpAvailable) { return _pFnHtmlHelp( hwndOwner, MSTSC_HELP_FILE_ANSI, helpCommand, 0L); } else { // // Display a message to the user that HTML help is // not availalbe on their system. // SH_DisplayErrorBox( hwndOwner, UI_IDS_NOHTMLHELP); return NULL; } #else if ((GetFileAttributes(PEGHELP_EXE) != -1) && (GetFileAttributes(TSC_HELP_FILE) != -1)) { CreateProcess(PEGHELP_EXE, MSTSC_HELP_FILE, 0,0,0,0,0,0,0,0); } else { SH_DisplayErrorBox( hwndOwner, UI_IDS_NOHTMLHELP); } #endif DC_END_FN(); #ifdef OS_WINCE return NULL; #endif } BOOL CSH::SH_Cleanup() { DC_BEGIN_FN("SH_Cleanup"); if(_hModHHCTRL) { FreeLibrary(_hModHHCTRL); _pFnHtmlHelp = NULL; _hModHHCTRL = NULL; } if (_hUxTheme) { FreeLibrary(_hUxTheme); _hUxTheme = NULL; _pFnEnableThemeDialogTexture = NULL; } DC_END_FN(); return TRUE; } // // Enable or disable an array of dlg controls // VOID CSH::EnableControls(HWND hwndDlg, PUINT pCtls, const UINT numCtls, BOOL fEnable) { DC_BEGIN_FN("EnableControls"); for(UINT i=0;i= 5) { return TRUE; } } return FALSE; #else //CryptProtectData and CryptUnprotectData are present in all CE configs. //(At least everything with filesys or registry) So return TRUE.always. return TRUE; #endif } // // DataProtect // Protect data for persistence using data protection API // params: // pInData - (in) input bytes to protect // pOutData - (out) output data caller must free // returns: bool status // typedef BOOL (WINAPI* PFNCryptProtectData)( IN DATA_BLOB* pDataIn, IN LPCWSTR szDataDescr, IN OPTIONAL DATA_BLOB* pOptionalEntropy, IN PVOID pvReserved, IN OPTIONAL CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, IN DWORD dwFlags, OUT DATA_BLOB* pDataOut ); BOOL CSH::DataProtect(PDATA_BLOB pInData, PDATA_BLOB pOutData) { #ifndef OS_WINCE HMODULE hCryptLib = NULL; PFNCryptProtectData fnCryptProtectData = NULL; #endif BOOL bRet = TRUE; DC_BEGIN_FN("DataProtect"); TRC_ASSERT( IsCryptoAPIPresent(), (TB,_T("Crytpapi not present shouldn't call DataProtect"))); if (pInData && pInData->cbData && pInData->pbData && pOutData) { #ifndef OS_WINCE hCryptLib = (HMODULE) LoadLibrary( _T("crypt32.dll") ); if (hCryptLib) { fnCryptProtectData = (PFNCryptProtectData) GetProcAddress( hCryptLib, "CryptProtectData"); } else { TRC_ERR((TB,_T("LoadLib for crypt32.dll failed: 0x%x"), GetLastError())); return FALSE; } if (fnCryptProtectData) { if (fnCryptProtectData( pInData, #else if (CryptProtectData( pInData, #endif TEXT("psw"), // DESCRIPTION STRING. NULL, // optional entropy NULL, // reserved NULL, // NO prompting CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI pOutData )) { bRet = TRUE; } else { DWORD dwLastErr = GetLastError(); TRC_ERR((TB,_T("CryptProtectData FAILED error:%d\n"), dwLastErr)); bRet = FALSE; } #ifndef OS_WINCE } else { TRC_ERR((TB,_T("GetProcAddress for CryptProtectData failed: 0x%x"), GetLastError())); bRet = FALSE; } #endif } else { TRC_ERR((TB,_T("Invalid data"))); return FALSE; } #ifndef OS_WINCE if (hCryptLib) { FreeLibrary(hCryptLib); } #endif DC_END_FN(); return bRet; } // // DataUnprotect // UnProtect persisted out data using data protection API // params: // pInData - (in) input bytes to UN protect // cbLen - (in) length of pInData in bytes // ppOutData - (out) output bytes // pcbOutLen - (out) length of output // returns: bool status // // typedef BOOL (WINAPI* PFNCryptUnprotectData)( IN DATA_BLOB* pDataIn, IN LPCWSTR szDataDescr, IN OPTIONAL DATA_BLOB* pOptionalEntropy, IN PVOID pvReserved, IN OPTIONAL CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, IN DWORD dwFlags, OUT DATA_BLOB* pDataOut ); BOOL CSH::DataUnprotect(PDATA_BLOB pInData, PDATA_BLOB pOutData) { #ifndef OS_WINCE HMODULE hCryptLib = NULL; PFNCryptUnprotectData fnCryptUnprotectData = NULL; #endif BOOL bRet = TRUE; DC_BEGIN_FN("DataUnprotect"); TRC_ASSERT( IsCryptoAPIPresent(), (TB,_T("Crytpapi not present shouldn't call DataUnprotect"))); if (pInData && pInData->cbData && pInData->pbData && pOutData) { #ifndef OS_WINCE hCryptLib = (HMODULE) LoadLibrary( _T("crypt32.dll") ); if (hCryptLib) { fnCryptUnprotectData = (PFNCryptUnprotectData) GetProcAddress( hCryptLib, "CryptUnprotectData"); } else { TRC_ERR((TB,_T("LoadLib for crypt32.dll failed: 0x%x"), GetLastError())); return FALSE; } if (fnCryptUnprotectData) { if (fnCryptUnprotectData( pInData, #else if (CryptUnprotectData( pInData, #endif NULL, // no description NULL, // optional entropy NULL, // reserved NULL, // NO prompting CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI pOutData )) { bRet = TRUE; } else { DWORD dwLastErr = GetLastError(); TRC_ERR((TB,_T("fnCryptUnprotectData FAILED error:%d\n"), dwLastErr)); bRet = FALSE; } #ifndef OS_WINCE } else { TRC_ERR((TB,_T("GetProcAddress for CryptUnprotectData failed: 0x%x"), GetLastError())); bRet = FALSE; } #endif } else { TRC_ERR((TB,_T("Invalid data"))); return FALSE; } #ifndef OS_WINCE if (hCryptLib) { FreeLibrary(hCryptLib); } #endif DC_END_FN(); return bRet; } #ifndef OS_WINCE BOOL CALLBACK MaxMonitorSizeEnumProc(HMONITOR hMonitor, HDC hdcMonitor, RECT* prc, LPARAM lpUserData) { LPRECT prcLrg = (LPRECT)lpUserData; if ((prc->right - prc->left) >= (prcLrg->right - prcLrg->left) && (prc->bottom - prc->top) >= (prcLrg->bottom - prcLrg->top)) { *prcLrg = *prc; } return TRUE; } #endif BOOL CSH::GetLargestMonitorRect(LPRECT prc) { DC_BEGIN_FN("GetLargestMonitorRect"); if (prc) { // default screen size prc->top = 0; prc->left = 0; prc->bottom = GetSystemMetrics(SM_CYSCREEN); prc->right = GetSystemMetrics(SM_CXSCREEN); #ifndef OS_WINCE //No multimon on CE if (GetSystemMetrics(SM_CMONITORS)) { //Enumerate and look for a larger monitor EnumDisplayMonitors(NULL, NULL, MaxMonitorSizeEnumProc, (LPARAM) prc); } #endif //OS_WINCE return TRUE; } else { return FALSE; } DC_END_FN(); } BOOL CSH::MonitorRectFromHwnd(HWND hwnd, LPRECT prc) { #ifndef OS_WINCE HMONITOR hMonitor; MONITORINFO monInfo; #endif DC_BEGIN_FN("MonitorRectFromHwnd") // default screen size prc->top = 0; prc->left = 0; prc->bottom = GetSystemMetrics(SM_CYSCREEN); prc->right = GetSystemMetrics(SM_CXSCREEN); #ifndef OS_WINCE // for multi monitor, need to find which monitor the client window // resides, then get the correct screen size of the corresponding // monitor if (GetSystemMetrics(SM_CMONITORS)) { hMonitor = MonitorFromWindow( hwnd, MONITOR_DEFAULTTONULL); if (hMonitor != NULL) { monInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfo(hMonitor, &monInfo)) { *prc = monInfo.rcMonitor; } } } #endif DC_END_FN(); return TRUE; } BOOL CSH::MonitorRectFromNearestRect(LPRECT prcNear, LPRECT prcMonitor) { #ifndef OS_WINCE HMONITOR hMonitor; MONITORINFO monInfo; #endif DC_BEGIN_FN("MonitorRectFromHwnd") // default screen size prcMonitor->top = 0; prcMonitor->left = 0; prcMonitor->bottom = GetSystemMetrics(SM_CYSCREEN); prcMonitor->right = GetSystemMetrics(SM_CXSCREEN); // for multi monitor, need to find which monitor the client window // resides, then get the correct screen size of the corresponding // monitor #ifndef OS_WINCE if (GetSystemMetrics(SM_CMONITORS)) { hMonitor = MonitorFromRect(prcNear, MONITOR_DEFAULTTONEAREST); if (hMonitor != NULL) { monInfo.cbSize = sizeof(MONITORINFO); if (GetMonitorInfo(hMonitor, &monInfo)) { *prcMonitor = monInfo.rcMonitor; } } } #endif DC_END_FN(); return TRUE; } LPTSTR CSH::FormatMessageVAList(LPCTSTR pcszFormat, va_list *argList) { LPTSTR pszOutput; if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, pcszFormat, 0, 0, reinterpret_cast(&pszOutput), 0, argList) == 0) { pszOutput = NULL; } return(pszOutput); } LPTSTR CSH::FormatMessageVArgs(LPCTSTR pcszFormat, ...) { LPTSTR pszOutput; va_list argList; va_start(argList, pcszFormat); pszOutput = FormatMessageVAList(pcszFormat, &argList); va_end(argList); return(pszOutput); } // // Create a hidden file // BOOL CSH::SH_CreateHiddenFile(LPCTSTR szPath) { HANDLE hFile; BOOL fRet = FALSE; DC_BEGIN_FN("SH_CreateHiddenFile"); hFile = CreateFile( szPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, //Creates if !exist FILE_ATTRIBUTE_HIDDEN, NULL); if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); fRet = TRUE; } else { TRC_ERR((TB, _T("CreateFile failed: %s - err:%x"), szPath, GetLastError())); fRet = FALSE; } DC_END_FN(); return fRet; } BOOL CSH::SH_IsRunningOn9x() { BOOL fRunningOnWin9x = FALSE; DC_BEGIN_FN("SH_IsRunningOn9x"); fRunningOnWin9x = FALSE; OSVERSIONINFO osVersionInfo; osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); //call A version to avoid wrapping if(GetVersionEx(&osVersionInfo)) { fRunningOnWin9x = (osVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } else { fRunningOnWin9x = FALSE; TRC_ERR((TB,_T("GetVersionEx failed: %d\n"), GetLastError())); } DC_END_FN(); return fRunningOnWin9x; } // // Dynamically load and call the EnableThemeDialogTexture API // (since it is not available on all platforms) // HRESULT CSH::SH_ThemeDialogWindow(HWND hwnd, DWORD dwFlags) { HRESULT hr = E_NOTIMPL; DC_BEGIN_FN("SH_ThemeDialogWindow"); if (_fFailedToGetThemeDll) { // // If failed once then bail out to avoid repeatedly // trying to load theme dll that isn't there // DC_QUIT; } if (!_hUxTheme) { _hUxTheme = (HMODULE)LoadLibrary(_T("uxtheme.dll")); if(_hUxTheme) { _pFnEnableThemeDialogTexture = (PFNEnableThemeDialogTexture) #ifndef OS_WINCE GetProcAddress( _hUxTheme, "EnableThemeDialogTexture"); #else GetProcAddress( _hUxTheme, _T("EnableThemeDialogTexture")); #endif if (NULL == _pFnEnableThemeDialogTexture) { _fFailedToGetThemeDll = TRUE; TRC_ERR((TB, _T("Failed to GetProcAddress for EnableThemeDialogTexture"))); } else { TRC_NRM((TB,_T("Got EnableThemeDialogTexture entry point"))); } } else { _fFailedToGetThemeDll = TRUE; TRC_ERR((TB,_T("LoadLibrary failed for uxtheme: 0x%x"), GetLastError())); } } if (_pFnEnableThemeDialogTexture) { hr = _pFnEnableThemeDialogTexture(hwnd, dwFlags); if (FAILED(hr)) { TRC_ERR((TB,_T("_pFnEnableThemeDialogTexture ret 0x%x\n"), hr)); } } DC_EXIT_POINT: DC_END_FN(); return hr; }