/*++ Microsoft Windows Copyright (C) Microsoft Corporation, 1981 - 1998 Module Name: utils.cxx Abstract: Author: Rahul Thombre (RahulTh) 4/8/1998 Revision History: 4/8/1998 RahulTh Created this module. --*/ #include "precomp.hxx" BOOL IsSpecialDescendant (const long nID, UINT* parentID /*= NULL*/) { BOOL fRetVal; int prntID = -1; switch (nID) { case IDS_MYPICS: prntID = IDS_MYDOCS; break; case IDS_PROGRAMS: prntID = IDS_STARTMENU; break; case IDS_STARTUP: prntID = IDS_PROGRAMS; break; default: prntID = -1; break; } if (fRetVal = (-1 != prntID)) { if (parentID) *parentID = prntID; } return fRetVal; } //this is a helper function for ConvertOldStyleSection(...) which is used //to convert Beta3 style ini files to Win2K style ini files. void SplitRHS (CString& szValue, unsigned long & flags, CString& szPath) { int index; //take some precautions szValue.TrimRight(); szValue.TrimLeft(); szPath.Empty(); flags = 0; if (szValue.IsEmpty()) return; //if we are here, szValue at least contains the flags swscanf ((LPCTSTR)szValue, TEXT("%x"), &flags); //check if there is a path too. index = szValue.Find(' '); //we will find a space only if there is a path too. if (-1 != index) //there is a path too. { szPath = szValue.Mid (index + 1); szPath.TrimLeft(); szPath.TrimRight(); ASSERT (!szPath.IsEmpty()); } } ////////////////////////////////////////////////////////////////////////// // Given a full path name, this routine extracts its display name, viz. // the part of which follows the final \. If there are no \'s in the // full name, then it sets the display name to the full name ////////////////////////////////////////////////////////////////////////// void ExtractDisplayName (const CString& szFullname, CString& szDisplayname) { CString szName; szName = szFullname; //first get rid of any trailing spaces; this might happen in cases //where one is trying to create a shortcut to a network drive and //when resolved to a UNC path, it yields a path ending in a slash //reverse the string so that any trailing slashes will now be at the //head of the string szName.MakeReverse(); //get rid of the leading slashes and spaces of the reversed string szName = szName.Mid ((szName.SpanIncluding(TEXT("\\ "))).GetLength()); //reverse the string again and we will have a string without //any trailing '\' or ' ' szName.MakeReverse(); //with the trailing '\' and spaces removed, we can go about the //business of getting the display name //if \ cannot be found, ReverseFind returns -1 which gives 0 on adding //1, therefore szDisplayname gets the entire name if no \ is found. szDisplayname = szName.Mid (szName.ReverseFind('\\') + 1); } //+-------------------------------------------------------------------------- // // Function: SplitProfileString // // Synopsis: This function takes in a string of the type key=value and // extracts the key and the value from it. // // Arguments: [in] szPair : the key value pair // [out] szKey : the key // [out] szValue : the value // // Returns: S_OK : if everything goes well. // E_FAIL: if the '=' sign cannot be found // // History: 9/28/1998 RahulTh created // // Notes: // //--------------------------------------------------------------------------- HRESULT SplitProfileString (CString szPair, CString& szKey, CString& szValue) { int nEqPos; nEqPos = szPair.Find ('='); if (-1 == nEqPos) return E_FAIL; szKey = szPair.Left(nEqPos); szKey.TrimLeft(); szKey.TrimRight(); szValue = szPair.Mid (nEqPos + 1); szValue.TrimLeft(); szValue.TrimRight(); return S_OK; } //+-------------------------------------------------------------------------- // // Function: ConvertOldStyleSection // // Synopsis: this function looks at an ini file and if does not have the // new ini file format, it reads the old redirect section and // transforms it into the new ini file format which supports // scaleability // // Arguments: [in] szGPTPath : the directory where the ini file resides // [in] pScope : pointer to the scope pane // // Returns: S_OK if it was successful // an error code if it fails // // History: 9/28/1998 RahulTh created // // Notes: This function exists primarily for backward compatibility // with Win2K betas. Might be okay to remove it. // //--------------------------------------------------------------------------- HRESULT ConvertOldStyleSection ( const CString& szGPTPath ) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); CString szIniFile; TCHAR* lpszSection; TCHAR* szEntry; DWORD cbSize = 1024; DWORD cbCopied; CString SectionEntry; CString Key; CString Dir; CString Value; CString Path; CString szStartMenu; CString szPrograms; CString szStartup; ULONG flags; DWORD Status; BOOL bStatus; HRESULT hr; const TCHAR szEveryOne[] = TEXT("s-1-1-0"); //derive the full path of the ini file. szIniFile.LoadString (IDS_INIFILE); szIniFile = szGPTPath + '\\' + szIniFile; //create an empty section lpszSection = new TCHAR [cbSize]; lpszSection[0] = lpszSection[1] = '\0'; switch (CheckIniFormat (szIniFile)) { case S_OK: //this section has already been converted goto ConOldStlSec_CleanupAndQuit; case S_FALSE: break; //has the Redirect section but not the FolderStatus //section, so there is processing to do. case REGDB_E_KEYMISSING: //this means that the function has neither the FolderStatus section //nor the Redirect section, so we just create an empty FolderStatus //section to make processing simpler in future. //ignore any errors here because they don't really cause any harm //however, make sure that the file is pre-created in unicode so that //the WritePrivateProfile* functions don't puke in ANSI PrecreateUnicodeIniFile ((LPCTSTR)szIniFile); WritePrivateProfileSection (TEXT("FolderStatus"), lpszSection, (LPCTSTR) szIniFile); hr = S_OK; goto ConOldStlSec_CleanupAndQuit; } //this means that we need to convert the section ourselves //first load the redirect section do { cbCopied = GetPrivateProfileSection (TEXT("Redirect"), lpszSection, cbSize, (LPCTSTR) szIniFile ); if (cbSize - 2 == cbCopied) { delete [] lpszSection; cbSize *= 2; lpszSection = new TCHAR [cbSize]; continue; } //the section has been successfully loaded if we are here. break; } while (TRUE); //start the conversion process: for (szEntry = lpszSection; *szEntry; szEntry += (lstrlen(szEntry) + 1)) { SectionEntry = szEntry; if (FAILED(hr = SplitProfileString (SectionEntry, Key, Value))) goto ConOldStlSec_CleanupAndQuit; SplitRHS (Value, flags, Path); Path.TrimLeft(); Path.TrimRight(); if (Path.IsEmpty()) Path = TEXT("%USERPROFILE%") + ('\\' + Key); //we used the relative paths for keys in the old style section ExtractDisplayName (Key, Dir); //set the new flags or modify the existing flags to reflect new behavior szStartMenu.LoadString (IDS_STARTMENU); szPrograms.LoadString (IDS_PROGRAMS); szStartup.LoadString (IDS_STARTUP); if (Dir.CompareNoCase (szStartMenu) && //it is not the start menu and Dir.CompareNoCase (szPrograms) && //it is not programs and Dir.CompareNoCase (szStartup)) //it is not the startup folder { flags |= REDIR_SETACLS; //apply acls. this was the default behavior earlier, but not any more } else //it is one of start menu/programs/startup { //move contents is not allowed for start menu and its descendants flags &= ~REDIR_MOVE_CONTENTS; } if ((flags & REDIR_DONT_CARE) && (flags & REDIR_FOLLOW_PARENT)) { //if both flags were present, this implies they are linked together //in the new format, in order to express this, only follow_parent //is required flags &= ~REDIR_DONT_CARE; } Value.Format (TEXT("%x"), flags); bStatus = WritePrivateProfileString (TEXT("FolderStatus"), Dir, Value, (LPCTSTR)szIniFile ); if (bStatus && (!(flags & REDIR_DONT_CARE)) && (!(flags & REDIR_FOLLOW_PARENT))) bStatus = WritePrivateProfileString ((LPCTSTR) Dir, szEveryOne, (LPCTSTR) Path, (LPCTSTR) szIniFile ); if (!bStatus) { Status = GetLastError(); hr = HRESULT_FROM_WIN32 (Status); goto ConOldStlSec_CleanupAndQuit; } } ConOldStlSec_CleanupAndQuit: delete [] lpszSection; return hr; } //+-------------------------------------------------------------------------- // // Function: GetFolderIndex // // Synopsis: given the name of a folder, this function returns its index // in the array of CFileInfo objects in the scope pane // // Arguments: [in] szName : name of the folder // // Returns: the index of the folder or -1 if the name is invalid // // History: 9/28/1998 RahulTh created // // Notes: // //--------------------------------------------------------------------------- LONG GetFolderIndex (const CString& szName) { LONG i; CString szBuiltinFolder; for (i = IDS_DIRS_START; i < IDS_DIRS_END; i++) { szBuiltinFolder.LoadString (i); if (szName.CompareNoCase((LPCTSTR)szBuiltinFolder)) break; } return GETINDEX (i); } //+-------------------------------------------------------------------------- // // Function: CheckIniFormat // // Synopsis: this function examines the sections of an ini file to see // if it supports the new ini file format (that allows for // scaleability) // // Arguments: [in] szIniFile : the full path of the ini file // // Returns: S_OK : if it finds the FolderStatus section // S_FALSE : if it does not find the FolderStatus section but // finds the Redirect section // REGDB_E_KEYMISSING : if it finds neither the FolderStatus // section nor the Redirect section // // History: 9/28/1998 RahulTh created // // Notes: this function exists for backward compatibility with Win2K // Betas. Might be okay to get rid of this eventually. // //--------------------------------------------------------------------------- HRESULT CheckIniFormat (LPCTSTR szIniFile) { DWORD cbSize = 1024; DWORD cbCopied; TCHAR* lpszNames; TCHAR* szSectionName; BOOL fHasFolderStatus = FALSE; BOOL fHasRedirect = FALSE; do { lpszNames = new TCHAR [cbSize]; if (! lpszNames) return E_OUTOFMEMORY; *lpszNames = L'\0'; cbCopied = GetPrivateProfileSectionNames (lpszNames, cbSize, szIniFile); if (cbSize - 2 == cbCopied) //the buffer was not enough. { delete [] lpszNames; cbSize *= 2; //increase the buffer size continue; } break; //if we are here, we are done. } while (TRUE); for (szSectionName = lpszNames; *szSectionName; szSectionName += (lstrlen(szSectionName) + 1)) { if (0 == lstrcmpi(TEXT("FolderStatus"), szSectionName)) { fHasFolderStatus = TRUE; continue; } if (0 == lstrcmpi (TEXT("Redirect"), szSectionName)) { fHasRedirect = TRUE; continue; } } //cleanup dynamically allocated memory before quitting delete [] lpszNames; if (fHasFolderStatus) return S_OK; //if we are here, the file does not have the FolderStatus section if (fHasRedirect) return S_FALSE; //if we are here, then the file has neither the folder status section //nor the Redirect section return REGDB_E_KEYMISSING; } //+-------------------------------------------------------------------------- // // Function: GetIntfromUnicodeString // // Synopsis: converts a unicode string into an integer // // Arguments: [in] szNum : the number represented as a unicode string // [in] Base : the base in which the resultant int is desired // [out] pValue : pointer to the integer representation of the // number // // Returns: STATUS_SUCCESS if successful. // or some other error code // // History: 9/29/1998 RahulTh created // // Notes: // //--------------------------------------------------------------------------- NTSTATUS GetIntFromUnicodeString (const WCHAR* szNum, ULONG Base, PULONG pValue) { CString StrNum; UNICODE_STRING StringW; size_t len; NTSTATUS Status; StrNum = szNum; len = StrNum.GetLength(); StringW.Length = len * sizeof(WCHAR); StringW.MaximumLength = sizeof(WCHAR) * (len + 1); StringW.Buffer = StrNum.GetBuffer(len); Status = RtlUnicodeStringToInteger (&StringW, Base, pValue); return Status; } //+-------------------------------------------------------------------------- // // Function: GetUNCPath // // Synopsis: this function tries to retrieve the UNC path of an item // given its PIDL // // Arguments: [in] lpszPath : the full path to the selected file. // [out] szUNC : the UNC path of the item // // Returns: NO_ERROR if the conversion was successful. // other error codes if not... // // History: 10/1/1998 RahulTh created // 4/12/1999 RahulTh added error code. changed params. // (item id list is no longer passed in) // // Notes: if this function is unsuccessful, then szUNC will contain an // empty string // //--------------------------------------------------------------------------- DWORD GetUNCPath (LPCTSTR lpszPath, CString& szUNC) { TCHAR* lpszUNCName; UNIVERSAL_NAME_INFO* pUNCInfo; DWORD lBufferSize; DWORD retVal = NO_ERROR; szUNC.Empty(); //precautionary measures //we have a path, now we shall try to get a UNC path lpszUNCName = new TCHAR[MAX_PATH]; pUNCInfo = (UNIVERSAL_NAME_INFO*)lpszUNCName; lBufferSize = MAX_PATH * sizeof(TCHAR); retVal = WNetGetUniversalName (lpszPath, UNIVERSAL_NAME_INFO_LEVEL, (LPVOID)pUNCInfo, &lBufferSize); if (ERROR_MORE_DATA == retVal) //MAX_PATH was insufficient to hold the UNC path { delete [] lpszUNCName; lpszUNCName = new TCHAR[lBufferSize/(sizeof(TCHAR)) + 1]; pUNCInfo = (UNIVERSAL_NAME_INFO*)lpszUNCName; retVal = WNetGetUniversalName (lpszPath, UNIVERSAL_NAME_INFO_LEVEL, (LPVOID)pUNCInfo, &lBufferSize); } //at this point we may or may not have a UNC path. //if we do, we return that, or we return whatever we already have if (NO_ERROR == retVal) szUNC = pUNCInfo->lpUniversalName; delete [] lpszUNCName; return retVal; } //+-------------------------------------------------------------------------- // // Function: BrowseCallbackProc // // Synopsis: the callback function for SHBrowseForFolder // // Arguments: see Platform SDK // // Returns: see Platform SDK // // History: 4/9/1999 RahulTh created // // Notes: // //--------------------------------------------------------------------------- int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData ) { CString * pszData; CString szStart; int index; LPITEMIDLIST lpidl = NULL; TCHAR lpszPath [MAX_PATH]; pszData = (CString *) lpData; switch (uMsg) { case BFFM_INITIALIZED: if (pszData) { szStart = *pszData; szStart.TrimRight(); szStart.TrimLeft(); if (! szStart.IsEmpty()) { index = szStart.ReverseFind (L'\\'); if (-1 != index && index > 1) szStart = szStart.Left (index); SendMessage (hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)(LPCTSTR)szStart); } } break; case BFFM_SELCHANGED: //we need to check if we can get the full path to the selected folder. //e.g. if the full path exceeds MAX_PATH, we cannot obtain the path //from the item id list. if we cannot get the path, we should not //enable the OK button. So, over here, as a precaution, we first //disable the OK button. We will enable it only after we get the path. SendMessage (hwnd, BFFM_ENABLEOK, FALSE, FALSE); if (SHGetPathFromIDList((LPCITEMIDLIST)lParam, lpszPath)) { //set the path into the data member and enable the OK button if (lpData) { SendMessage (hwnd, BFFM_ENABLEOK, TRUE, TRUE); *((CString *) lpData) = lpszPath; } } break; default: break; } return 0; } //+-------------------------------------------------------------------------- // // Function: PrecreateUnicodeIniFile // // Synopsis: The WritePrivateProfile* functions do not write in unicode // unless the file already exists in unicode format. Therefore, // this function is used to precreate a unicode file so that // the WritePrivateProfile* functions can preserve the unicodeness. // // Arguments: [in] lpszFilePath : the full path of the ini file. // // Returns: ERROR_SUCCESS if successful. // an error code otherwise. // // History: 7/9/1999 RahulTh created // // Notes: // //--------------------------------------------------------------------------- DWORD PrecreateUnicodeIniFile (LPCTSTR lpszFilePath) { HANDLE hFile; WIN32_FILE_ATTRIBUTE_DATA fad; DWORD Status = ERROR_ALREADY_EXISTS; DWORD dwWritten; if (!GetFileAttributesEx (lpszFilePath, GetFileExInfoStandard, &fad)) { if (ERROR_FILE_NOT_FOUND == (Status = GetLastError())) { hFile = CreateFile(lpszFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, NULL); if (hFile != INVALID_HANDLE_VALUE) { //add the unicode marker to the beginning of the file //so that APIs know for sure that it is a unicode file. WriteFile(hFile, L"\xfeff\r\n", 3 * sizeof(WCHAR), &dwWritten, NULL); //add some unicode characters to the file. WriteFile(hFile, L" \r\n", 7 * sizeof(WCHAR), &dwWritten, NULL); CloseHandle(hFile); Status = ERROR_SUCCESS; } else { Status = GetLastError(); } } } return Status; } //+-------------------------------------------------------------------------- // // Function: IsValidPrefix // // Synopsis: Given a path, this function determines if it is a valid prefix // // Arguments: [in] pathType : the type of the path // [in] pwszPath : the supplied path // // Returns: TRUE: if the prefix is valid. // FALSE: otherwise // // History: 3/14/2000 RahulTh created // // Notes: A valid prefix is either a non-unc path or a UNC path which // has at least the server and the share component. It must also // be a non-empty path. // //--------------------------------------------------------------------------- BOOL IsValidPrefix (UINT pathType, LPCTSTR pwszPath) { CString szPath; const WCHAR * pwszProcessedPath; if (! pwszPath || L'\0' == *pwszPath) return FALSE; szPath = pwszPath; szPath.TrimLeft(); szPath.TrimRight(); szPath.TrimRight(L'\\'); pwszProcessedPath = (LPCTSTR) szPath; if (PathIsUNC ((LPCTSTR) szPath)) { // Make sure it has both the server and the share component if (lstrlen (pwszProcessedPath) <= 2 || L'\\' != pwszProcessedPath[0] || L'\\' != pwszProcessedPath[1] || NULL == wcschr (&pwszProcessedPath[2], L'\\')) { return FALSE; } } // // If we are here, we just need to make sure that the path does not contain // any environment variables -- if it is not IDS_SPECIFIC_PATH // if (pathType != IDS_SPECIFIC_PATH && NULL != wcschr(pwszProcessedPath, L'%')) { return FALSE; } // If we make it up to here, then the path is a valid prefix. return TRUE; } //+-------------------------------------------------------------------------- // // Function: AlwaysShowMyPicsNode // // Synopsis: In WindowsXP, we now show the MyPics node in the scope pane // only if My Pics does not follow My Docs. However, if required // this MyPics can always be made visible by setting a reg. value // under HKLM\Software\Policies\Microsoft\Windows\System called // FRAlwaysShowMyPicsNode. This is a DWORD value and if set to // non-zero, the MyPics node will always be displayed. // // Arguments: none. // // Returns: TRUE : if the value was found in the registry and was non-zero. // FALSE : otherwise. // // History: 4/10/2001 RahulTh created // // Notes: Note: In case of errors, the default value of FALSE is returned. // //--------------------------------------------------------------------------- BOOL AlwaysShowMyPicsNode (void) { BOOL bAlwaysShowMyPics = FALSE; DWORD dwValue = 0; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(DWORD); HKEY hKey = NULL; if (ERROR_SUCCESS != RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("Software\\Policies\\Microsoft\\Windows\\System"), &hKey) ) { return bAlwaysShowMyPics; } if (ERROR_SUCCESS == RegQueryValueEx (hKey, TEXT("FRAlwaysShowMyPicsNode"), NULL, &dwType, (LPBYTE)(&dwValue), &dwSize) ) { if (REG_DWORD == dwType && dwValue) bAlwaysShowMyPics = TRUE; } RegCloseKey(hKey); return bAlwaysShowMyPics; } //+-------------------------------------------------------------------------- // // Function: CreateThemedPropertyPage // // Synopsis: Helper function to make sure that property pages put up // by the snap-in are themed. // // Arguments: // // Returns: // // History: 4/20/2001 RahulTh created // // Notes: // //--------------------------------------------------------------------------- HPROPSHEETPAGE CreateThemedPropertySheetPage(AFX_OLDPROPSHEETPAGE* psp) { PROPSHEETPAGE_V3 sp_v3 = {0}; CopyMemory (&sp_v3, psp, psp->dwSize); sp_v3.dwSize = sizeof(sp_v3); return (::CreatePropertySheetPage (&sp_v3)); }