661 lines
17 KiB
C++
661 lines
17 KiB
C++
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Duinst.cpp
|
|
|
|
Abstract:
|
|
|
|
Contains the entry point for the Dynamic
|
|
Update package and the install and
|
|
uninstall main functions.
|
|
|
|
Notes:
|
|
|
|
Unicode only.
|
|
|
|
History:
|
|
|
|
03/02/2001 rparsons Created
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "systemrestore.h"
|
|
|
|
SETUP_INFO g_si;
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Application entry point
|
|
|
|
Arguments:
|
|
|
|
hInstance - App instance handle
|
|
hPrevInstance - Always NULL
|
|
lpCmdLine - Pointer to the command line
|
|
nCmdShow - Window show flag
|
|
|
|
Return Value:
|
|
|
|
-1 on failure, 0 on success
|
|
|
|
--*/
|
|
int APIENTRY
|
|
WinMain(
|
|
IN HINSTANCE hInstance,
|
|
IN HINSTANCE hPrevInstance,
|
|
IN LPSTR lpCmdLine,
|
|
IN int nCmdShow
|
|
)
|
|
{
|
|
int nReturn = -1;
|
|
BOOL fReturn = FALSE;
|
|
|
|
Print(TRACE, L"[WinMain] Application is started\n");
|
|
|
|
//
|
|
// Ensure that two separate instances of the app
|
|
// are not running
|
|
//
|
|
if (IsAnotherInstanceRunning(L"WUINST")) {
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Parse the command line and save app-related
|
|
// data away
|
|
//
|
|
if (!ParseCommandLine()) {
|
|
Print(ERROR, L"[WinMain] Call to ParseCommandLine failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_PARSE_CMD_LINE, TRUE);
|
|
goto eh;
|
|
}
|
|
|
|
g_si.hInstance = hInstance;
|
|
|
|
//
|
|
// Determine the action to take
|
|
//
|
|
if (g_si.fInstall) {
|
|
|
|
//
|
|
// Start system restore point
|
|
//
|
|
if (!SystemRestorePointStart(TRUE)) {
|
|
Print(ERROR, L"[WinMain] Call to SystemRestorePointStart failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_INIT_FAILED, TRUE);
|
|
goto eh;
|
|
}
|
|
|
|
if (!WUInitialize()) {
|
|
|
|
Print(ERROR, L"[WinMain] Call to WUInitialize failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_INIT_FAILED, TRUE);
|
|
goto eh;
|
|
}
|
|
|
|
fReturn = DoInstallation();
|
|
|
|
} else if (!g_si.fInstall) {
|
|
|
|
//
|
|
// Start system restore point
|
|
//
|
|
if (!SystemRestorePointStart(FALSE)) {
|
|
Print(ERROR, L"[WinMain] Call to SystemRestorePointStart failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_SYSTEM_RESTORE_FAIL, TRUE);
|
|
goto eh;
|
|
}
|
|
|
|
if (!WUInitialize()) {
|
|
|
|
Print(ERROR, L"[WinMain] Call to WUInitialize failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_INIT_FAILED, TRUE);
|
|
goto eh;
|
|
}
|
|
|
|
fReturn = DoUninstallation();
|
|
|
|
} else {
|
|
goto eh;
|
|
}
|
|
|
|
nReturn = 0;
|
|
|
|
eh:
|
|
//
|
|
// Perform cleanup
|
|
//
|
|
WUCleanup();
|
|
|
|
if (nReturn == 0) {
|
|
if (!SystemRestorePointEnd()) {
|
|
Print(ERROR, L"[WinMain] Call to SystemRestorePointEnd failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_SYSTEM_RESTORE_FAIL, TRUE);
|
|
}
|
|
} else {
|
|
if (!SystemRestorePointCancel()) {
|
|
Print(ERROR, L"[WinMain] Call to SystemRestorePointCancel failed\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_SYSTEM_RESTORE_FAIL, TRUE);
|
|
}
|
|
}
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs the grunt work of the installation
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise
|
|
|
|
--*/
|
|
BOOL
|
|
DoInstallation()
|
|
{
|
|
int nReturn = 0;
|
|
WCHAR wszError[1024] = L"";
|
|
WCHAR wszTemp[MAX_PATH] = L"";
|
|
WCHAR wszBegin[MAX_PATH] = L"";
|
|
WCHAR wszDestFileName[MAX_PATH] = L"";
|
|
WORD wNumStrings = 0;
|
|
LPWSTR lpwMessageArray[2];
|
|
|
|
Print(TRACE, L"[DoInstallation] Installation is starting\n");
|
|
|
|
//
|
|
// Display the prompt for installation if we're
|
|
// not in quiet mode
|
|
//
|
|
if (!g_si.fQuiet) {
|
|
|
|
LoadString(g_si.hInstance, IDS_INSTALL_PROMPT, wszTemp, MAX_PATH);
|
|
|
|
wsprintf(wszBegin, wszTemp, g_si.lpwPrettyAppName);
|
|
|
|
if (MessageBox(NULL,
|
|
wszBegin,
|
|
g_si.lpwMessageBoxTitle,
|
|
MB_YESNO | MB_ICONQUESTION)!=IDYES)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log an event that the install is starting
|
|
//
|
|
LogEventDisplayError(EVENTLOG_INFORMATION_TYPE,
|
|
ID_INSTALL_START,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
//
|
|
// If we're not forcing an installation, check the
|
|
// version number
|
|
//
|
|
if (!g_si.fForceInstall) {
|
|
|
|
nReturn = InstallCheckVersion();
|
|
}
|
|
|
|
if (0 == nReturn) {
|
|
|
|
//
|
|
// This indicates that a newer package is installed
|
|
// on the user's PC. Warn them if we're not in quiet
|
|
// mode
|
|
//
|
|
Print(TRACE,
|
|
L"[DoInstallation] Newer package is installed\n");
|
|
|
|
if (!g_si.fQuiet) {
|
|
|
|
LoadString(g_si.hInstance, IDS_NEWER_VERSION, wszTemp, MAX_PATH);
|
|
|
|
wsprintf(wszError,
|
|
wszTemp,
|
|
g_si.lpwPrettyAppName,
|
|
g_si.lpwPrettyAppName);
|
|
|
|
if (MessageBox(NULL,
|
|
wszError,
|
|
g_si.lpwMessageBoxTitle,
|
|
MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2)!=IDYES)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we're in quiet mode, log an event to the event log
|
|
//
|
|
LogEventDisplayError(EVENTLOG_INFORMATION_TYPE,
|
|
ID_NO_ACTION_TAKEN,
|
|
FALSE,
|
|
FALSE);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Attempt to verify if our target directory exists
|
|
// If not, try to create it
|
|
//
|
|
if (GetFileAttributes(g_si.lpwInstallDirectory)== -1) {
|
|
|
|
if (!CreateDirectory(g_si.lpwInstallDirectory, NULL)) {
|
|
|
|
wNumStrings = 0;
|
|
lpwMessageArray[wNumStrings++] = (LPWSTR) g_si.lpwPrettyAppName;
|
|
lpwMessageArray[wNumStrings++] = (LPWSTR) g_si.lpwInstallDirectory;
|
|
|
|
LogWUEvent(EVENTLOG_ERROR_TYPE,
|
|
ID_NO_APPPATCH_DIR,
|
|
2,
|
|
(LPCWSTR*) lpwMessageArray);
|
|
|
|
|
|
if (!g_si.fQuiet) {
|
|
|
|
DisplayErrMsg(GetDesktopWindow(),
|
|
ID_NO_APPPATCH_DIR,
|
|
(LPWSTR) lpwMessageArray);
|
|
}
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to create installation directory\n");
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we need to adjust permissions on our target directory, do it
|
|
//
|
|
if (g_si.fNeedToAdjustACL) {
|
|
|
|
if (!AdjustDirectoryPerms(g_si.lpwInstallDirectory)) {
|
|
|
|
wNumStrings = 0;
|
|
lpwMessageArray[wNumStrings++] = (LPWSTR) g_si.lpwPrettyAppName;
|
|
lpwMessageArray[wNumStrings++] = (LPWSTR) g_si.lpwInstallDirectory;
|
|
|
|
LogWUEvent(EVENTLOG_ERROR_TYPE,
|
|
ID_ACL_APPPATCH_FAILED,
|
|
2,
|
|
(LPCWSTR*) lpwMessageArray);
|
|
|
|
|
|
if (!g_si.fQuiet) {
|
|
|
|
DisplayErrMsg(GetDesktopWindow(),
|
|
ID_ACL_APPPATCH_FAILED,
|
|
(LPWSTR) lpwMessageArray);
|
|
}
|
|
|
|
Print(ERROR,
|
|
L"[DoInstallation] Failed to apply ACL to installation directory\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get section names from the INF
|
|
//
|
|
if (!InstallGetSectionsFromINF()) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to get section names from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_INF_SCAN_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Install catalog files
|
|
//
|
|
if (!InstallCatalogFiles(g_si.hInf, g_si.lpwExtractPath)) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to install catalog file\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_CATALOG_INSTALL_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// If we're allowing an uninstall, backup files listed in the INF
|
|
//
|
|
if (!g_si.fNoUninstall) {
|
|
|
|
if (!InstallBackupFiles()) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to backup files from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_FILE_BACKUP_FAILED, TRUE, FALSE);
|
|
g_si.fCanUninstall = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're allowing an uninstall and the backup of files worked,
|
|
// backup registry keys listed in the INF
|
|
//
|
|
if (!g_si.fNoUninstall) {
|
|
|
|
if (g_si.fCanUninstall) {
|
|
|
|
if (!InstallBackupRegistryKeys()) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to backup registry keys from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_REG_BACKUP_FAILED, TRUE, FALSE);
|
|
g_si.fCanUninstall = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove any registry keys specified in the INF
|
|
//
|
|
CommonDeleteRegistryKeys();
|
|
|
|
//
|
|
// Remove files from the installation directory
|
|
//
|
|
CommonRemoveDirectoryAndFiles(g_si.lpwInstallDirectory,
|
|
(PVOID) TRUE,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
//
|
|
// Copy files specified in the INF
|
|
//
|
|
if (!InstallCopyFiles()) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to copy files from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_FILE_COPY_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Merge any registry data specified in the INF
|
|
//
|
|
if (!InstallRegistryData()) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to install registry data from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_REG_MERGE_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Perform any server registrations specified in the INF
|
|
//
|
|
if (!CommonRegisterServers(TRUE)) {
|
|
|
|
Print(ERROR, L"[DoInstallation] Failed to register servers from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_REGSVR32_FAILED, TRUE, FALSE);
|
|
}
|
|
|
|
//
|
|
// Run any processes specified in the INF
|
|
//
|
|
InstallRunINFProcesses();
|
|
|
|
//
|
|
// If the backup operations worked, write out the uninstall key
|
|
// In addition, put our uninstall INF in the installation directory
|
|
//
|
|
if (g_si.fCanUninstall) {
|
|
InstallWriteUninstallKey();
|
|
|
|
wsprintf(wszDestFileName,
|
|
L"%s\\%s",
|
|
g_si.lpwInstallDirectory,
|
|
UNINST_INF_FILE_NAMEW);
|
|
|
|
ForceCopy(g_si.lpwUninstallINFPath, wszDestFileName);
|
|
}
|
|
|
|
//
|
|
// Perform a reboot
|
|
//
|
|
if (!g_si.fQuiet && !g_si.fNoReboot) {
|
|
|
|
LoadString(g_si.hInstance, IDS_REBOOT_NEEDED, wszTemp, MAX_PATH);
|
|
|
|
nReturn = MessageBox(NULL,
|
|
wszTemp,
|
|
g_si.lpwMessageBoxTitle,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nReturn == IDYES) {
|
|
|
|
if (!ShutdownSystem(FALSE, TRUE)) {
|
|
|
|
// The shutdown failed - prompt the user for a manual one
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_INS_REBOOT_FAILED, TRUE, FALSE);
|
|
|
|
} else {
|
|
|
|
// The shutdown was successful - write a message to the event log
|
|
LogEventDisplayError(EVENTLOG_INFORMATION_TYPE, ID_INSTALL_SUCCESSFUL, FALSE, FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
// Interactive mode - the user said no to the reboot - write it to the event log
|
|
// Don't display a message.
|
|
LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_INS_QREBOOT_NEEDED, FALSE, FALSE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Quiet mode - A reboot is needed - write it to event log
|
|
LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_INS_QREBOOT_NEEDED, FALSE, FALSE);
|
|
}
|
|
|
|
Print(TRACE, L"[DoInstallation] Installation is complete\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs the grunt work of the uninstall
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise
|
|
|
|
--*/
|
|
BOOL
|
|
DoUninstallation()
|
|
{
|
|
WCHAR wszTemp[MAX_PATH] = L"";
|
|
WCHAR wszBegin[MAX_PATH] = L"";
|
|
WCHAR wszBackupDir[MAX_PATH] = L"";
|
|
char szGuid[80] = "";
|
|
WCHAR wszGuid[80] = L"";
|
|
BOOL fReturn = FALSE;
|
|
int nReturn = 0;
|
|
|
|
Print(TRACE, L"[DoUninstallation] Uninstall is starting\n");
|
|
|
|
//
|
|
// Display the prompt for installation if we're
|
|
// not in quiet mode
|
|
//
|
|
if (!g_si.fQuiet) {
|
|
|
|
LoadString(g_si.hInstance, IDS_UNINSTALL_PROMPT, wszTemp, MAX_PATH);
|
|
|
|
wsprintf(wszBegin, wszTemp, g_si.lpwPrettyAppName);
|
|
|
|
if (MessageBox(NULL,
|
|
wszBegin,
|
|
g_si.lpwMessageBoxTitle,
|
|
MB_YESNO | MB_ICONQUESTION)!=IDYES)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Log an event that the uninstall is starting
|
|
//
|
|
LogEventDisplayError(EVENTLOG_INFORMATION_TYPE,
|
|
ID_UNINSTALL_START,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
//
|
|
// Get section names from the INF
|
|
//
|
|
if (!UninstallGetSectionsFromINF()) {
|
|
|
|
Print(ERROR, L"[DoUninstallation] Failed to get section names from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_INF_SCAN_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove the Uninstall key
|
|
// If we replaced it during install, it will be restored
|
|
// below. If not, no package was installed previously
|
|
//
|
|
fReturn = SetupGetLineTextA(NULL,
|
|
g_si.hInf,
|
|
"Strings",
|
|
"GUID",
|
|
szGuid,
|
|
sizeof(szGuid),
|
|
NULL);
|
|
|
|
if (!fReturn) {
|
|
Print(ERROR, L"[DoUninstallation] Failed to get GUID from INF\n");
|
|
LogEventDisplayError(NULL, EVENTLOG_ERROR_TYPE, ID_GET_INF_FAIL, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
pAnsiToUnicode(szGuid, wszGuid, 80);
|
|
|
|
UninstallDeleteSubKey(REG_UNINSTALL, wszGuid);
|
|
UninstallDeleteSubKey(REG_ACTIVE_SETUP, wszGuid);
|
|
|
|
//
|
|
// Delete any registry keys in prep for restore
|
|
//
|
|
if (!CommonDeleteRegistryKeys()) {
|
|
|
|
Print(ERROR, L"[DoUninstallation] Failed to delete registry keys\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_REG_DELETE_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Restore registry keys we replaced
|
|
//
|
|
if (!UninstallRestoreRegistryKeys()) {
|
|
|
|
Print(ERROR, L"[DoUninstallation] Failed to restore registry keys from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_REG_RESTORE_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The routine below does some custom things
|
|
//
|
|
UninstallCustomWorker();
|
|
|
|
//
|
|
// Remove files under the installation directory
|
|
//
|
|
UninstallRemoveFiles();
|
|
|
|
//
|
|
// Restore any files we replaced
|
|
//
|
|
if (!UninstallRestoreFiles()) {
|
|
Print(ERROR, L"[DoUninstallation] Failed to restore files from INF\n");
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_FILE_RESTORE_FAILED, TRUE, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove the '$Uninstall$ directory and delete the uninstall INF file
|
|
//
|
|
wsprintf(wszBackupDir,
|
|
L"%s\\%s",
|
|
g_si.lpwInstallDirectory,
|
|
g_si.lpwUninstallDirectory);
|
|
|
|
Print(TRACE, L"[DoUninstallation] Path to Uninstall dir: %s\n", wszBackupDir);
|
|
|
|
CommonRemoveDirectoryAndFiles(wszBackupDir, (PVOID) FALSE, TRUE, FALSE);
|
|
|
|
SetupCloseInfFile(g_si.hInf);
|
|
|
|
DeleteFile(g_si.lpwUninstallINFPath);
|
|
|
|
//
|
|
// Perform a reboot
|
|
//
|
|
if (!g_si.fQuiet) {
|
|
|
|
LoadString(g_si.hInstance, IDS_REBOOT_NEEDED, wszTemp, MAX_PATH);
|
|
|
|
// Prompt the user to reboot
|
|
nReturn = MessageBox(NULL,
|
|
wszTemp,
|
|
g_si.lpwMessageBoxTitle,
|
|
MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (nReturn == IDYES) {
|
|
|
|
if (!ShutdownSystem(FALSE, TRUE)) {
|
|
|
|
// The shutdown failed - prompt the user for a manual one
|
|
LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_UNINS_REBOOT_FAILED, TRUE, FALSE);
|
|
|
|
} else {
|
|
|
|
// The shutdown was successful - write a message to the event log
|
|
LogEventDisplayError(EVENTLOG_INFORMATION_TYPE, ID_UNINSTALL_SUCCESSFUL, FALSE, FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
// Interactive mode - the user said no to the reboot - write it to the event log
|
|
// Don't display a message.
|
|
LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_UNINS_QREBOOT_NEEDED, FALSE, FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
// Quiet mode - A reboot is needed - write it to event log
|
|
LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_UNINS_QREBOOT_NEEDED, FALSE, FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|