722 lines
20 KiB
C++
722 lines
20 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2001 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
Duuninst.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Contains the main uninstallation code
|
||
|
used by the app.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
Unicode only.
|
||
|
|
||
|
History:
|
||
|
|
||
|
03/02/2001 rparsons Created
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
extern SETUP_INFO g_si;
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Removes the specified registry key
|
||
|
Assumes HKEY_LOCAL_MACHINE
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
lpwKey - Path of the key to open
|
||
|
lpwSubKey - Path of the subkey to delete
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
void
|
||
|
UninstallDeleteSubKey(
|
||
|
IN LPCWSTR lpwKey,
|
||
|
IN LPCWSTR lpwSubKey
|
||
|
)
|
||
|
{
|
||
|
CRegistry creg;
|
||
|
|
||
|
//
|
||
|
// Remove the key from the registry
|
||
|
//
|
||
|
creg.DeleteRegistryKey(HKEY_LOCAL_MACHINE,
|
||
|
lpwKey,
|
||
|
lpwSubKey,
|
||
|
TRUE,
|
||
|
TRUE);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Retrieves the section names from the uninstall
|
||
|
INF file. This dictates what operations will be
|
||
|
performed during uninstall
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE on success, FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
UninstallGetSectionsFromINF()
|
||
|
{
|
||
|
BOOL fReturn = FALSE;
|
||
|
DWORD dwType = 0;
|
||
|
char szSectionName[MAX_QUEUE_SIZE] = "";
|
||
|
char *pSectionName;
|
||
|
WCHAR wszDirective[MAX_PATH] = L"";
|
||
|
INFCONTEXT InfContext;
|
||
|
|
||
|
//
|
||
|
// Loop through all the lines in the Sections section(s),
|
||
|
//
|
||
|
fReturn = SetupFindFirstLineA(g_si.hInf, INF_MASTER_SECTIONS, NULL,
|
||
|
&InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szSectionName, MAX_QUEUE_SIZE, NULL);
|
||
|
|
||
|
while (fReturn) {
|
||
|
|
||
|
//
|
||
|
// Determine which section we're working with
|
||
|
//
|
||
|
if (strstr(szSectionName, INF_RESTORE_FILES)) {
|
||
|
dwType = dwRestoreFiles;
|
||
|
|
||
|
} else if (strstr(szSectionName, INF_DELETE_REGISTRY)) {
|
||
|
dwType = dwDeleteRegistry;
|
||
|
|
||
|
} else if (strstr(szSectionName, INF_RESTORE_REGISTRY)) {
|
||
|
dwType = dwRestoreRegistry;
|
||
|
|
||
|
} else if (strstr(szSectionName, INF_UNREGISTRATIONS)) {
|
||
|
dwType = dwUnRegistrations;
|
||
|
|
||
|
} else if (strstr(szSectionName, INF_EXCLUDE)) {
|
||
|
dwType = dwExclusionsUninstall;
|
||
|
|
||
|
} else {
|
||
|
Print(ERROR,
|
||
|
L"[UninstallGetSectionsFromINF] Illegal section name passed %s\n",
|
||
|
szSectionName);
|
||
|
return FALSE; // illegal section name
|
||
|
}
|
||
|
|
||
|
pSectionName = strtok(szSectionName, ",");
|
||
|
|
||
|
do {
|
||
|
|
||
|
pAnsiToUnicode(pSectionName, wszDirective, MAX_PATH);
|
||
|
|
||
|
//
|
||
|
// Loop through each section name and add it to the
|
||
|
// appropriate queue
|
||
|
//
|
||
|
switch (dwType) {
|
||
|
case dwRestoreFiles:
|
||
|
g_si.RestoreFileQueue.Enqueue(wszDirective);
|
||
|
break;
|
||
|
|
||
|
case dwDeleteRegistry:
|
||
|
g_si.DeleteRegistryQueue.Enqueue(wszDirective);
|
||
|
break;
|
||
|
|
||
|
case dwRestoreRegistry:
|
||
|
g_si.RestoreRegistryQueue.Enqueue(wszDirective);
|
||
|
break;
|
||
|
|
||
|
case dwUnRegistrations:
|
||
|
g_si.RegistrationQueue.Enqueue(wszDirective);
|
||
|
break;
|
||
|
|
||
|
case dwExclusionsUninstall:
|
||
|
g_si.ExclusionQueue.Enqueue(wszDirective);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return FALSE; // illegal section name
|
||
|
}
|
||
|
|
||
|
pSectionName = strtok(NULL, ",");
|
||
|
|
||
|
} while (NULL != pSectionName);
|
||
|
|
||
|
fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szSectionName, MAX_QUEUE_SIZE, NULL);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Restores registry keys that were saved
|
||
|
during the install
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE on success, FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
UninstallRestoreRegistryKeys()
|
||
|
{
|
||
|
BOOL fReturn = FALSE, fResult = FALSE;
|
||
|
HKEY hKeyRoot = NULL;
|
||
|
char szEntry[MAX_QUEUE_SIZE] = "";
|
||
|
WCHAR wszEntry[MAX_QUEUE_SIZE] = L"";
|
||
|
char szKeyPath[MAX_PATH*3] = "";
|
||
|
WCHAR wszKeyPath[MAX_PATH*3] = L"";
|
||
|
LPWSTR lpwSubKey = NULL, lpwFilePath = NULL, lpwKeyRoot = NULL;
|
||
|
UINT uCount = 0;
|
||
|
CRegistry creg;
|
||
|
INFCONTEXT InfContext;
|
||
|
|
||
|
//
|
||
|
// The entry in the uninstall INF will have the following format:
|
||
|
// HKxx,SubKeyPath,PathToBackupFile
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Step through each entry in the queue
|
||
|
//
|
||
|
while (g_si.RestoreRegistryQueue.GetSize()) {
|
||
|
|
||
|
g_si.RestoreRegistryQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE);
|
||
|
|
||
|
pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH);
|
||
|
|
||
|
//
|
||
|
// Loop through all the lines in the restore registry section(s),
|
||
|
// and perform the restore
|
||
|
//
|
||
|
fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL,
|
||
|
&InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szKeyPath, MAX_PATH*3, NULL);
|
||
|
|
||
|
|
||
|
while (fReturn) {
|
||
|
|
||
|
pAnsiToUnicode(szKeyPath, wszKeyPath, MAX_PATH*3);
|
||
|
|
||
|
//
|
||
|
// Split the key path into three separate parts
|
||
|
//
|
||
|
lpwKeyRoot = GetNextToken(wszKeyPath, L",");
|
||
|
|
||
|
if (NULL == lpwKeyRoot) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Print(TRACE, L"[UninstallRestoreRegistryKeys] Key root: %s\n", lpwKeyRoot);
|
||
|
|
||
|
if (!_wcsicmp(lpwKeyRoot, L"HKLM")) {
|
||
|
hKeyRoot = HKEY_LOCAL_MACHINE;
|
||
|
|
||
|
} else if (!_wcsicmp(lpwKeyRoot, L"HKCR")) {
|
||
|
hKeyRoot = HKEY_CLASSES_ROOT;
|
||
|
|
||
|
} else if (!_wcsicmp(lpwKeyRoot, L"HKCU")) {
|
||
|
hKeyRoot = HKEY_CURRENT_USER;
|
||
|
|
||
|
} else if (!_wcsicmp(lpwKeyRoot, L"HKU")) {
|
||
|
hKeyRoot = HKEY_USERS;
|
||
|
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the subkey path
|
||
|
//
|
||
|
lpwSubKey = GetNextToken(NULL, L",");
|
||
|
|
||
|
if (NULL == lpwSubKey) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Print(TRACE, L"[UninstallRestoreRegistryKeys] Subkey path: %s\n", lpwSubKey);
|
||
|
|
||
|
//
|
||
|
// Get the file name with full path
|
||
|
//
|
||
|
lpwFilePath = GetNextToken(NULL, L",");
|
||
|
|
||
|
if (NULL == lpwFilePath) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Print(TRACE, L"[UninstallRestoreRegistryKeys] File path: %s\n", lpwFilePath);
|
||
|
|
||
|
//
|
||
|
// Restore the key
|
||
|
//
|
||
|
fResult = creg.RestoreKey(hKeyRoot, lpwSubKey, lpwFilePath, TRUE);
|
||
|
|
||
|
if (!fResult) {
|
||
|
Print(ERROR,
|
||
|
L"[UninstallRestoreRegistryKeys] Failed to restore key\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szKeyPath, MAX_PATH*3, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Removes files from the installation directory
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
UninstallRemoveFiles()
|
||
|
{
|
||
|
CEnumDir ced;
|
||
|
|
||
|
//
|
||
|
// Remove files from the installation directory,
|
||
|
// but leave subdirectories alone
|
||
|
//
|
||
|
ced.EnumerateDirectoryTree(g_si.lpwInstallDirectory,
|
||
|
DeleteOneFile,
|
||
|
FALSE,
|
||
|
(PVOID) TRUE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Restores files that were backed up during the install
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE on success, FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
UninstallRestoreFiles()
|
||
|
{
|
||
|
WCHAR wszBackupDir[MAX_PATH] = L"";
|
||
|
WCHAR wszDestFileName[MAX_PATH] = L"";
|
||
|
WCHAR wszSourceFileName[MAX_PATH] = L"";
|
||
|
char szEntry[MAX_PATH] = "";
|
||
|
WCHAR wszEntry[MAX_PATH] = L"";
|
||
|
char szFileName[MAX_PATH] = "";
|
||
|
WCHAR wszFileName[MAX_PATH] = L"";
|
||
|
BOOL fReturn = FALSE, fResult = FALSE;
|
||
|
LPWSTR lpwDestDir = NULL;
|
||
|
DWORDLONG dwlSourceVersion = 0, dwlDestVersion = 0;
|
||
|
INFCONTEXT InfContext;
|
||
|
|
||
|
//
|
||
|
// Step through each entry in the queue
|
||
|
//
|
||
|
while (g_si.RestoreFileQueue.GetSize()) {
|
||
|
|
||
|
g_si.RestoreFileQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE);
|
||
|
|
||
|
pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH);
|
||
|
|
||
|
//
|
||
|
// Get the destination directory
|
||
|
//
|
||
|
GetNextToken(wszEntry, L".");
|
||
|
GetNextToken(NULL, L".");
|
||
|
lpwDestDir = GetNextToken(NULL, L".");
|
||
|
|
||
|
if (NULL == lpwDestDir) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Loop through all the lines in the restore files section(s),
|
||
|
// and perform the copy
|
||
|
//
|
||
|
fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL,
|
||
|
&InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szFileName, MAX_PATH, NULL);
|
||
|
|
||
|
while (fReturn) {
|
||
|
|
||
|
pAnsiToUnicode(szFileName, wszFileName, MAX_PATH);
|
||
|
|
||
|
//
|
||
|
// Build the path to the destination file
|
||
|
//
|
||
|
wsprintf(wszDestFileName,
|
||
|
L"%s\\%s\\%s",
|
||
|
g_si.lpwWindowsDirectory,
|
||
|
lpwDestDir,
|
||
|
wszFileName);
|
||
|
|
||
|
//
|
||
|
// Build the path to the source file
|
||
|
//
|
||
|
wsprintf(wszSourceFileName,
|
||
|
L"%s\\%s\\%s",
|
||
|
g_si.lpwExtractPath,
|
||
|
g_si.lpwUninstallDirectory,
|
||
|
wszFileName);
|
||
|
|
||
|
//
|
||
|
// Ensure that this file is not under WFP
|
||
|
//
|
||
|
fResult = IsFileProtected(wszDestFileName);
|
||
|
|
||
|
//
|
||
|
// Copy the file - be sensitive to WFP
|
||
|
//
|
||
|
if (fResult) {
|
||
|
|
||
|
Print(TRACE,
|
||
|
L"[UninstallRestoreFiles] Preparing to restore WFP file from %s to %s\n",
|
||
|
wszSourceFileName, wszDestFileName);
|
||
|
|
||
|
fResult = CommonEnableProtectedRenames();
|
||
|
|
||
|
if (!fResult) {
|
||
|
Print(ERROR,
|
||
|
L"[UninstallRestoreFiles] Failed to enable protected renames\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Print(TRACE,
|
||
|
L"[UninstallRestoreFiles] Preparing to install WFP file from %s to %s\n",
|
||
|
wszSourceFileName, wszDestFileName);
|
||
|
|
||
|
fResult = UninstallWFPFile(wszSourceFileName,
|
||
|
wszFileName,
|
||
|
wszDestFileName,
|
||
|
g_si.fUpdateDllCache);
|
||
|
|
||
|
if (!fResult) {
|
||
|
Print(ERROR,
|
||
|
L"[UninstallRestoreFiles] Failed to install WFP file %s\n",
|
||
|
wszFileName);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
Print(TRACE,
|
||
|
L"[UninstallRestoreFiles] Preparing to restore file from %s to %s\n",
|
||
|
wszSourceFileName, wszDestFileName);
|
||
|
|
||
|
fResult = ForceMove(wszSourceFileName, wszDestFileName);
|
||
|
|
||
|
if (!fResult) {
|
||
|
Print(ERROR,
|
||
|
L"[UninstallRestoreFiles] Failed to restore file from %s to %s. GLE = %d\n",
|
||
|
wszSourceFileName, wszDestFileName, GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
fReturn = SetupFindNextLine(&InfContext, &InfContext) &&
|
||
|
SetupGetLineTextA(&InfContext,
|
||
|
NULL, NULL, NULL,
|
||
|
szFileName, MAX_PATH, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Restores a file that is protected
|
||
|
by WFP
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
lpwSourceFileName - Path to the source file
|
||
|
lpwDestFileName - Name of the destination file
|
||
|
lpwDestFileNamePath Name & path to the destination file
|
||
|
fUpdateDllCache - A flag to indicate if the file
|
||
|
should be placed in the DllCache directory
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE on success, FALSE otherwise
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
UninstallWFPFile(
|
||
|
IN LPCWSTR lpwSourceFileName,
|
||
|
IN LPCWSTR lpwDestFileName,
|
||
|
IN LPCWSTR lpwDestFileNamePath,
|
||
|
IN BOOL fUpdateDllCache
|
||
|
)
|
||
|
{
|
||
|
LPWSTR lpwCachePath = NULL;
|
||
|
LPWSTR lpwExpandedCachePath = NULL;
|
||
|
LPWSTR lpwTempFileName = NULL;
|
||
|
DWORD cbSize = 0;
|
||
|
WCHAR wszDllCachePath[MAX_PATH] = L"";
|
||
|
CRegistry creg;
|
||
|
|
||
|
if (g_si.fUpdateDllCache) {
|
||
|
|
||
|
Print(TRACE,
|
||
|
L"[UninstallWFPFile] Restoring %s to DllCache dir\n",
|
||
|
lpwSourceFileName);
|
||
|
|
||
|
//
|
||
|
// Try to get the dllcache directory path from
|
||
|
// the registry
|
||
|
//
|
||
|
lpwCachePath = creg.GetString(HKEY_LOCAL_MACHINE,
|
||
|
REG_WINFP_PATH,
|
||
|
L"SfcDllCacheDir",
|
||
|
TRUE);
|
||
|
|
||
|
if (lpwCachePath) {
|
||
|
|
||
|
if (cbSize = ExpandEnvironmentStrings(lpwCachePath,
|
||
|
lpwExpandedCachePath,
|
||
|
0)) {
|
||
|
|
||
|
lpwExpandedCachePath = (LPWSTR) MALLOC(cbSize*sizeof(WCHAR));
|
||
|
|
||
|
if (lpwExpandedCachePath) {
|
||
|
|
||
|
if (ExpandEnvironmentStrings(lpwCachePath,
|
||
|
lpwExpandedCachePath,
|
||
|
cbSize)) {
|
||
|
|
||
|
//
|
||
|
// Build a full path to \%windir%\system32\dllcache\filename.xxx
|
||
|
//
|
||
|
wsprintf(wszDllCachePath,
|
||
|
L"%s\\%s",
|
||
|
lpwExpandedCachePath,
|
||
|
lpwDestFileName);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we couldn't get it from that key, try another
|
||
|
//
|
||
|
if (NULL == lpwExpandedCachePath) {
|
||
|
|
||
|
lpwCachePath = creg.GetString(HKEY_LOCAL_MACHINE,
|
||
|
REG_WINLOGON_PATH,
|
||
|
L"SfcDllCacheDir",
|
||
|
TRUE);
|
||
|
|
||
|
if (lpwCachePath) {
|
||
|
|
||
|
if (cbSize = ExpandEnvironmentStrings(lpwCachePath,
|
||
|
lpwExpandedCachePath,
|
||
|
0)) {
|
||
|
|
||
|
lpwExpandedCachePath = (LPWSTR) MALLOC(cbSize*sizeof(WCHAR));
|
||
|
|
||
|
if (lpwExpandedCachePath) {
|
||
|
|
||
|
if (ExpandEnvironmentStrings(lpwCachePath,
|
||
|
lpwExpandedCachePath,
|
||
|
cbSize)) {
|
||
|
|
||
|
//
|
||
|
// Build a full path to \%windir%\system32\dllcache\filename.xxx
|
||
|
//
|
||
|
wsprintf(wszDllCachePath,
|
||
|
L"%s\\%s",
|
||
|
lpwExpandedCachePath,
|
||
|
lpwDestFileName);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If neither key worked, build the path manually
|
||
|
//
|
||
|
if (NULL == lpwExpandedCachePath) {
|
||
|
|
||
|
wsprintf(wszDllCachePath,
|
||
|
L"%s\\DllCache\\%s",
|
||
|
g_si.lpwSystem32Directory,
|
||
|
lpwDestFileName);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Replace the file in the DllCache directory
|
||
|
//
|
||
|
if (!CopyFile(lpwSourceFileName, wszDllCachePath, FALSE)) {
|
||
|
|
||
|
Print(ERROR,
|
||
|
L"[UninstallWFPFile] Failed to copy %s to %s\n",
|
||
|
lpwSourceFileName, wszDllCachePath);
|
||
|
goto cleanup;
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This is semi-custom, but because some of the
|
||
|
// WFPs will be in use, we'll delay replace them
|
||
|
// and let the Session Manager rename them
|
||
|
//
|
||
|
lpwTempFileName = CopyTempFile(lpwSourceFileName,
|
||
|
g_si.lpwSystem32Directory);
|
||
|
|
||
|
if (NULL == lpwTempFileName) {
|
||
|
|
||
|
Print(ERROR, L"[UninstallWFPFile] Failed to get temp file\n");
|
||
|
goto cleanup;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!MoveFileEx(lpwTempFileName,
|
||
|
lpwDestFileNamePath,
|
||
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT)) {
|
||
|
|
||
|
Print(ERROR, L"[UninstallWFPFile] Failed to delay replace from %s to %s\n",
|
||
|
lpwTempFileName, lpwDestFileNamePath);
|
||
|
goto cleanup;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
if (lpwTempFileName) {
|
||
|
FREE(lpwTempFileName);
|
||
|
}
|
||
|
|
||
|
if (lpwExpandedCachePath) {
|
||
|
FREE(lpwExpandedCachePath);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Custom worker routine for uninstall
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
void
|
||
|
UninstallCustomWorker()
|
||
|
{
|
||
|
//
|
||
|
// This is where determine if we should remove
|
||
|
// any regsvr32s that we did during install.
|
||
|
// We also remove the $Sources$ directory,
|
||
|
// if necessary.
|
||
|
// Our method of detection is to see if our
|
||
|
// Guid is present under the Active Setup
|
||
|
// key. If not, it's safe to assume that
|
||
|
// no package is currently installed
|
||
|
//
|
||
|
|
||
|
BOOL fReturn = FALSE;
|
||
|
CRegistry creg;
|
||
|
char szActiveSetupKey[MAX_PATH] = "";
|
||
|
WCHAR wszActiveSetupKey[MAX_PATH] = L"";
|
||
|
WCHAR wszSourcesDir[MAX_PATH] = L"";
|
||
|
|
||
|
fReturn = SetupGetLineTextA(NULL,
|
||
|
g_si.hInf,
|
||
|
"Strings",
|
||
|
"ActiveSetupKey",
|
||
|
szActiveSetupKey,
|
||
|
sizeof(szActiveSetupKey),
|
||
|
NULL);
|
||
|
|
||
|
if (!fReturn) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pAnsiToUnicode(szActiveSetupKey, wszActiveSetupKey, MAX_PATH);
|
||
|
|
||
|
fReturn = creg.IsRegistryKeyPresent(HKEY_LOCAL_MACHINE,
|
||
|
wszActiveSetupKey);
|
||
|
|
||
|
if (!fReturn) {
|
||
|
wsprintf(wszSourcesDir, L"%s\\$Sources$", g_si.lpwInstallDirectory);
|
||
|
CommonRemoveDirectoryAndFiles(wszSourcesDir, (PVOID) FALSE, TRUE, FALSE);
|
||
|
CommonRegisterServers(FALSE);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|