804 lines
24 KiB
C++
804 lines
24 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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 <filelist.hxx>
|
||
|
#include <bothchar.hxx>
|
||
|
|
||
|
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__
|