/*++ Copyright (c) 2000 Microsoft Corporation Module Name: DeleteSpecifiedFiles.cpp Abstract: This SHIM renames the MFC42loc.dll that is installed by the APP in the %windir%\system32 directory and sets up the temporary file for destruction. Notes: This app. places the MFC42loc.dll into the %windir%\system32 directory even on a English language locale thereby forcing some APPS to use it and thereby some 'Dialogs' and 'Message boxes' are messed up. History: 08/21/2000 prashkud Created --*/ #include "precomp.h" #include "CharVector.h" #include "strsafe.h" #include // for inplace new IMPLEMENT_SHIM_BEGIN(DeleteSpecifiedFiles) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_END class FILENAME_PATH { public: CString FileName; CString FileVersion; BOOL bPresent; FILENAME_PATH() { bPresent = FALSE; } }; class FileNamePathList : public VectorT { }; VectorT *g_StFileNamePath = NULL; /*++ This function checks the 'FileVersion' of the first variable 'szFileName' matches that of the 2nd parameter 'szFileVersion' and if it matches returns TRUE else returns FALSE. --*/ BOOL FileCheckVersion( const CString & csFileName, const CString & csFileVersion ) { DWORD dwDummy; PVOID pVersionInfo; UINT cbTranslate, i; WCHAR SubBlock[100]; LPWSTR lpBuffer; DWORD dwBytes; WORD wLangID; struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } *lpTranslate; DWORD dwVersionInfoSize; // // There is no File Version specified. So, no point in going ahead. Return TRUE. // if (csFileVersion.IsEmpty()) { return TRUE; } dwVersionInfoSize = GetFileVersionInfoSizeW((LPWSTR)csFileName.Get(), &dwDummy); if (dwVersionInfoSize > 0) { pVersionInfo = malloc(dwVersionInfoSize); if (pVersionInfo) { if (0 != GetFileVersionInfoW( (LPWSTR)csFileName.Get(), 0, dwVersionInfoSize, pVersionInfo )) { // Now, pVersionInfo contains the required version block. // Use it with VerQueryValue to get the // the language info that is needed // Get System locale before and note down the language for the system // Read the list of languages and code pages. if (VerQueryValueW( pVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate )) { // // Read the language string each language and code page. // for (i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++) { if (StringCbPrintfW( SubBlock, (sizeof(SubBlock) - 1), L"\\StringFileInfo\\%04x%04x\\FileVersion", lpTranslate[i].wLanguage, lpTranslate[i].wCodePage ) < 0) { DPFN( eDbgLevelError, "Long string - Cannot handle"); break; } else { SubBlock[ARRAYSIZE(SubBlock) - 1] = '\0'; } // // Retrieve FileVersion for language and code page "i" from the pVersionInfo. // if (VerQueryValueW( pVersionInfo, SubBlock, (LPVOID*)&lpBuffer, (UINT*)&dwBytes)) { if (!(csFileVersion.Compare(lpBuffer))) { DPFN( eDbgLevelInfo, "Version string for current file is %S,%S", lpBuffer, csFileVersion.Get()); free(pVersionInfo); return TRUE; } } } // for loop } } free(pVersionInfo); } } return FALSE; } /*++ This function as the name suggests deletes the file or if it is in use moves it to the 'Temp' folder. --*/ VOID DeleteFiles() { for (int i = 0; i < g_StFileNamePath->Size(); ++i) { const FILENAME_PATH & fnp = g_StFileNamePath->Get(i); DPFN( eDbgLevelSpew, "DeleteFiles file(%S) version(%S)", fnp.FileName.Get(), fnp.FileVersion.Get()); if (!fnp.bPresent) { // // CheckFileVersion // if (FileCheckVersion(fnp.FileName, fnp.FileVersion)) { LOGN(eDbgLevelError,"Deleting file %S.", fnp.FileName.Get()); // Delete the file.. if (!DeleteFileW(fnp.FileName)) { CString csTempDir; CString csTempPath; LOGN(eDbgLevelError,"Moving file %S.", fnp.FileName.Get()); // // Could not delete.Retry by renaming it and then deleting it. // New file is %windir%\Temp // csTempDir.GetTempPathW(); csTempPath.GetTempFileNameW(csTempDir, L"XXXX", 0); if (MoveFileExW( fnp.FileName, csTempPath, MOVEFILE_REPLACE_EXISTING )) { SetFileAttributesW( csTempPath, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_TEMPORARY); DeleteFileW(csTempPath); } } } } } } /*++ This function checks for the existence of the file specified on the commandline . This is called during the DLL_PROCESS_ATTACH notification --*/ BOOL CheckFileExistence() { // If any among the list is not present, mark it as 'Not Present' and only those marked as // 'Not present' will be deleted. BOOL bFileDoesNotExist = FALSE; WIN32_FIND_DATAW StWin32FileData; for (int i = 0; i < g_StFileNamePath->Size(); ++i) { FILENAME_PATH & fnp = g_StFileNamePath->Get(i); DPFN( eDbgLevelSpew, "CheckFileExistence file(%S) version(%S)", fnp.FileName.Get(), fnp.FileVersion.Get()); HANDLE hTempFile = FindFirstFileW(fnp.FileName, &StWin32FileData); if (INVALID_HANDLE_VALUE != hTempFile) { FindClose(hTempFile); // // File is present. Check its version if given. // if (FileCheckVersion(fnp.FileName, fnp.FileVersion)) { fnp.bPresent = TRUE; } else { bFileDoesNotExist = TRUE; fnp.bPresent = FALSE; } } else { bFileDoesNotExist = TRUE; fnp.bPresent = FALSE; } } return bFileDoesNotExist; } /*++ The command line can contain FileName:Path:VersionString,FileName1:Path1:VersionString1 etc.... Eg. Ole2.dll:system:604.5768.94567,MFC42.dll:0:,Foo.dll:d:\program Files\DisneyInteractive etc.. 'system' implies the %windir%. '0' implies that the filename itself is a fully qualified path OR one has the option of giving the path seperately OR it can be left blank. --*/ BOOL ParseCommandLine(LPCSTR lpszCommandLine) { CSTRING_TRY { g_StFileNamePath = new VectorT; if (!g_StFileNamePath) { return FALSE; } CStringToken csCommandLine(COMMAND_LINE, ":,;"); CString csTok; CString csFilePart; DWORD dwState = 0; while (csCommandLine.GetToken(csTok)) { FILENAME_PATH fnp; switch(dwState) { case 0: dwState++; fnp.FileName = csTok; break; case 1: // The file part needs to be stored. csFilePart = fnp.FileName; if (csTok.CompareNoCase(L"system") == 0) { fnp.FileName.GetSystemDirectoryW(); fnp.FileName.AppendPath(csFilePart); } else { fnp.FileName = csTok; fnp.FileName.AppendPath(csFilePart); } dwState++; break; case 2: dwState = 0; fnp.FileVersion = csTok; DPFN( eDbgLevelInfo, "ParseCommandLine file(%S) version(%S)", fnp.FileName.Get(), fnp.FileVersion.Get()); // Found all three states, add it to the list if (!g_StFileNamePath->AppendConstruct(fnp)) { // Append failed, stop parsing return FALSE; } break; } } } CSTRING_CATCH { return FALSE; } return TRUE; } /*++ Register hooked functions --*/ BOOL NOTIFY_FUNCTION( DWORD fdwReason ) { static BOOL fDidNotExist = FALSE; switch (fdwReason) { case SHIM_STATIC_DLLS_INITIALIZED: { if (ParseCommandLine(COMMAND_LINE)) { // // Ok...CommandLine is in place...Now Check for those Files... // If any one file exists, then we are not responsible. We bail out.. // fDidNotExist = CheckFileExistence(); } break; } case DLL_PROCESS_DETACH: { // // Check for the specified file at the specified location. If it existed prior to // to this in PROCESS_DLL_ATTACH, it is not installed us...Just bail out ! // If the file did not exist before, it is our problem and we should remove them. // if (fDidNotExist) { DeleteFiles(); } break; } } return TRUE; } HOOK_BEGIN CALL_NOTIFY_FUNCTION HOOK_END IMPLEMENT_SHIM_END