|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
CorrectPathChangesBase.cpp
Abstract: Several paths were changed between Win9x and WinNT. This routine defines the CorrectPathChangesBase routines that is called with a Win9x path and returns the corresponding WinNT path.
History:
03-Mar-00 robkenny Converted CorrectPathChanges.cpp to this class. 21-Mar-00 robkenny StringISub("abc", "abcd") now works 06/20/2000 robkenny EnvironmentValues::Initialize now checks the return status of the system calls 12/12/2000 mnikkel Some apps look for ddhelp.exe to exist to confirm directx is installed, set this to look for ddraw.dll since ddhelp.exe no longer exists in directx. 02/13/2001 robkenny/a-larrsh Added AllProfile and UserProfile to EnvironmentValues 03/22/2001 robkenny Do not redirect files to directories that the user does not have permission. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
--*/ #include "ClassCFP.h"
#include "Userenv.h"
#include <stdio.h>
namespace ShimLib {
/*++
A simple string class. Mostly to ease the pain and drudgery of string manipulation in StringISub
--*/
class SimpleString { protected: DWORD dwLen; // String Length
DWORD dwSize; // String Data size
WCHAR * lpData; // String Data
bool Resize(DWORD dwSize); // Make buffer large enough for dwSize characters (not bytes)
public: SimpleString(); ~SimpleString();
//
// Note, this ctor should only be used for const.
//
SimpleString(LPCWSTR pwsz, DWORD dwLen) { lpData = (LPWSTR)pwsz; this->dwLen = dwLen; dwSize = 0; }
DWORD Length() const; // Number of characters in string
bool Append(const SimpleString & string); BOOL Set(const WCHAR * string); BOOL SetN(const WCHAR * string, DWORD dwStringLen); const WCHAR * Get() const;
BOOL StringISub(const SimpleString & ssMatch, const SimpleString & ssReplace);
WCHAR * ReleaseString(); // Caller now owns the string
operator const WCHAR * () const; SimpleString & operator = (const SimpleString & string);
static WCHAR * ReplaceAllStringsAllocW(const WCHAR * lpOrig, const StringPairW * ReplaceList, DWORD dwReplaceListSize); };
typedef const SimpleString ConstSimpleString;
/*++
Constructor, init all values
--*/ SimpleString::SimpleString() { dwLen = 0; dwSize = 0; lpData = NULL; }
/*++
Destructor, free memory
--*/ SimpleString::~SimpleString() { if (lpData && dwSize) free(lpData); }
/*++
Return the number of char in the string
--*/ DWORD SimpleString::Length() const { return dwLen; }
/*++
Make the string large enough to hold size characters. Note: size should include EOS at the end.
--*/ bool SimpleString::Resize(DWORD size) { if (size > dwSize) { lpData = (WCHAR *) realloc(lpData, size * sizeof(WCHAR)); dwSize = size; } return(size == 0) || (lpData != NULL); }
/*++
Append string onto the end of this.
--*/ bool SimpleString::Append(const SimpleString & string) { bool stringSafe = Resize(Length() + string.Length() + 1); if (stringSafe) { wcscat(lpData, string); dwLen = Length() + string.Length(); } return stringSafe; }
/*++
Initialize this with a char string
--*/ BOOL SimpleString::Set(const WCHAR * string) { return SetN(string, wcslen(string)); }
/*++
Return a pointer to the string, class still owns pointer.
--*/ const WCHAR * SimpleString::Get() const { return lpData; }
/*++
Initialize this with the first stringLen chars in string.
--*/ BOOL SimpleString::SetN(const WCHAR * string, DWORD stringLen) { BOOL bAllocGood = Resize(stringLen + 1); if (bAllocGood) { dwLen = stringLen; wcsncpy(lpData, string, stringLen); lpData[stringLen] = 0; }
return bAllocGood; }
/*++
Release ownership of the string. Caller is now responsible for calling free()
--*/ WCHAR * SimpleString::ReleaseString() { WCHAR * lpOwner = lpData;
dwLen = 0; dwSize = 0; lpData = NULL;
return lpOwner; }
/*++
String substitution, replace all ssMatch with ssReplace
--*/ BOOL SimpleString::StringISub(const SimpleString & ssMatch, const SimpleString & ssReplace) { // Look to see if the match string exists
WCHAR * lpMatchInString = wcsistr( lpData, ssMatch ); if (lpMatchInString != NULL) { // The remainder of the uncorrected string
SimpleString strRemainder; if (!strRemainder.Set(lpMatchInString + ssMatch.Length())) return FALSE;
BOOL stringRemainderReplaced = strRemainder.StringISub(ssMatch, ssReplace);
if (!SetN(lpData, (DWORD)(lpMatchInString - lpData))) return FALSE;
if (!Append(ssReplace)) return FALSE; if (!Append(strRemainder)) return FALSE;
return stringRemainderReplaced; } else { return FALSE; } } /*++
Return a const pointer to the string.
--*/ SimpleString::operator const WCHAR * () const { return lpData; }
/*++
Assignment operator, safely make a duplicate.
--*/ SimpleString & SimpleString::operator = (const SimpleString & string) { SetN(string.lpData, string.dwLen);
return *this; } /*++
Func: ReplaceAllStringsAllocW
Params: lpOrig Original string ReplaceList Array of string old/new pairs dwReplaceListSize Number of entries in ReplaceList
Return: WCHAR * Newly allocated string containing (possibly) modified string
Desc: For each entry in ReplaceList replace the string in lpOrig, placing the result into lpCorrected. --*/ WCHAR * SimpleString::ReplaceAllStringsAllocW( const WCHAR * lpOrig, const StringPairW * ReplaceList, DWORD dwReplaceListSize) { //DPF("SimpleString", eDbgLevelInfo, "\nReplaceAllStringsAllocW(%S)\n", lpOrig);
SimpleString ssReplaced; if (ssReplaced.Set(lpOrig)) { for (DWORD i = 0; i < dwReplaceListSize; ++i) { // Attempt a string substitution
ssReplaced.StringISub( ConstSimpleString(ReplaceList[i].lpOld, ReplaceList[i].dwLenOld), ConstSimpleString(ReplaceList[i].lpNew, ReplaceList[i].dwLenNew));
//DPF("SimpleString", eDbgLevelInfo, "Old(%S)\n", ReplaceList[i].lpOld);
//DPF("SimpleString", eDbgLevelInfo, "New(%S)\n", ReplaceList[i].lpNew);
//DPF("SimpleString", eDbgLevelInfo, "Val(%S)\n", ssReplaced.Get());
} }
return ssReplaced.ReleaseString(); }
//-------------------------------------------------------------------------------------------------------------
EnvironmentValues::EnvironmentValues() { bInitialized = FALSE; }
EnvironmentValues::~EnvironmentValues() { // Clear the list
Erase(); }
// Given an CLSIDL, create an environment variable and its two variants
// CSIDL_WINDOWS would add c:\windows, \windows and windows
void EnvironmentValues::Add_Variants(const WCHAR * lpEnvName, const WCHAR * lpEnvValue, eAddNameEnum addName, eAddNoDLEnum noDL) { CSTRING_TRY { CString csEnvName(lpEnvName); CString csEnvValue(lpEnvValue);
csEnvName.Format(L"%%%s%%", lpEnvName); AddEnvironmentValue(csEnvName, csEnvValue);
// Remove the drive letter and the colon.
if (noDL == eAddNoDL) { CString csNoDL(csEnvValue); csNoDL.Delete(0, 2);
csEnvName.Format(L"%%%s_NODL%%", lpEnvName); AddEnvironmentValue(csEnvName, csNoDL); }
// Use the last path component as the name.
if (addName == eAddName) { CString csName; csEnvValue.GetLastPathComponent(csName);
csEnvName.Format(L"%%%s_NAME%%", lpEnvName); AddEnvironmentValue(csEnvName, csName); } } CSTRING_CATCH { // Do Nothing
} }
// Given an CLSIDL, create an environment variable and its two variants
// CSIDL_WINDOWS would add c:\windows, \windows and windows
void EnvironmentValues::Add_CSIDL(const WCHAR * lpEnvName, int nFolder, eAddNameEnum addName, eAddNoDLEnum noDL) { CSTRING_TRY { CString csPath; SHGetSpecialFolderPathW(csPath, nFolder);
if (csPath.GetLength() > 0) { Add_Variants(lpEnvName, csPath, addName, noDL); } } CSTRING_CATCH { // Do Nothing
} }
// Add all _CSIDL values as environment variables.
void EnvironmentValues::AddAll_CSIDL() { Add_CSIDL(L"CSIDL_APPDATA", CSIDL_APPDATA, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_ADMINTOOLS", CSIDL_COMMON_ADMINTOOLS, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_APPDATA", CSIDL_COMMON_APPDATA, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_DESKTOPDIRECTORY", CSIDL_COMMON_DESKTOPDIRECTORY, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_DOCUMENTS", CSIDL_COMMON_DOCUMENTS, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_FAVORITES", CSIDL_COMMON_FAVORITES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_MUSIC", CSIDL_COMMON_MUSIC, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_PICTURES", CSIDL_COMMON_PICTURES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_PROGRAMS", CSIDL_COMMON_PROGRAMS, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_STARTMENU", CSIDL_COMMON_STARTMENU, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_STARTUP", CSIDL_COMMON_STARTUP, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COMMON_TEMPLATES", CSIDL_COMMON_TEMPLATES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_COOKIES", CSIDL_COOKIES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_DESKTOPDIRECTORY", CSIDL_DESKTOPDIRECTORY, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_FAVORITES", CSIDL_FAVORITES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_FONTS", CSIDL_FONTS, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_HISTORY", CSIDL_HISTORY, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_INTERNET_CACHE", CSIDL_INTERNET_CACHE, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_LOCAL_APPDATA", CSIDL_LOCAL_APPDATA, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_MYMUSIC", CSIDL_MYMUSIC, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_MYPICTURES", CSIDL_MYPICTURES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_NETHOOD", CSIDL_NETHOOD, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PERSONAL", CSIDL_PERSONAL, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PRINTHOOD", CSIDL_PRINTHOOD, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PROFILE", CSIDL_PROFILE, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PROGRAM_FILES", CSIDL_PROGRAM_FILES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PROGRAM_FILES_COMMON", CSIDL_PROGRAM_FILES_COMMON, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_PROGRAMS", CSIDL_PROGRAMS, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_RECENT", CSIDL_RECENT, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_SENDTO", CSIDL_SENDTO, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_STARTMENU", CSIDL_STARTMENU, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_STARTUP", CSIDL_STARTUP, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_SYSTEM", CSIDL_SYSTEM, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_TEMPLATES", CSIDL_TEMPLATES, eAddName, eAddNoDL); Add_CSIDL(L"CSIDL_WINDOWS", CSIDL_WINDOWS, eAddName, eAddNoDL); }
void EnvironmentValues::Initialize() { if (bInitialized == FALSE) { bInitialized = TRUE;
WCHAR lpDir[MAX_PATH]; DWORD dwSize; HRESULT result; DWORD dwChars; BOOL bResult;
dwChars = GetWindowsDirectoryW(lpDir, MAX_PATH); if (dwChars != 0) { AddEnvironmentValue( L"%WinDir%", lpDir ); AddEnvironmentValue( L"%SystemRoot%", lpDir );
lpDir[2] = 0; AddEnvironmentValue( L"%SystemDrive%", lpDir ); }
dwChars = GetSystemDirectoryW( lpDir, MAX_PATH); if (dwChars != 0) { AddEnvironmentValue( L"%SystemDir%", lpDir ); }
dwSize = MAX_PATH; bResult = GetUserNameW(lpDir, &dwSize); if (bResult) { AddEnvironmentValue( L"%Username%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%ProgramFiles%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_STARTMENU, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%UserStartMenu%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_COMMON_STARTMENU, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%AllStartMenu%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%UserDesktop%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%AllDesktop%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_FAVORITES, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%UserFavorites%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_COMMON_FAVORITES, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%AllFavorites%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%UserAppData%", lpDir ); }
result = SHGetFolderPathW( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_DEFAULT, lpDir ); if (SUCCEEDED(result)) { AddEnvironmentValue( L"%AllAppData%", lpDir ); }
// C:\Documents and Settings\All Users
dwSize = MAX_PATH; bResult = GetAllUsersProfileDirectoryW(lpDir, &dwSize); if (bResult) { Add_Variants( L"AllUsersProfile", lpDir, eAddName, eAddNoDL); // same as real Env var
}
// C:\Documents and Settings\owner
HANDLE hProcessHandle = GetCurrentProcess(); HANDLE hUserToken; if (OpenProcessToken(hProcessHandle, TOKEN_QUERY, &hUserToken)) { dwSize = MAX_PATH; bResult = GetUserProfileDirectoryW(hUserToken, lpDir, &dwSize); if (bResult) { Add_Variants( L"UserProfile", lpDir, eAddName, eAddNoDL); } }
// Add the new CLSIDL variables (some have duplicate values to above)
AddAll_CSIDL(); } }
WCHAR * EnvironmentValues::ExpandEnvironmentValueW(const WCHAR * lpOld) { Initialize();
// Replace all the "environment" values into their real values
WCHAR * lpMassagedOld = SimpleString::ReplaceAllStringsAllocW(lpOld, vectorList, nVectorListMax );
return lpMassagedOld; }
char * EnvironmentValues::ExpandEnvironmentValueA(const char * lpOld) { Initialize();
char * lpMassagedOld = NULL;
WCHAR * lpOldWide = ToUnicode(lpOld); if (lpOldWide) { WCHAR * lpMassagedOldWide = SimpleString::ReplaceAllStringsAllocW(lpOldWide, vectorList, nVectorListMax ); if (lpMassagedOldWide) { lpMassagedOld = ToAnsi(lpMassagedOldWide); free(lpMassagedOldWide); }
free(lpOldWide); }
return lpMassagedOld; }
void EnvironmentValues::AddEnvironmentValue(const WCHAR * lpOld, const WCHAR * lpNew) { Initialize();
StringPairW appendThis(lpOld, lpNew);
if (Append(appendThis)) { DPF("EnvironmentValues", eDbgLevelInfo, "AddEnvironmentValue: (%S) to (%S)\n", appendThis.lpOld, appendThis.lpNew ); } }
//-------------------------------------------------------------------------------------------------------------
CorrectPathChangesBase::CorrectPathChangesBase() { lpEnvironmentValues = new EnvironmentValues;
dwKnownPathFixesCount = 0; lpKnownPathFixes = (StringPairW *)malloc(sizeof(StringPairW)); // prime the pump for realloc later
bInitialized = FALSE; bEnabled = TRUE;
InitializeCriticalSection(&csCritical); }
CorrectPathChangesBase::~CorrectPathChangesBase() { if (lpEnvironmentValues) delete lpEnvironmentValues;
if (lpKnownPathFixes) free(lpKnownPathFixes);
DeleteCriticalSection(&csCritical); }
void CorrectPathChangesBase::EnterCS() { EnterCriticalSection(&csCritical); }
void CorrectPathChangesBase::LeaveCS() { LeaveCriticalSection(&csCritical); }
/*++
Func: AddEnvironmentValue
Params: dwIndex lpOld Name of "environment" variable lpNew Value of "environment" variable
--*/ void CorrectPathChangesBase::AddEnvironmentValue(const WCHAR * lpOld, const WCHAR * lpNew ) { if (lpEnvironmentValues) { lpEnvironmentValues->AddEnvironmentValue(lpOld, lpNew); } }
/*++
Func: InsertPathChangeW
Params: lpOld Old Win9x path lpNew New Win2000 path
Desc: Insert the Old/New string pair into lpKnownPathFixes making sure the list is large enough. --*/ void CorrectPathChangesBase::InsertPathChangeW( const WCHAR * lpOld, const WCHAR * lpNew ) { // Ignore identical strings
if (lstrcmpiW(lpOld, lpNew) == 0) return;
// Ignore duplicates
int i; for (i = 0; i < dwKnownPathFixesCount; ++i) { if (_wcsicmp(lpKnownPathFixes[i].lpOld, lpOld) == 0) { DPF("CorrectPathChangesBase", eDbgLevelSpew, "Duplicate PathChange (%S) to (%S)\n", lpOld, lpNew ); return; } }
// Grow the list
size_t oldListSize = dwKnownPathFixesCount * sizeof(StringPairW); lpKnownPathFixes = (StringPairW *)realloc(lpKnownPathFixes, oldListSize + sizeof(StringPairW)); if (lpKnownPathFixes == NULL) { // Alloc failed, don't add anymore
return; }
DWORD dwLenOld = wcslen(lpOld); wcsncpy( lpKnownPathFixes[dwKnownPathFixesCount].lpOld, lpOld, dwLenOld + 1); lpKnownPathFixes[dwKnownPathFixesCount].dwLenOld = dwLenOld; DWORD dwLenNew = wcslen(lpNew); wcsncpy( lpKnownPathFixes[dwKnownPathFixesCount].lpNew, lpNew, dwLenNew + 1); lpKnownPathFixes[dwKnownPathFixesCount].dwLenNew = dwLenNew;
DPF("CorrectPathChangesBase", eDbgLevelInfo, "PathChange (%S) to (%S)\n", lpKnownPathFixes[dwKnownPathFixesCount].lpOld, lpKnownPathFixes[dwKnownPathFixesCount].lpNew );
dwKnownPathFixesCount += 1; }
/*++
Func: AddPathChangeW
Params: lpOld Old Win9x path lpNew New Win2000 path
Desc: Add lpOld/lpNew combo to the list, two times: first: lpOld/short(lpNew) second: short(lpOld)/short(lpNew)
--*/
void CorrectPathChangesBase::AddPathChangeW( const WCHAR * lpOld, const WCHAR * lpNew ) { InitializeCorrectPathChanges();
// Replace all the "environment" values into their real values
WCHAR * lpExpandedOld = ExpandEnvironmentValueW(lpOld); WCHAR * lpExpandedNew = ExpandEnvironmentValueW(lpNew);
const WCHAR * lpNewShort = lpExpandedNew;
// Convert lpNew to its short name
WCHAR lpNewShortBuffer[MAX_PATH]; DWORD status = GetShortPathNameW(lpExpandedNew, lpNewShortBuffer, MAX_PATH); if (status > 0 && status < MAX_PATH) { lpNewShort = lpNewShortBuffer; }
// first: lpOld/short(lpNew)
InsertPathChangeW(lpExpandedOld, lpNewShort);
// Convert lpOld to its short name
WCHAR lpOldShort[MAX_PATH]; status = GetShortPathNameW(lpExpandedOld, lpOldShort, MAX_PATH); if (status > 0 && status < MAX_PATH) // successfully got the short path
{ if (_wcsicmp(lpOld, lpOldShort) != 0) { // second: short(lpOld)/short(lpNew)
InsertPathChangeW( lpOldShort, lpNewShort ); } }
free(lpExpandedOld); free(lpExpandedNew); }
/*++
Func: ExpandEnvironmentValueA
Params: lpOld string with environment vars
Desc: Return a pointer to a malloc() string with all internal env values expanded.
--*/
char * CorrectPathChangesBase::ExpandEnvironmentValueA(const char * lpOld) { WCHAR * lpOldWide = ToUnicode(lpOld);
// Replace all the "environment" values into their real values
WCHAR * lpExpandedOldWide = ExpandEnvironmentValueW(lpOldWide);
char * lpExpandedOld = ToAnsi(lpExpandedOldWide);
free(lpOldWide); free(lpExpandedOldWide);
return lpExpandedOld; }
/*++
Func: ExpandEnvironmentValueW
Params: lpOld string with environment vars
Desc: Return a pointer to a malloc() string with all internal env values expanded.
--*/
WCHAR * CorrectPathChangesBase::ExpandEnvironmentValueW(const WCHAR * lpOld) { WCHAR * lpMassagedOld = NULL;
InitializeCorrectPathChanges();
if (lpEnvironmentValues) { lpMassagedOld = lpEnvironmentValues->ExpandEnvironmentValueW(lpOld); }
return lpMassagedOld; }
/*++
Func: InitializeEnvironmentValuesW
Params: None, applies changes to lpEnvironmentValues
Desc: This function sets the "environment" values to their explicit values --*/ void CorrectPathChangesBase::InitializeEnvironmentValuesW( ) { if (lpEnvironmentValues) { lpEnvironmentValues->Initialize(); } }
/*++
Func: InitializePathFixes
Params: None, applies changes to lpEnvironmentValues
Desc: This function sets the "environment" values to their explicit values --*/ void CorrectPathChangesBase::InitializePathFixes( ) { }
/*++
Func: InitializeCorrectPathChanges
Params: None.
Desc: Initialize the CorrectPathChangesBase values, both A and W versions. This *must* be called prior to calling either CorrectPathChangesA or CorrectPathChangesW --*/ void CorrectPathChangesBase::InitializeCorrectPathChanges( ) { // This seems a little odd, but we wanted to minimize the amount of time spent
// attempting to enter the critical section. The simple code is:
// EnterCS();
// if (!bInitialized)
// {
// BOOL isEnabled = bEnabled; // remember previous enabled state
//
// This would enter the critical section every time we make sure
// the class is initialized.
if (!bInitialized) { EnterCS();
if (!bInitialized) { BOOL isEnabled = bEnabled; // remember previous enabled state
// We must not be enabled while we are initializing, otherwise
// we can (and do!) hook routines that we are trying to use while
// grabbing values from the system.
bEnabled = FALSE; bInitialized = TRUE;
InitializeEnvironmentValuesW(); InitializePathFixes();
bEnabled = isEnabled; }
LeaveCS(); } }
/*++
Helper routine to call CorrectPathA, allocates necessary buffer space and returns a pointer to the corrected path. Caller is responsible for releasing the memory by calling free().
--*/
char * CorrectPathChangesBase::CorrectPathAllocA(const char * str) { if (str == NULL) return NULL;
// Convert lpOrig to WCHAR, correct the WCHAR path, then convert back to char
WCHAR * strWide = ToUnicode(str);
// Correct
WCHAR * strCorrectedWide = CorrectPathAllocW(strWide);
char * strCorrected = ToAnsi(strCorrectedWide);
free(strWide); free(strCorrectedWide);
return strCorrected; }
/*++
Helper routine to call CorrectPathW, allocates necessary buffer space and returns a pointer to the corrected path. Caller is responsible for releasing the memory by calling free().
--*/
WCHAR * CorrectPathChangesBase::CorrectPathAllocW(const WCHAR * str) { if (str == NULL) return NULL;
// Make sure the paths have been initialized.
InitializeCorrectPathChanges();
if (bEnabled) { WCHAR * strCorrected = SimpleString::ReplaceAllStringsAllocW(str, lpKnownPathFixes, dwKnownPathFixesCount);
return strCorrected; } else { return StringDuplicateW(str); } }
void CorrectPathChangesBase::AddFromToPairW(const WCHAR * lpFromToPair ) { // Make sure the paths have been initialized.
InitializeCorrectPathChanges();
WCHAR * FromPath = NULL; WCHAR * ToPath = NULL; const WCHAR * PathBegin = NULL; char argSeperator = 0; // Stop parsing the string when we reach this char
SkipBlanksW(lpFromToPair);
// Malformed input, stop processing
if (*lpFromToPair == 0) goto AllDone;
// If the beginning of the string is a quote, look for the matching close quote
if (*lpFromToPair == '"') { argSeperator = L'"'; lpFromToPair += 1; }
// The beginning of the From path
PathBegin = lpFromToPair;
// Search for the first from/to seperator, this is end of the From path
while (*lpFromToPair != L';') { // Malformed input, stop processing
if (*lpFromToPair == 0) goto AllDone;
lpFromToPair += 1; }
// Malformed input, stop processing
if (lpFromToPair == PathBegin) goto AllDone;
// Copy into our From string
FromPath = StringNDuplicateW(PathBegin, (int)(lpFromToPair - PathBegin));
lpFromToPair += 1; // Skip the from/to seperator
// The beginning of the To path
PathBegin = lpFromToPair;
// Search for argSeperator, this is end of the To path
while (*lpFromToPair != argSeperator) { // Found the end of the string, To path is definately complete
if (*lpFromToPair == 0) break;
lpFromToPair += 1; }
// Malformed input, stop processing
if (lpFromToPair == PathBegin) goto AllDone;
// Copy into our To string
ToPath = StringNDuplicateW(PathBegin, (int)(lpFromToPair - PathBegin));
lpFromToPair += 1; // Skip the argSeperator
// Success!
AddPathChangeW(FromPath, ToPath);
AllDone: free(FromPath); free(ToPath); }
/*++
Take a single string containing (multiple) path change pairs, split them up and call AddPathChangeW. The from/to pair is seperated by a : (colon) If a path contains spaces, the entire pair must be surrounded by quotes
Example: "%windir%\Goofy Location:%SystemDir%\CorrectLocation" %windir%\Goofy2:%SystemDir%\CorrectLocation2
will call AddPathChangeW("%windir%\Goofy Location", "%SystemDir%\CorrectLocation"); AddPathChangeW("%windir%\Goofy2", "%SystemDir%\CorrectLocation2");
--*/ void CorrectPathChangesBase::AddCommandLineW(const WCHAR * lpCommandLine ) { if (!lpCommandLine || *lpCommandLine == 0) return;
DPF("CorrectPathChangesBase", eDbgLevelInfo, "AddCommandLine(%S)\n", lpCommandLine);
int argc; LPWSTR * argv = _CommandLineToArgvW(lpCommandLine, &argc); if (!argv) return;
for (int i = 0; i < argc; ++i) { AddFromToPairW(argv[i]); }
free(argv); }
/*++
Simply widen the string and call AddCommandLineW
--*/ void CorrectPathChangesBase::AddCommandLineA(const char * lpCommandLine ) { if (!lpCommandLine || *lpCommandLine == 0) return;
WCHAR * wszCommandLine = ToUnicode(lpCommandLine);
AddCommandLineW(wszCommandLine);
free(wszCommandLine); }
// Get the full path to wordpad.exe from the registry
BOOL GetWordpadPath(CString & csWordpad) { csWordpad.Truncate(0);
DWORD dwRegType; LONG lStatus = RegQueryValueExW(csWordpad, HKEY_CLASSES_ROOT, L"Applications\\wordpad.exe\\shell\\open\\command", NULL, // default key
&dwRegType); if (ERROR_SUCCESS == lStatus && (dwRegType == REG_SZ || dwRegType == REG_EXPAND_SZ)) { // String is of the form "wordpad path" "%1"
// We want to grab all the stuff between the first pair of quotes
if (csWordpad[0] == L'"') { int nNextQuote = csWordpad.Find(L'"', 1); if (nNextQuote > 0) { csWordpad.Truncate(nNextQuote); csWordpad.Delete(0, 1);
if (dwRegType == REG_EXPAND_SZ) { csWordpad.ExpandEnvironmentStringsW(); }
return TRUE; } } }
return FALSE; }
void CorrectPathChangesUser::InitializePathFixes() { // The order of this list is important. Early entries may create paths that are modified by later entries.
// Hardcoded bad path
AddPathChangeW( L"c:\\windows", L"%WinDir%" ); // robkenny 4/2/2001 Do not redirect Program Files, because it is common to
// create this directory on many hard drives, especially when c:\ is nearly full
// AddPathChangeW( L"c:\\program files", L"%ProgramFiles%" );
// Moved system applications
AddPathChangeW( L"%WinDir%\\rundll32.exe", L"%SystemDir%\\rundll32.exe" ); AddPathChangeW( L"%WinDir%\\rundll.exe", L"%SystemDir%\\rundll32.exe" ); AddPathChangeW( L"%WinDir%\\write.exe", L"%SystemDir%\\write.exe" ); AddPathChangeW( L"%WinDir%\\dxdiag.exe", L"%SystemDir%\\dxdiag.exe" );
CSTRING_TRY { CString csWordpad; if (GetWordpadPath(csWordpad)) { AddPathChangeW( L"%WinDir%\\wordpad.exe", csWordpad); AddPathChangeW( L"%ProgramFiles%\\Accessories\\wordpad.exe", csWordpad); } } CSTRING_CATCH { // Do nothing
}
// Win9x single user locations (also default)
AddPathChangeW( L"%WinDir%\\Start Menu", L"%UserStartMenu%" ); AddPathChangeW( L"%WinDir%\\Desktop", L"%UserDesktop%" ); AddPathChangeW( L"%WinDir%\\Favorites", L"%UserFavorites%" ); // These locations are properly internationalized. Duplicates of above for English
AddPathChangeW( L"%WinDir%\\%CSIDL_STARTMENU_NAME%", L"%UserStartMenu%" ); AddPathChangeW( L"%WinDir%\\%CSIDL_DESKTOPDIRECTORY_NAME%", L"%UserDesktop%" ); AddPathChangeW( L"%WinDir%\\%CSIDL_FAVORITES_NAME%", L"%UserFavorites%" );
// Win9x & WinNT multi user locations
AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\Start Menu", L"%UserStartMenu%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\Desktop", L"%UserDesktop%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\Favorites", L"%UserFavorites%" ); // These locations are properly internationalized. Duplicates of above for English
AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\%CSIDL_STARTMENU_NAME%", L"%UserStartMenu%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\%CSIDL_DESKTOPDIRECTORY_NAME%", L"%UserDesktop%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%Username%\\%CSIDL_FAVORITES_NAME%", L"%UserFavorites%" );
// WinNT all user location
AddPathChangeW( L"%WinDir%\\Profiles\\All Users\\Start Menu", L"%AllStartMenu%" ); AddPathChangeW( L"%WinDir%\\Profiles\\All Users\\Desktop", L"%AllDesktop%" ); AddPathChangeW( L"%WinDir%\\Profiles\\All Users\\Favorites", L"%UserFavorites%" ); // Should be %AllFavorites%, but IE 5.0 doesn't look there.
// These locations are properly internationalized. Duplicates of above for English
AddPathChangeW( L"%WinDir%\\Profiles\\%AllUsersProfile_NAME%\\%CSIDL_STARTMENU_NAME%", L"%AllStartMenu%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%AllUsersProfile_NAME%\\%CSIDL_DESKTOPDIRECTORY_NAME%", L"%AllDesktop%" ); AddPathChangeW( L"%WinDir%\\Profiles\\%AllUsersProfile_NAME%\\%CSIDL_FAVORITES_NAME%", L"%UserFavorites%" ); // Should be %AllFavorites%, but IE 5.0 doesn't look there.
// Win9x deleted DirectX files
AddPathChangeW( L"ddhelp.exe", L"ddraw.dll" ); AddPathChangeW( L"ddraw16.dll", L"ddraw.dll" ); AddPathChangeW( L"dsound.vxd", L"ddraw.dll" ); }
// Does the current process have permission to write into this directory?
BOOL CanWriteHere(DWORD clsid) { BOOL bCanWriteHere = FALSE;
WCHAR wszDir[MAX_PATH]; HRESULT result = SHGetFolderPathW( NULL, clsid, NULL, SHGFP_TYPE_DEFAULT, wszDir ); if (SUCCEEDED(result)) { WCHAR wszTempFile[MAX_PATH];
// We do not use GetTempFileName() to test if we have permission
// to the directory even though it does all that we need. Unfortunately
// the temp file will appear in the start menu since it is not hidden.
// Emulate the behaviour of GetTempFileName but use our file attributes.
// Loop a bunch of times attempting to create a temp file,
// If we can create this file return immediately,
// If we have insuffient permission return immediately
// certain other errors will return immediately
// otherwise we'll attempt to open the next temp file name
// 100 is totally arbitrary: just need to attempt this a bunch of times
static const int MaxTempFileAttempts = 100;
int i; for (i = 0; i < MaxTempFileAttempts; ++i) { swprintf(wszTempFile, L"%s\\CFP%08x.tmp", wszDir, i);
DPF("CanWriteHere", eDbgLevelSpew, "File(%S)\n", wszTempFile);
HANDLE hTempFile = CreateFileW( wszTempFile, GENERIC_WRITE | DELETE, 0, // no sharing
NULL, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL );
if (hTempFile != INVALID_HANDLE_VALUE) { DPF("CanWriteHere", eDbgLevelSpew, "success\n");
CloseHandle(hTempFile); return TRUE; } else { // Borrowed this code from GetTempFileName:
DWORD LastError = GetLastError(); DPF("CanWriteHere", eDbgLevelSpew, "Error(0x%08x)\n", LastError);
switch (LastError) { case ERROR_INVALID_PARAMETER : case ERROR_WRITE_PROTECT : case ERROR_FILE_NOT_FOUND : case ERROR_BAD_PATHNAME : case ERROR_INVALID_NAME : case ERROR_PATH_NOT_FOUND : case ERROR_NETWORK_ACCESS_DENIED : case ERROR_DISK_CORRUPT : case ERROR_FILE_CORRUPT : case ERROR_DISK_FULL : // An error from which we cannot recover...
return FALSE;
case ERROR_ACCESS_DENIED : // It's possible for us to hit this if there's a
// directory with the name we're trying; in that
// case, we can usefully continue.
// CreateFile() uses BaseSetLastNTError() to set
// LastStatusValue to the actual NT error in the
// TEB; we just need to check it, and only abort
// if it's not a directory.
// This was bug #397477.
if (NtCurrentTeb()->LastStatusValue != STATUS_FILE_IS_A_DIRECTORY) { // Insuffient permission
return FALSE; } } } }
}
return bCanWriteHere; }
void CorrectPathChangesAllUser::InitializePathFixes() { CorrectPathChangesUser::InitializePathFixes();
// The choice to put these values into All Users instead of <UserName>
// was not taken lightly. The problem is: some apps create ...\All Users\Start Menu\folder
// then attempt to place files into c:\windows\Start Menu\folder or username\Start Menu\folder.
// Yes the apps are WRONG, but we want them to work. By directing all of these paths
// to All Users we *know* where the files will be placed and can make sure they all are the same place.
// Another note, IE 5.0 does *not* look in All Users\Favorites for links,
// so we force all favorites to end up in the user favorites. Sheesh.
// We add these changes twice, the first to convert any long path names to the All User dir,
// the second to convert any short path names to All User.
if (CanWriteHere(CSIDL_COMMON_STARTMENU)) { AddPathChangeW( L"%UserStartMenu%", L"%AllStartMenu%" ); } else { DPF("CorrectPathChangesAllUser", eDbgLevelInfo, "*NOT* forcing %UserStartMenu% to %AllStartMenu% -- insufficient permission"); }
/*
// 05/11/2001 robkenny:
// We are nolonger modifying the Desktop directory
if (CanWriteHere(CSIDL_COMMON_DESKTOPDIRECTORY)) { AddPathChangeW( L"%UserDesktop%", L"%AllDesktop%" ); } else { DPF("CorrectPathChangesAllUser", eDbgLevelInfo, "*NOT* forcing %UserDesktop% to %AllDesktop% -- insufficient permission"); } */
/*
// IE 5.0/5.5 doesn't use All Users
if (CanWriteHere(CSIDL_COMMON_FAVORITES)) { AddPathChangeW( L"%UserFavorites%", L"%AllFavorites%" ); // IE 5.0 doesn't use All Users
} else { DPF("CorrectPathChangesAllUser", eDbgLevelInfo, "*NOT* forcing %UserFavorites% to %AllFavorites% -- insufficient permission"); } */ }
}; // end of namespace ShimLib
|