//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1998 - 1998. // // File: special.hxx // // Contents: Special directories class and headers // // Classes: // // Functions: // // History: 23-Sep-99 PhilipLa Created // //---------------------------------------------------------------------------- #ifndef __SPECIAL_HXX__ #define __SPECIAL_HXX__ #include #include const DWORD SPECIALDIR_MACHINE = 2; const DWORD SPECIALDIR_USER = 1; #define SDM SPECIALDIR_MACHINE #define SDU SPECIALDIR_USER struct sSpecialDir { TCHAR *ptsSpecialName; int iSpecialDir; DWORD dwFlags; TCHAR *ptsKeyName; }; const sSpecialDir ssdSpecial[] = { {TEXT("%csidl_admintools%"), CSIDL_ADMINTOOLS, SDU, TEXT("Administrative Tools")}, {TEXT("%csidl_altstartup%"), CSIDL_ALTSTARTUP, SDU, TEXT("AltStartup")}, {TEXT("%csidl_appdata%"), CSIDL_APPDATA, SDU, TEXT("AppData")}, {TEXT("%csidl_bitbucket%"), CSIDL_BITBUCKET, SDM, TEXT("RecycleBinFolder")}, {TEXT("%csidl_common_admintools%"), CSIDL_COMMON_ADMINTOOLS, SDM, TEXT("Common Administrative Tools")}, {TEXT("%csidl_common_altstartup%"), CSIDL_COMMON_ALTSTARTUP, SDU, TEXT("Common AltStartup")}, {TEXT("%csidl_common_appdata%"), CSIDL_COMMON_APPDATA, SDM, TEXT("Common AppData")}, {TEXT("%csidl_common_desktopdirectory%"), CSIDL_COMMON_DESKTOPDIRECTORY, SDM, TEXT("Common Desktop")}, {TEXT("%csidl_common_documents%"), CSIDL_COMMON_DOCUMENTS, SDM, TEXT("Common Documents")}, {TEXT("%csidl_common_favorites%"), CSIDL_COMMON_FAVORITES, SDM, TEXT("Common Favorites")}, {TEXT("%csidl_common_programs%"), CSIDL_COMMON_PROGRAMS, SDM, TEXT("Common Programs")}, {TEXT("%csidl_common_startmenu%"), CSIDL_COMMON_STARTMENU, SDM, TEXT("Common Start Menu")}, {TEXT("%csidl_common_startup%"), CSIDL_COMMON_STARTUP, SDM, TEXT("Common Startup")}, {TEXT("%csidl_common_templates%"), CSIDL_COMMON_TEMPLATES, SDM, TEXT("Common Templates")}, {TEXT("%csidl_controls%"), CSIDL_CONTROLS, SDM, TEXT("ControlPanelFolder")}, {TEXT("%csidl_cookies%"), CSIDL_COOKIES, SDU, TEXT("Cookies")}, {TEXT("%csidl_desktop%"), CSIDL_DESKTOP, SDU, TEXT("Desktop")}, //The key shfolder.dll uses is "DesktopFolder" {TEXT("%csidl_desktopdirectory%"), CSIDL_DESKTOPDIRECTORY, SDU, TEXT("Desktop")}, {TEXT("%csidl_drives%"), CSIDL_DRIVES, SDM, TEXT("DriveFolder")}, {TEXT("%csidl_favorites%"), CSIDL_FAVORITES, SDU, TEXT("Favorites")}, {TEXT("%csidl_fonts%"), CSIDL_FONTS, SDU, TEXT("Fonts")}, {TEXT("%csidl_history%"), CSIDL_HISTORY, SDU, TEXT("History")}, {TEXT("%csidl_internet%"), CSIDL_INTERNET, SDM, TEXT("InternetFolder")}, {TEXT("%csidl_internet_cache%"), CSIDL_INTERNET_CACHE, SDU, TEXT("Cache")}, {TEXT("%csidl_local_appdata%"), CSIDL_LOCAL_APPDATA, SDU, TEXT("Local AppData")}, {TEXT("%csidl_mypictures%"), CSIDL_MYPICTURES, SDU, TEXT("My Pictures")}, {TEXT("%csidl_nethood%"), CSIDL_NETHOOD, SDU, TEXT("Nethood")}, {TEXT("%csidl_network%"), CSIDL_NETWORK, SDM, TEXT("NetworkFolder")}, {TEXT("%csidl_personal%"), CSIDL_PERSONAL, SDU, TEXT("Personal")}, {TEXT("%csidl_printers%"), CSIDL_PRINTERS, SDM, TEXT("PrintersFolder")}, {TEXT("%csidl_printhood%"), CSIDL_PRINTHOOD, SDU, TEXT("PrintHood")}, {TEXT("%csidl_profile%"), CSIDL_PROFILE, SDU, TEXT("Profile")}, {TEXT("%csidl_program_files%"), CSIDL_PROGRAM_FILES, SDM, TEXT("ProgramFiles")}, {TEXT("%csidl_program_files_common%"), CSIDL_PROGRAM_FILES_COMMON, SDM, TEXT("CommonProgramFiles")}, {TEXT("%csidl_program_filesx86%"), CSIDL_PROGRAM_FILESX86, SDM, TEXT("ProgramFilesX86")}, {TEXT("%csidl_programs%"), CSIDL_PROGRAMS, SDU, TEXT("Programs")}, {TEXT("%csidl_recent%"), CSIDL_RECENT, SDU, TEXT("Recent")}, {TEXT("%csidl_sendto%"), CSIDL_SENDTO, SDU, TEXT("SendTo")}, {TEXT("%csidl_startmenu%"), CSIDL_STARTMENU, SDU, TEXT("Start Menu")}, {TEXT("%csidl_startup%"), CSIDL_STARTUP, SDU, TEXT("Startup")}, {TEXT("%csidl_system%"), CSIDL_SYSTEM, SDM, TEXT("System")}, {TEXT("%csidl_systemx86%"), CSIDL_SYSTEMX86, SDM, TEXT("SystemX86")}, {TEXT("%csidl_templates%"), CSIDL_TEMPLATES, SDU, TEXT("Templates")}, {TEXT("%csidl_windows%"), CSIDL_WINDOWS, SDM, TEXT("Windows")}}; const DWORD cSpecialDirs = sizeof(ssdSpecial) / sizeof(sSpecialDir); class CSpecialDirectory { public: CSpecialDirectory(); ~CSpecialDirectory(); inline const TCHAR * GetDirectoryName(ULONG i) const; inline const TCHAR * GetDirectoryPath(ULONG i) const; inline DWORD GetDirectoryCount(void) const; inline DWORD Init(void); inline DWORD InitForUser(HKEY hKey); inline DWORD InitFromInf(TCHAR *ptsName, TCHAR *ptsPath); inline DWORD AddSpecialDir(TCHAR *ptsName, TCHAR *ptsPath, LONG *i); inline DWORD ExpandMacro(TCHAR *pbuf, TCHAR *pbufTmp, TCHAR **pptsFinal, BOOL fUpdate); private: CFileList *_pfl; BOOL _fUser; }; CSpecialDirectory::CSpecialDirectory() { _pfl = NULL; _fUser = FALSE; } CSpecialDirectory::~CSpecialDirectory() { FreeFileList(_pfl); } inline const TCHAR * CSpecialDirectory::GetDirectoryName(ULONG i) const { return _pfl->GetFullName(i); } inline const TCHAR * CSpecialDirectory::GetDirectoryPath(ULONG i) const { return _pfl->GetDestination(i); } inline DWORD CSpecialDirectory::GetDirectoryCount(void) const { return _pfl->GetNameCount(); } inline DWORD CSpecialDirectory::AddSpecialDir(TCHAR *ptsName, TCHAR *ptsPath, LONG *piName) { DWORD dwErr = ERROR_SUCCESS; if (_pfl == NULL) { _pfl = new CFileList; if (_pfl == NULL) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); return ERROR_OUTOFMEMORY; } } DWORD cDirs = _pfl->GetNameCount(); for (ULONG i = 0; i < cDirs; i++) { if (_tcsicmp(_pfl->GetFullName(i), ptsName) == 0) { Win32PrintfResource(LogFile, IDS_FAILED); return ERROR_INVALID_PARAMETER; } } //Not found, so go ahead and try to add it. if (ptsPath == NULL) { //Look it up HRESULT hr; int iFolder = 0; //It's a new one, see if it matches anything in the special //dir list for (i = 0; i < cSpecialDirs; i++) { if (_tcsicmp(ptsName, ssdSpecial[i].ptsSpecialName) == 0) { //Bingo iFolder = ssdSpecial[i].iSpecialDir; break; } } if (iFolder != 0) { TCHAR tsDirName[MAX_PATH + 1]; hr = SHGetFolderPath(NULL, iFolder, NULL, SHGFP_TYPE_CURRENT, tsDirName); //Only handle success cases, since some special paths aren't //available on all machines. if (SUCCEEDED(hr) && (tsDirName[0] != 0)) { dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE); if (dwErr) return dwErr; dwErr = _pfl->SetDestination(tsDirName, (ULONG)*piName); return dwErr; } } //It isn't a special directory, see if it's an environment //variable. TCHAR tsVarName[MAX_PATH + 1]; TCHAR tsVarValue[MAX_PATH + 1]; if (_tcslen(ptsName) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsName too long %s\r\n", ptsName); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(tsVarName, ptsName + 1); tsVarName[_tcslen(tsVarName) - 1] = 0; dwErr = ERROR_INVALID_PARAMETER; if (_fUser) { //Check for userprofile, which is a special case if (_tcsicmp(tsVarName, TEXT("USERPROFILE")) == 0) { //We have the user's profile directory stored //in UserPath if (_tcslen(UserPath) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: UserPath too long %s\r\n", UserPath); return ERROR_FILENAME_EXCED_RANGE; } _tcscpy(tsVarValue, UserPath); dwErr = ERROR_SUCCESS; } else if (CurrentUser != NULL) { //Check in the registry HKEY hKeyEnv = NULL; dwErr = RegOpenKey(CurrentUser, TEXT("Environment"), &hKeyEnv); if (dwErr) { //Do nothing, try again below } else { DWORD dwValueType; DWORD dwLen = MAX_PATH + 1; dwErr = RegQueryValueEx(hKeyEnv, tsVarName, NULL, &dwValueType, (BYTE *)tsVarValue, &dwLen); if ((dwErr == ERROR_SUCCESS) && (dwValueType != REG_SZ)) { //Flag the error, try again below dwErr = ERROR_INVALID_PARAMETER; } RegCloseKey(hKeyEnv); } } } if (dwErr != ERROR_SUCCESS) { dwErr = GetEnvironmentVariable(tsVarName, tsVarValue, MAX_PATH + 1); //GetEnvironmentVariable returns a length, not an error code. //A value of 0 is an error here. if (dwErr == 0) { // if no mapping found, it might not actually be a macro. // just continue without setting this as a special dir if (Verbose) { Win32Printf(LogFile, "Warning: Couldn't find macro mapping for [%s]. " "Assuming not a macro\r\n", ptsName); } *piName = -1; return ERROR_SUCCESS; } } dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE); if (dwErr) return dwErr; dwErr = _pfl->SetDestination(tsVarValue, (ULONG)*piName); } else { dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE); if (dwErr) return dwErr; dwErr = _pfl->SetDestination(ptsPath, (ULONG)*piName); } return dwErr; } inline DWORD CSpecialDirectory::InitFromInf(TCHAR *ptsName, TCHAR *ptsPath) { LONG i; return AddSpecialDir(ptsName, ptsPath, &i); } inline DWORD CSpecialDirectory::Init(void) { _pfl = new CFileList; if (_pfl == NULL) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); return ERROR_OUTOFMEMORY; } for (ULONG i = 0; i < cSpecialDirs; i++) { HRESULT hr; ULONG iName; TCHAR tsDirName[MAX_PATH + 1]; DWORD dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName, &iName, FALSE); if (dwErr) return dwErr; hr = SHGetFolderPath(NULL, ssdSpecial[i].iSpecialDir, NULL, SHGFP_TYPE_CURRENT, tsDirName); //Only handle success cases, since some special paths aren't //available on all machines. if (SUCCEEDED(hr)) { if (tsDirName[0] == 0) { //WTF happened? Why didn't we get an error? //Continue anyway, we can ignore this case } else { dwErr = _pfl->SetDestination(tsDirName, iName); if (dwErr) return dwErr; } } } return ERROR_SUCCESS; } inline DWORD CSpecialDirectory::InitForUser(HKEY hKey) { DWORD dwErr = ERROR_SUCCESS; TCHAR *ptsValData = NULL; _fUser = TRUE; _pfl = new CFileList; if (_pfl == NULL) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); return ERROR_OUTOFMEMORY; } HKEY hKeyUser = NULL; HKEY hKeyEnv = NULL; //Pick up all the environment strings from the user's registry block dwErr = RegOpenKey(CurrentUser, TEXT("Environment"), &hKeyEnv); if (dwErr) { Win32PrintfResource(LogFile, ResultToRC(dwErr)); goto Cleanup; } DWORD dwIndex; dwIndex = 0; do { TCHAR tsValName[MAX_PATH + 1]; DWORD dwValNameLength = (MAX_PATH + 1); DWORD dwValDataLength = 0; DWORD dwType; // Read the value name dwErr = RegEnumValue(hKeyEnv, dwIndex, tsValName, &dwValNameLength, NULL, //reserved NULL, // dont care about type NULL, // dont care about data NULL); // dont care about datalength if (dwErr == ERROR_NO_MORE_ITEMS) { dwErr = ERROR_SUCCESS; break; } if (dwErr) { Win32PrintfResource(LogFile, ResultToRC(dwErr)); goto Cleanup; } // Read the size required for the data dwErr = RegQueryValueEx(hKeyEnv, tsValName, NULL, // reserved NULL, // dont care about type NULL, // dont care about data &dwValDataLength); if (dwErr) { Win32PrintfResource(LogFile, ResultToRC(dwErr)); goto Cleanup; } ptsValData = (TCHAR *)malloc(dwValDataLength + 1); if (NULL == ptsValData) { Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY); return ERROR_OUTOFMEMORY; } // Read the actual data dwErr = RegQueryValueEx(hKeyEnv, tsValName, NULL, // reserved &dwType, (BYTE *)ptsValData, &dwValDataLength); if (dwErr) { Win32PrintfResource(LogFile, ResultToRC(dwErr)); goto Cleanup; } if ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)) { DWORD i; //Add %s TCHAR tsFinal[MAX_PATH+1]; if (dwValNameLength > MAX_PATH-1) { dwErr = ERROR_FILENAME_EXCED_RANGE; goto Cleanup; } tsFinal[0] = TEXT('%'); _tcscpy(tsFinal+1, tsValName); tsFinal[dwValNameLength + 1] = TEXT('%'); tsFinal[dwValNameLength + 2] = 0; dwErr = _pfl->SetName(tsFinal, &i, FALSE); if (dwErr) goto Cleanup; dwErr = _pfl->SetDestination(ptsValData, i); if (dwErr) goto Cleanup; } dwIndex++; free (ptsValData); ptsValData = NULL; } while (1); //Also pick up USERPROFILE so we don't need to get it later. dwErr = _pfl->SetName(TEXT("%USERPROFILE%"), &dwIndex, FALSE); if (dwErr) goto Cleanup; dwErr = _pfl->SetDestination(UserPath, dwIndex); if (dwErr) goto Cleanup; dwErr = RegOpenKey(hKey, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"), &hKeyUser); if (dwErr) { Win32PrintfResource(LogFile, ResultToRC(dwErr)); goto Cleanup; } ULONG i; for (i = 0; i < cSpecialDirs; i++) { HRESULT hr; ULONG iName; TCHAR tsDirName[MAX_PATH + 1]; int iSpecialDir = ssdSpecial[i].iSpecialDir; dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName, &iName, FALSE); if (dwErr) { goto Cleanup; } if (ssdSpecial[i].dwFlags & SDU) { //User specific directory, get from appropriate place in //hKey TCHAR *ptsFinal; TCHAR tsMacro[MAX_PATH + 1]; // ptsFinal points here after ExpandMacro call if (iSpecialDir == CSIDL_DESKTOP) { //Another special case. This is the user profile directory //plus a localized desktop directory name, but we don't //have that localization info, so we're in trouble. // //Pretend we're doing CSIDL_DESKTOPDIRECTORY instead iSpecialDir = CSIDL_DESKTOPDIRECTORY; } if (iSpecialDir == CSIDL_PROFILE) { //Special case, not stored in registry if (_tcslen(UserPath) > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: UserPath Too Long %s\r\n", UserPath); dwErr = ERROR_FILENAME_EXCED_RANGE; goto Cleanup; } _tcscpy(tsDirName, UserPath); dwErr = ERROR_SUCCESS; ptsFinal = tsDirName; } else { DWORD dwValueType; DWORD dwLen = MAX_PATH + 1; dwErr = RegQueryValueEx(hKeyUser, ssdSpecial[i].ptsKeyName, NULL, &dwValueType, (BYTE *)tsDirName, &dwLen); if (dwErr == ERROR_SUCCESS) { if (dwValueType == REG_EXPAND_SZ) { dwErr = ExpandMacro(tsDirName, tsMacro, &ptsFinal, TRUE); if (dwErr) goto Cleanup; } else { ptsFinal = tsDirName; } } } if (dwErr == ERROR_SUCCESS) { dwErr = _pfl->SetDestination(ptsFinal, iName); if (dwErr) goto Cleanup; continue; } } dwErr = ERROR_SUCCESS; //Machine global directory, get through normal mechanism hr = SHGetFolderPath(NULL, iSpecialDir, NULL, SHGFP_TYPE_CURRENT, tsDirName); //Only handle success cases, since some special paths aren't //available on all machines. if (SUCCEEDED(hr)) { if (tsDirName[0] == 0) { //WTF happened? Why didn't we get an error? //Continue anyway, we can ignore this case } else { dwErr = _pfl->SetDestination(tsDirName, iName); if (dwErr) { goto Cleanup; } } } } Cleanup: if (hKeyUser != NULL) RegCloseKey(hKeyUser); if (hKeyEnv != NULL) RegCloseKey(hKeyEnv); if (ptsValData != NULL) free(ptsValData); return dwErr; } // Notes (chrisab 3/1/00): this function expands nested macros, but // does not expand multiple macros in the same source string. Could // also be changed to use cleaner temp buffers. DWORD CSpecialDirectory::ExpandMacro(TCHAR *pbuf, TCHAR *pbufTmp, TCHAR **pptsFinal, BOOL fUpdate) { TCHAR *ptsFinalName = pbuf; TCHAR *ptsMacro; // Point the final at the original, in case we // return early without making any substitutions *pptsFinal = pbuf; while ((ptsMacro = _tcschr(ptsFinalName, TEXT('%'))) != NULL) { //% found, find the closing % TCHAR tsMacro[MAX_PATH + 1]; TCHAR *ptsMacroEnd; ptsMacroEnd = _tcschr(ptsMacro + 1, TEXT('%')); if (ptsMacroEnd == NULL) { // '%' is a valid character in files, too. // Assume if no match, it's not an error return ERROR_SUCCESS; } if (ptsMacroEnd - ptsMacro + 1 > MAX_PATH) { if (DebugOutput) Win32Printf(LogFile, "Error: ptsMacro too long %*s\r\n", ptsMacroEnd - ptsMacro + 1, ptsMacro); return ERROR_FILENAME_EXCED_RANGE; } _tcsncpy(tsMacro, ptsMacro, ptsMacroEnd - ptsMacro + 1); tsMacro[ptsMacroEnd - ptsMacro + 1] = 0; if (DebugOutput) { Win32Printf(LogFile, "Found macro %s\r\n", tsMacro); } const TCHAR *ptsDirMapping = NULL; for (ULONG i = 0; i < GetDirectoryCount(); i++) { if (_tcsicmp(tsMacro, GetDirectoryName(i)) == 0) { //Winnah ptsDirMapping = GetDirectoryPath(i); // Set fUpdate to FALSE so we don't try to add this again // for when the Name is a macro we can't expand fUpdate = FALSE; break; } } if ((ptsDirMapping == NULL) && fUpdate) { //It's a new one, try to add it. LONG iName; DWORD dwErr = AddSpecialDir(tsMacro, NULL, &iName); if (dwErr) return dwErr; if (iName >= 0) { ptsDirMapping = GetDirectoryPath(iName); } } if (ptsDirMapping == NULL) { // If no mapping found, it might not actually be a macro. // Just leave it as it is return ERROR_SUCCESS; } if (DebugOutput) Win32Printf(LogFile, "Mapping for %s == %s\r\n", tsMacro, ptsDirMapping); //Do substitution TCHAR *ptsWorkBuffer = (ptsFinalName == pbuf) ? pbufTmp : pbuf; if ((ptsMacro - ptsFinalName) + _tcslen(ptsDirMapping) + _tcslen(ptsMacroEnd+1) > MAX_PATH) { if (Verbose) { Win32Printf(LogFile, "Error too long filename: %*s\\%s\\%s\r\n", ptsMacro-ptsFinalName, // int: length of ptsFinalName to print ptsFinalName, ptsDirMapping, ptsMacroEnd+1); } return ERROR_FILENAME_EXCED_RANGE; } _tcsncpy(ptsWorkBuffer, ptsFinalName, ptsMacro - ptsFinalName); _tcscpy(ptsWorkBuffer + (ptsMacro - ptsFinalName), ptsDirMapping); _tcscpy(ptsWorkBuffer + (ptsMacro - ptsFinalName) + _tcslen(ptsDirMapping), ptsMacroEnd + 1); if (DebugOutput) Win32Printf(LogFile, "New string: %s\r\n", ptsWorkBuffer); ptsFinalName = ptsWorkBuffer; } *pptsFinal = ptsFinalName; return ERROR_SUCCESS; } #endif // #ifndef __SPECIAL_HXX__