/*++ Copyright (c) 2001 Microsoft Corporation Module Name: Dufns.cpp Abstract: Functions used throughout the app. Notes: Unicode only. History: 03/02/2001 rparsons Created --*/ #include "precomp.h" extern SETUP_INFO g_si; /*++ Routine Description: Parses the command line and fills in our structure that contains app-related data Arguments: None Return Value: TRUE on success, FALSE otherwise --*/ BOOL ParseCommandLine() { int nArgc = 0; int nCount = 0; LPWSTR *lpwCommandLine = NULL; // // Initialize structure members to defaults // g_si.fQuiet = FALSE; g_si.fInstall = TRUE; g_si.fForceInstall = FALSE; g_si.nErrorLevel = ERROR; g_si.fNoReboot = FALSE; g_si.fNoUninstall = FALSE; g_si.fNeedToAdjustACL = FALSE; g_si.fOnWin2K = FALSE; g_si.fUpdateDllCache = TRUE; g_si.fCanUninstall = TRUE; g_si.nErrorLevel = ERROR; // // Get the command line // lpwCommandLine = CommandLineToArgvW(GetCommandLine(), &nArgc); if (NULL == lpwCommandLine) { return FALSE; } for (nCount = 1; nCount < nArgc; nCount++) { if ((lpwCommandLine[nCount][0] == L'-') || (lpwCommandLine[nCount][0] == L'/')) { switch (lpwCommandLine[nCount][1]) { case 'q': case 'Q': g_si.fQuiet = TRUE; break; case 'i': case 'I': g_si.fInstall = TRUE; break; case 'u': case 'U': g_si.fInstall = FALSE; break; case 'f': case 'F': g_si.fForceInstall = TRUE; break; case 'd': case 'D': g_si.nErrorLevel = TRACE; break; case 'z': case 'Z': g_si.fNoReboot = TRUE; break; case 'n': case 'N': g_si.fNoUninstall = TRUE; break; } } } GlobalFree(lpwCommandLine); return TRUE; } /*++ Routine Description: Determines if another instance of the application is running Arguments: lpwInstanceName - Name of the instance to look for Return Value: TRUE if another instance is present, FALSE otherwise --*/ BOOL IsAnotherInstanceRunning( IN LPCWSTR lpwInstanceName ) { HANDLE hMutex = NULL; DWORD dwLastError = 0; // // Attempt to create a mutex with the given name // hMutex = CreateMutex(NULL, FALSE, lpwInstanceName); if (NULL != hMutex) { // // See if it was created by another instance // dwLastError = GetLastError(); if (ERROR_ALREADY_EXISTS == dwLastError) { CloseHandle(hMutex); return TRUE; } else { CloseHandle(hMutex); return FALSE; } } return FALSE; } /*++ Routine Description: Obtains the path that the EXE is currently running from. This memory is allocated with the MALLOC macro and should be released with the FREE macro Arguments: None Return Value: On success, a pointer to our current directory On failure, NULL The directory path will not contain a trailing backslash --*/ LPWSTR GetCurWorkingDirectory() { WCHAR wszFullPath[MAX_PATH]; LPWSTR lpwEnd = NULL, lpwReturn = NULL; DWORD dwLen = 0; // // Retrieve the full path where we're running from // dwLen = GetModuleFileName(NULL, wszFullPath, MAX_PATH); if (dwLen <= 0) { return NULL; } // // Allocate memory and store the path // lpwReturn = (LPWSTR) MALLOC((wcslen(wszFullPath)+1)*sizeof(WCHAR)); if (NULL == lpwReturn) { return NULL; } wcscpy(lpwReturn, wszFullPath); // // Find the last backslash // lpwEnd = wcsrchr(lpwReturn, '\\'); if (NULL == lpwEnd) { return NULL; } // // Trim off the EXE name // if (lpwEnd){ *lpwEnd = '\0'; } return (lpwReturn); } /*++ Routine Description: Obtains a non-string value from the given INF file Arguments: hInf - Handle to the INF lpSectionName - Name of the section lpKeyName - Name of the key pdwValue - Receives the value Return Value: TRUE on success, FALSE otherwise --*/ BOOL GetInfValue( IN HINF hInf, IN LPCSTR lpSectionName, IN LPCSTR lpKeyName, OUT PDWORD pdwValue ) { BOOL fReturn = FALSE; char szBuffer[MAX_PATH] = ""; fReturn = SetupGetLineTextA(NULL, hInf, lpSectionName, lpKeyName, szBuffer, sizeof(szBuffer), NULL); *pdwValue = strtoul(szBuffer, NULL, 0); return (fReturn); } /*++ Routine Description: Determines if the user is an administrator Arguments: None Return Value: -1 on failure 1 if the user is an admin 0 if they are not --*/ int IsUserAnAdministrator() { HANDLE hToken; DWORD dwStatus = 0, dwAccessMask = 0; DWORD dwAccessDesired = 0, dwACLSize = 0; DWORD dwStructureSize = sizeof(PRIVILEGE_SET); PACL pACL = NULL; PSID psidAdmin = NULL; BOOL fReturn = FALSE; int nReturn = -1; PRIVILEGE_SET ps; GENERIC_MAPPING GenericMapping; PSECURITY_DESCRIPTOR psdAdmin = NULL; SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; __try { // AccessCheck() requires an impersonation token if (!ImpersonateSelf(SecurityImpersonation)) { __leave; } // Attempt to access the token for the current thread if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) { if (GetLastError() != ERROR_NO_TOKEN) { __leave; } // If the thread does not have an access token, we'll // examine the access token associated with the process. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) __leave; } // Build a SID for administrators group if (!AllocateAndInitializeSid(&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin)) __leave; // Allocate memory for the security descriptor psdAdmin = MALLOC(SECURITY_DESCRIPTOR_MIN_LENGTH); if (psdAdmin == NULL) { __leave; } // Initialize the new security descriptor if (!InitializeSecurityDescriptor(psdAdmin, SECURITY_DESCRIPTOR_REVISION)) { __leave; } // Compute size needed for the ACL dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD); // Allocate memory for ACL pACL = (PACL) MALLOC(dwACLSize); if (pACL == NULL) { __leave; } // Initialize the new ACL if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2)) { __leave; } dwAccessMask = ACCESS_READ | ACCESS_WRITE; // Add the access-allowed ACE to the DACL if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin)) { __leave; } // Set our DACL to the security descriptor if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE)) { __leave; } // AccessCheck is sensitive about the format of the // security descriptor; set the group & owner SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE); SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE); // Ensure that the SD is valid if (!IsValidSecurityDescriptor(psdAdmin)) { __leave; } dwAccessDesired = ACCESS_READ; // Initialize GenericMapping structure even though we // won't be using generic rights. GenericMapping.GenericRead = ACCESS_READ; GenericMapping.GenericWrite = ACCESS_WRITE; GenericMapping.GenericExecute = 0; GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE; // After all that work, it boils down to this call if (!AccessCheck(psdAdmin, hToken, dwAccessDesired, &GenericMapping, &ps, &dwStructureSize, &dwStatus, &fReturn)) { __leave; } RevertToSelf(); // Everything was a success nReturn = (int) fReturn; } // try __finally { if (pACL) { FREE(pACL); } if (psdAdmin){ FREE(psdAdmin); } if (psidAdmin){ FreeSid(psidAdmin); } } // finally return (nReturn); } /*++ Routine Description: Gets the amount of available disk space on the drive that contains the Windows system files (boot partition) Arguments: None Return Value: A 64-bit value containing the free space available on success, 0 on failure --*/ DWORDLONG GetDiskSpaceFreeOnNTDrive() { UINT nLen = 0; BOOL fUseHeap = FALSE, fReturn = FALSE; WCHAR wszSysDir[MAX_PATH] = L""; WCHAR wszDiskName[4] = {'x',':','\\','\0'}; LPWSTR lpwSysDir = NULL; DWORDLONG dwlReturn = 0; unsigned __int64 i64FreeBytesToCaller = 0, i64TotalBytes = 0, i64TotalFreeBytes = 0; nLen = GetSystemDirectory(wszSysDir, MAX_PATH); if (0 == nLen) { return 0; } if (nLen > MAX_PATH) { // // Allocate a buffer that will hold the return // lpwSysDir = (LPWSTR) MALLOC(nLen*sizeof(WCHAR)); if (NULL == lpwSysDir) { return 0; } GetSystemDirectory(lpwSysDir, nLen*sizeof(WCHAR)); fUseHeap = TRUE; } if (!fUseHeap) { wszDiskName[0] = wszSysDir[0]; } else { wszDiskName[0] = lpwSysDir[0]; } fReturn = GetDiskFreeSpaceEx(wszDiskName, (PULARGE_INTEGER) &i64FreeBytesToCaller, (PULARGE_INTEGER) &i64TotalBytes, (PULARGE_INTEGER) &i64TotalFreeBytes); if (FALSE == fReturn) { return 0; } // // Calculate free MBs on drive // dwlReturn = i64FreeBytesToCaller / 0x100000; if (lpwSysDir) { FREE(lpwSysDir); } return (dwlReturn); } /*++ Routine Description: Displays a formated error to the screen Arguments: hWnd - Handle to the parent window dwMessageId - Message identifier lpwMessageArray - An array of insertion strings Return Value: TRUE on success, FALSE otherwise --*/ BOOL DisplayErrMsg( IN HWND hWnd, IN DWORD dwMessageId, IN LPWSTR lpwMessageArray ) { LPVOID lpMsgBuf = NULL; DWORD dwReturn = 0; int nReturn = 0; WCHAR wszError[MAX_PATH] = L""; dwReturn = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, g_si.hInstance, dwMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &lpMsgBuf, 0, (va_list*) lpwMessageArray); if (0 == dwReturn) { // // We can't let it go without displaying an error message // to the user // nReturn = LoadString(g_si.hInstance, IDS_FORMAT_MESSAGE_FAILED, wszError, MAX_PATH); if (0 == nReturn) { // // Major problems - can't pull a string from the EXE // MessageBox(hWnd, LOAD_STRING_FAILED, g_si.lpwMessageBoxTitle, MB_OK | MB_ICONERROR); return FALSE; } MessageBox(hWnd, wszError, g_si.lpwMessageBoxTitle, MB_OK | MB_ICONERROR); return FALSE; } // // Display the intended message to the user // MessageBox(hWnd, (LPCWSTR) lpMsgBuf, g_si.lpwMessageBoxTitle, MB_OK | MB_ICONERROR); LocalFree(lpMsgBuf); return TRUE; } /*++ Routine Description: Given a version string, convert it to a long DWORD. This string will be in the form of aa,bb,cc,dd Arguments: lpwVersionString - The string to convert lpVersionNumber - Receives the converted version Return Value: TRUE on success, FALSE otherwise --*/ BOOL VersionStringToNumber( IN LPCWSTR lpwVersionString, IN OUT DWORDLONG *lpVersionNumber ) { DWORDLONG dwVersion = 0; DWORD dwSubVersion; int nIndex; for (nIndex = 0; nIndex < 4; nIndex++) { if (*lpwVersionString == '\0') { return FALSE; } dwSubVersion = 0; while ((*lpwVersionString >= '0') && (*lpwVersionString <= '9')) { dwSubVersion *= 10; dwSubVersion += ( *lpwVersionString - '0' ); if (dwSubVersion > 0x0000FFFF) { return FALSE; } lpwVersionString++; } if (*lpwVersionString == ',') { lpwVersionString++; } else if (*lpwVersionString != '\0') { return FALSE; } dwVersion <<= 16; dwVersion += dwSubVersion; } if (lpVersionNumber != NULL) { *lpVersionNumber = dwVersion; } return TRUE; } /*++ Routine Description: Converts a UNICODE string to an ANSI one Arguments: lpwUnicodeString - The UNICODE string lpAnsiString - Receives the ANSI string nLen - The size of the ANSI buffer Return Value: None --*/ void pUnicodeToAnsi( IN LPCWSTR lpwUnicodeString, IN OUT LPSTR lpAnsiString, IN int nLen ) { WideCharToMultiByte(CP_ACP, 0, lpwUnicodeString, -1, lpAnsiString, nLen, NULL, NULL); return; } /*++ Routine Description: Converts an ANSI string to a UNICODE one Arguments: lpAnsiString - The ANSI string lpwUnicodeString - Receives the UNICODE string nLen - The size of the UNICODE buffer Return Value: None --*/ void pAnsiToUnicode( IN LPCSTR lpAnsiString, IN OUT LPWSTR lpwUnicodeString, IN int nLen ) { MultiByteToWideChar(CP_ACP, 0, lpAnsiString, -1, lpwUnicodeString, nLen); return; } /*++ Routine Description: Initializes frequently used strings, paths, etc. Arguments: None Return Value: TRUE on success, FALSE otherwise --*/ BOOL WUInitialize() { BOOL fReturn = FALSE; int nReturn = 0; LPWSTR lpwCurrentDirectory = NULL; char szTemp[MAX_PATH] = ""; WCHAR wszTemp[MAX_PATH] = L""; WCHAR wszInstInfFileName[MAX_PATH] = L""; char szInstInfFileName[MAX_PATH] = ""; WCHAR wszUninstInfFileName[MAX_PATH] = L""; char szUninstInfFileName[MAX_PATH] = ""; char szMessageBoxTitle[MAX_PATH] = ""; WCHAR wszMessageBoxTitle[MAX_PATH] = L""; char szComponentName[MAX_PATH] = ""; WCHAR wszComponentName[MAX_PATH] = L""; char szEventLogSourceName[MAX_PATH] = ""; WCHAR wszEventLogSourceName[MAX_PATH] = L""; char szDestDir[MAX_PATH] = ""; WCHAR wszDestDir[MAX_PATH] = L""; char szUninstallDir[MAX_PATH] = ""; WCHAR wszUninstallDir[MAX_PATH] = L""; WCHAR wszSysDir[MAX_PATH] = L""; WCHAR wszWinDir[MAX_PATH] = L""; UINT uDirLen = 0; DWORD dwNoReboot = 0, dwNoUninstall = 0, dwQuiet = 0; DWORD dwAdjustACL = 0, dwUpdateDllCache = 0; DWORDLONG dwlFreeSpace = 0; OSVERSIONINFOEX osviex; // // Perform a version check - Windows 2000 or greater // ZeroMemory(&osviex, sizeof(OSVERSIONINFOEX)); osviex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionEx((OSVERSIONINFO *) &osviex); if ((osviex.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osviex.dwMajorVersion == 5) && (osviex.dwMinorVersion == 0)) { g_si.fOnWin2K = TRUE; } else if ((osviex.dwPlatformId != VER_PLATFORM_WIN32_NT) && (osviex.dwMajorVersion < 5)) { return FALSE; } // // Determine where we're running from and save it // lpwCurrentDirectory = GetCurWorkingDirectory(); if (NULL == lpwCurrentDirectory) { return FALSE; } g_si.lpwExtractPath = (LPWSTR) MALLOC((wcslen(lpwCurrentDirectory)+1)*sizeof(WCHAR)); if (NULL == g_si.lpwExtractPath) { return FALSE; } wcscpy(g_si.lpwExtractPath, lpwCurrentDirectory); // // Set up the path to the install INF, make it ANSI, and save it // wsprintf(wszInstInfFileName, L"%s\\%s", lpwCurrentDirectory, INF_FILE_NAMEW); pUnicodeToAnsi(wszInstInfFileName, szInstInfFileName, MAX_PATH); g_si.lpwInstallINFPath = (LPWSTR) MALLOC((wcslen(wszInstInfFileName)+1)*sizeof(WCHAR)); if (NULL == g_si.lpwInstallINFPath) { return FALSE; } wcscpy(g_si.lpwInstallINFPath, wszInstInfFileName); // // Set up the path to the uninstall INF, make it ANSI, and save it // wsprintf(wszUninstInfFileName, L"%s\\%s", lpwCurrentDirectory, UNINST_INF_FILE_NAMEW); pUnicodeToAnsi(wszUninstInfFileName, szUninstInfFileName, MAX_PATH); FREE(lpwCurrentDirectory); g_si.lpwUninstallINFPath = (LPWSTR) MALLOC((wcslen(wszUninstInfFileName)+1)*sizeof(WCHAR)); if (NULL == g_si.lpwUninstallINFPath) { return FALSE; } wcscpy(g_si.lpwUninstallINFPath, wszUninstInfFileName); // // We need to grab some strings from the INF // g_si.hInf = SetupOpenInfFileA(g_si.fInstall ? szInstInfFileName : szUninstInfFileName, NULL, INF_STYLE_WIN4, NULL); if ((NULL == g_si.hInf) || (INVALID_HANDLE_VALUE == g_si.hInf)) { Print(ERROR, L"[WUInitialize] Failed to open INF\n"); return FALSE; } fReturn = SetupGetLineTextA(NULL, g_si.hInf, "Strings", "MessageBoxTitle", szMessageBoxTitle, sizeof(szMessageBoxTitle), NULL); if (!fReturn) { return FALSE; } fReturn = SetupGetLineTextA(NULL, g_si.hInf, "Strings", "ComponentName", szComponentName, sizeof(szComponentName), NULL); if (!fReturn) { return FALSE; } fReturn = SetupGetLineTextA(NULL, g_si.hInf, "Strings", "EventLogSourceName", szEventLogSourceName, sizeof(szEventLogSourceName), NULL); if (!fReturn) { return FALSE; } fReturn = SetupGetLineTextA(NULL, g_si.hInf, "Strings", "DestDir", szTemp, sizeof(szTemp), NULL); if (!fReturn) { return FALSE; } fReturn = SetupGetLineTextA(NULL, g_si.hInf, "Strings", "UninstallDirName", szUninstallDir, sizeof(szUninstallDir), NULL); if (!fReturn) { return FALSE; } if (g_si.fInstall) { fReturn = GetInfValue(g_si.hInf, "Version", "RequiredFreeSpaceNoUninstall", &g_si.dwRequiredFreeSpaceNoUninstall); if (!fReturn) { return FALSE; } fReturn = GetInfValue(g_si.hInf, "Version", "RequiredFreeSpaceWithUninstall", &g_si.dwRequiredFreeSpaceWithUninstall); if (!fReturn) { return FALSE; } GetInfValue(g_si.hInf, "Version", "NoReboot", &dwNoReboot); GetInfValue(g_si.hInf, "Version", "NoUninstall", &dwNoUninstall); GetInfValue(g_si.hInf, "Version", "AdjustACLOnTargetDir", &dwAdjustACL); GetInfValue(g_si.hInf, "Version", "UpdateDllCache", &dwUpdateDllCache); } GetInfValue(g_si.hInf, "Version", "RunQuietly", &dwQuiet); if (dwNoReboot) { g_si.fNoReboot = TRUE; } if (dwNoUninstall) { g_si.fNoUninstall = TRUE; } if (dwQuiet) { g_si.fQuiet = TRUE; } if (dwAdjustACL) { g_si.fNeedToAdjustACL = TRUE; } if (!dwUpdateDllCache) { g_si.fUpdateDllCache = FALSE; } // // Expand the directory string // pAnsiToUnicode(szTemp, wszTemp, MAX_PATH); ExpandEnvironmentStrings(wszTemp, wszDestDir, MAX_PATH); // // Save the strings for later use // pAnsiToUnicode(szMessageBoxTitle, wszMessageBoxTitle, MAX_PATH); pAnsiToUnicode(szComponentName, wszComponentName, MAX_PATH); pAnsiToUnicode(szEventLogSourceName, wszEventLogSourceName, MAX_PATH); pAnsiToUnicode(szUninstallDir, wszUninstallDir, MAX_PATH); uDirLen = GetSystemDirectory(wszSysDir, MAX_PATH); if (0 == uDirLen) { return FALSE; } uDirLen = GetWindowsDirectory(wszWinDir, MAX_PATH); if (0 == uDirLen) { return FALSE; } g_si.lpwMessageBoxTitle = (LPWSTR) MALLOC((wcslen(wszMessageBoxTitle)+1)*sizeof(WCHAR)); g_si.lpwPrettyAppName = (LPWSTR) MALLOC((wcslen(wszComponentName)+1)*sizeof(WCHAR)); g_si.lpwEventLogSourceName = (LPWSTR) MALLOC((wcslen(wszEventLogSourceName)+1)*sizeof(WCHAR)); g_si.lpwInstallDirectory = (LPWSTR) MALLOC((wcslen(wszDestDir)+1)*sizeof(WCHAR)); g_si.lpwUninstallDirectory = (LPWSTR) MALLOC((wcslen(wszUninstallDir)+1)*sizeof(WCHAR)); g_si.lpwSystem32Directory = (LPWSTR) MALLOC((wcslen(wszSysDir)+1)*sizeof(WCHAR)); g_si.lpwWindowsDirectory = (LPWSTR) MALLOC((wcslen(wszWinDir)+1)*sizeof(WCHAR)); if ((NULL == g_si.lpwMessageBoxTitle) || (NULL == g_si.lpwPrettyAppName) || (NULL == g_si.lpwEventLogSourceName) || (NULL == g_si.lpwInstallDirectory) || (NULL == g_si.lpwUninstallDirectory) || (NULL == g_si.lpwSystem32Directory) || (NULL == g_si.lpwWindowsDirectory)) { return FALSE; } wcscpy(g_si.lpwMessageBoxTitle, wszMessageBoxTitle); wcscpy(g_si.lpwPrettyAppName, wszComponentName); wcscpy(g_si.lpwEventLogSourceName, wszEventLogSourceName); wcscpy(g_si.lpwInstallDirectory, wszDestDir); wcscpy(g_si.lpwUninstallDirectory, wszUninstallDir); wcscpy(g_si.lpwSystem32Directory, wszSysDir); wcscpy(g_si.lpwWindowsDirectory, wszWinDir); // // Ensure that the user has administrator rights // nReturn = IsUserAnAdministrator(); if (0 == nReturn) { // // The user is not an admin // LogEventDisplayError(EVENTLOG_ERROR_TYPE, ID_NOT_ADMIN, TRUE, FALSE); return FALSE; } else if (-1 == nReturn) { // // The access check failed. Assume that problems occured // during the check and continue. The worst thing that will // happen is the user will see an error later on // LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_ADMIN_CHECK_FAILED, FALSE, FALSE); return FALSE; } // // Verify that we have enough disk space for the install // if (g_si.fInstall) { dwlFreeSpace = GetDiskSpaceFreeOnNTDrive(); if (dwlFreeSpace < g_si.dwRequiredFreeSpaceNoUninstall) { // // Not enough space available to complete // LogEventDisplayError(EVENTLOG_WARNING_TYPE, ID_NOT_ENOUGH_DISK_SPACE, TRUE, FALSE); return FALSE; } } return TRUE; } /*++ Routine Description: Releases any memory used throughout the app Arguments: None Return Value: None --*/ void WUCleanup() { if (NULL != g_si.lpwEventLogSourceName) { FREE(g_si.lpwEventLogSourceName); } if (NULL != g_si.lpwExtractPath) { FREE(g_si.lpwExtractPath); } if (NULL != g_si.lpwInstallDirectory) { FREE(g_si.lpwInstallDirectory); } if (NULL != g_si.lpwInstallINFPath) { FREE(g_si.lpwInstallINFPath); } if (NULL != g_si.lpwUninstallINFPath) { FREE(g_si.lpwUninstallINFPath); } if (NULL != g_si.lpwMessageBoxTitle) { FREE(g_si.lpwMessageBoxTitle); } if (NULL != g_si.lpwPrettyAppName) { FREE(g_si.lpwPrettyAppName); } if (NULL != g_si.lpwSystem32Directory) { FREE(g_si.lpwSystem32Directory); } if (NULL != g_si.lpwUninstallDirectory) { FREE(g_si.lpwUninstallDirectory); } if (NULL != g_si.lpwWindowsDirectory) { FREE(g_si.lpwWindowsDirectory); } if (NULL != g_si.hInf) { SetupCloseInfFile(g_si.hInf); } return; } /*++ Routine Description: Takes the DACL from a given directory and applies it to another directory Arguments: lpwSourceDir - The directory to get the DACL from lpwDestDir - The directory to apply the DACL to Return Value: TRUE on success, FALSE otherwise --*/ BOOL MirrorDirectoryPerms( IN LPWSTR lpwSourceDir, IN LPWSTR lpwDestDir ) { DWORD dwResult = 0; BOOL fResult = FALSE; PACL pDACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; PSID pOwnerSid; __try { // // Get the DACL from the source directory // dwResult = GetNamedSecurityInfo(lpwSourceDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, &pOwnerSid, NULL, &pDACL, NULL, &pSD); if (ERROR_SUCCESS != dwResult) { __leave; } // // Attach the DACL to the destination directory // dwResult = SetNamedSecurityInfo(lpwDestDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, pOwnerSid, NULL, pDACL, NULL); if (ERROR_SUCCESS != dwResult) { __leave; } fResult = TRUE; } //try __finally { if (NULL != pSD) { LocalFree((HLOCAL) pSD); } } //finally return (fResult); } /*++ Routine Description: Adjusts permissions on the given directory Arguments: lpwDirPath - Directory to modify Return Value: TRUE on success, FALSE otherwise --*/ BOOL AdjustDirectoryPerms( IN LPWSTR lpwDirPath ) { DWORD dwResult = 0; PSID pSid = NULL; BOOL fReturn = FALSE, fResult = FALSE; PACL pOldDACL = NULL, pNewDACL = NULL; EXPLICIT_ACCESS ea; PSECURITY_DESCRIPTOR pSD = NULL; SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY; __try { // // Get the DACL for the directory // dwResult = GetNamedSecurityInfo(lpwDirPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD); if (ERROR_SUCCESS != dwResult) { __leave; } // // Build a SID to the local Users group // fReturn = AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &pSid); if (!fReturn) { __leave; } // // Ensure that the SID is valid // fReturn = IsValidSid(pSid); if (!fReturn) { __leave; } // // Initialize an EXPLICIT_ACCESS structure for the new ACE // ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE; ea.grfAccessMode = GRANT_ACCESS; ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.ptstrName = (LPTSTR) pSid; // // Create a new ACL that merges the new ACE // into the existing DACL // dwResult = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL); if (ERROR_SUCCESS != dwResult) { __leave; } // // Attach the new ACL as the object's DACL // dwResult = SetNamedSecurityInfo(lpwDirPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL); if (ERROR_SUCCESS != dwResult) { __leave; } // Everything was a success fResult = TRUE; } // try __finally { if (NULL != pSD) { LocalFree((HLOCAL) pSD); } if (NULL != pNewDACL) { LocalFree((HLOCAL) pNewDACL); } if (NULL != pSid) { FreeSid(pSid); } } //finally return (fResult); } /*++ Routine Description: Retrieves version information from a catalog file Arguments: lpwCatName - The catalog file to check Return Value: A DWORD which contains a version number for the catalog --*/ DWORD GetCatVersion( IN LPWSTR lpwCatName ) { char szVersionString[MAX_PATH] = ""; HANDLE hCat = NULL; DWORD dwVer = 0; CRYPTCATATTRIBUTE *pSpCatAttr = NULL; NTSTATUS Status; __try { hCat = CryptCATOpen(lpwCatName, 0, 0, 0, 0); if (NULL == hCat) { __leave; } pSpCatAttr = CryptCATGetCatAttrInfo(hCat, L"SPAttr"); if ((pSpCatAttr == NULL) || (pSpCatAttr->pbValue == NULL)) { __leave; } pUnicodeToAnsi((LPWSTR)pSpCatAttr->pbValue, szVersionString, MAX_PATH); Status = RtlCharToInteger(szVersionString, 10, (PULONG)&dwVer); if (!NT_SUCCESS(Status)) { __leave; } } // try __finally { if (hCat) { CryptCATClose(hCat); } } // finally return (dwVer); } /*++ Routine Description: Wraps CreateProcess and WaitForSingleObject Arguments: lpwCommandLine - Command line to execute pdwReturnCode - Receives a return code from the process Return Value: TRUE on success, FALSE otherwise --*/ BOOL LaunchProcessAndWait( IN LPCWSTR lpwCommandLine, OUT PDWORD pdwReturnCode ) { PROCESS_INFORMATION pi; STARTUPINFO si; BOOL fReturn = FALSE; LPWSTR lpwCmdLine = NULL; // // CreateProcess requires a non-const command line // lpwCmdLine = (LPWSTR) MALLOC((wcslen(lpwCommandLine)+1)*sizeof(WCHAR)); if (NULL == lpwCmdLine) { return FALSE; } wcscpy(lpwCmdLine, lpwCommandLine); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; fReturn = CreateProcess(NULL, lpwCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if (!fReturn) { return FALSE; } WaitForSingleObject(pi.hProcess, INFINITE); if (NULL != pdwReturnCode) { GetExitCodeProcess(pi.hProcess, pdwReturnCode); } if (NULL != lpwCmdLine) { FREE(lpwCmdLine); } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return TRUE; } /*++ Routine Description: Callback function for our directory enumeration routine Arguments: lpwPath - Path of the directory or filename pFindFileData - Pointer to WIN32_FIND_DATA struct containing information about lpwPath pEnumerateParameter - A 32-bit enumeration parameter Return Value: TRUE on success, FALSE otherwise --*/ BOOL DeleteOneFile( IN LPCWSTR lpwPath, IN PWIN32_FIND_DATA pFindFileData, IN PVOID pEnumerateParameter ) { WCHAR wszTempPath[MAX_PATH] = L""; char szFileName[MAX_PATH] = ""; WCHAR wszFileName[MAX_PATH] = L""; char szEntry[MAX_QUEUE_SIZE] = ""; WCHAR wszEntry[MAX_QUEUE_SIZE] = L""; WCHAR wszTestPath[MAX_PATH] = L""; BOOL fCheckQueue = FALSE, fReturn = FALSE, fDelete = TRUE; LPWSTR lpwDestDir = NULL; int nLen = 0; LONG cLines = 0; INFCONTEXT InfContext; if (pFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { return TRUE; } // // Note: This may seem a little complex, but because there could // multiple entries in a section, we have to run through them // each time this function gets called. // // // See if there's anything in the exclusion queue // fCheckQueue = (BOOL) pEnumerateParameter; if (fCheckQueue) { nLen = g_si.ExclusionQueue.GetSize(); while (nLen != 0) { // // Walk through the queue and make sure // we don't delete files we shouldn't // g_si.ExclusionQueue.Dequeue(wszEntry, MAX_QUEUE_SIZE - 1, TRUE); pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH); // // Get the destination directory // GetNextToken(wszEntry, L"."); lpwDestDir = GetNextToken(NULL, L"."); if (NULL == lpwDestDir) { return FALSE; } // // Get the number of files in the section // cLines = SetupGetLineCountA(g_si.hInf, szEntry); // // Loop through all the lines in the exclusion section(s), // and do a comparison // fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL); while (fReturn) { while (cLines != 0) { pAnsiToUnicode(szFileName, wszFileName, MAX_PATH); // // Put the path together // wsprintf(wszTestPath, L"%s\\%s", lpwDestDir, wszFileName); wcscpy(wszTempPath, lpwPath); CharLower(wszTestPath); CharLower(wszTempPath); // // See if the paths match // if (wcsstr(wszTempPath, wszTestPath)) { fDelete = FALSE; break; // paths match, move to the next file } SetupFindNextLine(&InfContext, &InfContext); SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL); --cLines; } if (fDelete) { ForceDelete(lpwPath); fDelete = TRUE; // reset the flag } fReturn = SetupFindNextLine(&InfContext, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL); } nLen--; } } else { // // Don't worry about the queue, delete the file // ForceDelete(lpwPath); } return TRUE; } /*++ Routine Description: Retrieves a token, given a separator. Basically a wrapper for strtok Arguments: lpwSourceString - The source string to parse lpwSeparator - The separator string that specifies the delimiter Return Value: A pointer to the return string --*/ LPWSTR GetNextToken( IN LPWSTR lpwSourceString, IN LPCWSTR lpwSeparator ) { LPWSTR lpwReturn = NULL; lpwReturn = wcstok(lpwSourceString, lpwSeparator); return (lpwReturn); } /*++ Routine Description: Attempts to move a file. If it's in use, replace it on reboot Arguments: lpwSourceFileName - The source file name lpwDestFileName - The destination file name Return Value: TRUE on success, FALSE otherwise --*/ BOOL ForceMove( IN LPCWSTR lpwSourceFileName, IN LPCWSTR lpwDestFileName ) { WCHAR wszTempPath[MAX_PATH] = L""; WCHAR wszDelFileName[MAX_PATH] = L""; if (!lpwSourceFileName || !lpwDestFileName) { return FALSE; } if (!MoveFileEx(lpwSourceFileName, lpwDestFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { Print(TRACE, L"[ForceMove] First call to MoveFileEx failed. GLE = %d\n", GetLastError()); if (!GetTempPath(MAX_PATH, wszTempPath)) { return FALSE; } if (!GetTempFileName(wszTempPath, L"DEL", 0, wszDelFileName)) { return FALSE; } if (!MoveFileEx(lpwDestFileName, wszDelFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { Print(ERROR, L"[ForceMove] Second call to MoveFileEx failed. GLE = %d\n", GetLastError()); return FALSE; } if (!MoveFileEx(wszDelFileName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) { Print(ERROR, L"[ForceMove] Third call to MoveFileEx failed. GLE = %d\n", GetLastError()); return FALSE; } if (!MoveFileEx(lpwSourceFileName, lpwDestFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { Print(ERROR, L"[ForceMove] Second call to MoveFileEx failed. GLE = %d\n", GetLastError()); return FALSE; } } return TRUE; } /*++ Routine Description: Attempts to delete a file. If it's in use, delete it on reboot Arguments: lpwFileName - The file name to delete Return Value: TRUE on success, FALSE otherwise --*/ BOOL ForceDelete( IN LPCWSTR lpwFileName ) { WCHAR *pTempFile = NULL; WCHAR *pExt = NULL; // // Attempt to delete the specified file // SetFileAttributes(lpwFileName, FILE_ATTRIBUTE_NORMAL); if (!DeleteFile(lpwFileName)) { // // The file is most likely in use // Attempt to rename it // pTempFile = _wcsdup(lpwFileName); pExt = PathFindExtension(pTempFile); if (pExt) { wcscpy(pExt, L"._wu"); } if (MoveFile(lpwFileName, pTempFile)) { // // We renamed the target - delete it on reboot // MoveFileEx(pTempFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); } else { // // Couldn't rename it - mark it for deletion on reboot // MoveFileEx(lpwFileName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); } free(pTempFile); } return TRUE; } /*++ Routine Description: Attempts to copy a file If it's in use, move it and replace on reboot Arguments: lpwSourceFileName - The source file name lpwDestFileName - The destination file name Return Value: TRUE on success, FALSE otherwise --*/ BOOL ForceCopy( IN LPCWSTR lpwSourceFileName, IN LPCWSTR lpwDestFileName ) { WCHAR wszTempPath[MAX_PATH] = L""; WCHAR wszDelFileName[MAX_PATH] = L""; if (!lpwSourceFileName || !lpwDestFileName) { return FALSE; } if (!CopyFile(lpwSourceFileName, lpwDestFileName, FALSE)) { if (!GetTempPath(MAX_PATH, wszTempPath)) { return FALSE; } if (!GetTempFileName(wszTempPath, L"DEL", 0, wszDelFileName)) { return FALSE; } if (!MoveFileEx(lpwDestFileName, wszDelFileName, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { return FALSE; } if (!MoveFileEx(wszDelFileName, NULL, MOVEFILE_COPY_ALLOWED | MOVEFILE_DELAY_UNTIL_REBOOT)) { return FALSE; } if (!CopyFile(lpwSourceFileName, lpwDestFileName, FALSE)) { return FALSE; } } return TRUE; } /*++ Routine Description: Determines if a file is protected by WFP Arguments: lpwFileName - Name of the file Return Value: TRUE if the file is protected, FALSE otherwise --*/ BOOL IsFileProtected( IN LPCWSTR lpwFileName ) { BOOL fReturn = FALSE; if (lpwFileName == NULL) { return FALSE; } return (SfcIsFileProtected(NULL, lpwFileName)); } /*++ Routine Description: Retrieve file version and language info from a file The version is specified in the dwFileVersionMS and dwFileVersionLS fields of a VS_FIXEDFILEINFO, as filled in by the win32 version APIs. For the language we look at the translation table in the version resources and assume that the first langid/codepage pair specifies the language If the file is not a coff image or does not have version resources, the function fails. The function does not fail if we are able to retrieve the version but not the language Arguments: lpwFileName - Supplies the full path of the file whose version data is desired pdwVersion - Receives the version stamp of the file. If the file is not a coff image or does not contain the appropriate version resource data, the function fails Return Value: TRUE on success, FALSE otherwise --*/ BOOL GetVersionInfoFromImage( IN LPCWSTR lpwFileName, OUT PDWORDLONG pdwVersion ) { DWORD nSize = 0L, dwIgnored = 0L; UINT nDataLength = 0; BOOL fResult = FALSE; PVOID pVersionBlock = NULL; VS_FIXEDFILEINFO *FixedVersionInfo; // // Get the size of version info block // nSize = GetFileVersionInfoSize((LPWSTR)lpwFileName, &dwIgnored); if (nSize) { // // Allocate memory block of sufficient size to hold version info block // pVersionBlock = MALLOC(nSize*sizeof(WCHAR)); if (pVersionBlock) { // // Get the version block from the file. // if (GetFileVersionInfo((LPWSTR)lpwFileName, 0, nSize*sizeof(WCHAR), pVersionBlock)) { // // Get fixed version info // if (VerQueryValue(pVersionBlock, L"\\", (LPVOID*) &FixedVersionInfo, &nDataLength)) { // // Return version to caller // *pdwVersion = (((DWORDLONG)FixedVersionInfo->dwFileVersionMS) << 32) + FixedVersionInfo->dwFileVersionLS; fResult = TRUE; } } FREE(pVersionBlock); } } return (fResult); } /*++ Routine Description: Copies the specified file giving it a temporary name Arguments: lpwFileName - The full path & name of the file Return Value: A pointer to the full path & name of the file The caller is responsible for freeing the memory --*/ LPWSTR CopyTempFile( IN LPCWSTR lpwSrcFileName, IN LPCWSTR lpwDestDir ) { LPWSTR lpwTempFileName = NULL; lpwTempFileName = (LPWSTR) MALLOC(MAX_PATH * sizeof(WCHAR)); if (NULL == lpwTempFileName) { return NULL; } if (!GetTempFileName(lpwDestDir, L"_wu", 0, lpwTempFileName)) { FREE(lpwTempFileName); return NULL; } else { // // Copy the source file down to the destination directory // using the name we just got // if (!CopyFile(lpwSrcFileName, lpwTempFileName, FALSE)) { FREE(lpwTempFileName); return NULL; } } return (lpwTempFileName); } /*++ Routine Description: Prints formatted debug strings Arguments: uLevel - A flag to indicate if the message should be displayed lpwFmt - A string to be displayed ... - A variable length argument list of insertion strings Return Value: TRUE on success, FALSE otherwise --*/ #if DBG void Print( IN UINT uLevel, IN LPWSTR lpwFmt, IN ... ) { va_list arglist; if (g_si.nErrorLevel < uLevel) { return; } va_start(arglist, lpwFmt); _vsnwprintf(g_si.wszDebugOut, 2047, lpwFmt, arglist); g_si.wszDebugOut[2047] = 0; va_end(arglist); OutputDebugString(g_si.wszDebugOut); return; } #endif /*++ Routine Description: Reboots the system Arguments: fForceClose - A flag to indicate if apps should be forced to close fReboot - A flag to indicate if we should reboot after shutdown Return Value: TRUE on success, FALSE otherwise --*/ BOOL ShutdownSystem( IN BOOL fForceClose, IN BOOL fReboot ) { BOOL fResult = FALSE; if (!ModifyTokenPrivilege(L"SeShutdownPrivilege", TRUE)) { return FALSE; } fResult = InitiateSystemShutdown(NULL, // machinename NULL, // shutdown message 0, // delay fForceClose, // force apps close fReboot // reboot after shutdown ); ModifyTokenPrivilege(L"SeShutdownPrivilege", FALSE); return (fResult); } /*++ Routine Description: Enables or disables a specified privilege Arguments: lpwPrivilege - The name of the privilege fEnable - A flag to indicate if the privilege should be enabled Return Value: TRUE on success, FALSE otherwise --*/ BOOL ModifyTokenPrivilege( IN LPCWSTR lpwPrivilege, IN BOOL fEnable ) { HANDLE hToken = NULL; LUID luid; BOOL fResult = FALSE; TOKEN_PRIVILEGES tp; if (NULL == lpwPrivilege) { return FALSE; } __try { // // Get a handle to the access token associated with the current process // OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (NULL == hToken) { __leave; } // // Obtain a LUID for the specified privilege // if (!LookupPrivilegeValue(NULL, lpwPrivilege, &luid)) { __leave; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0; // // Modify the access token // if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { __leave; } fResult = TRUE; } // try __finally { if (hToken) { CloseHandle(hToken); } } // finally return (fResult); } /*++ Routine Description: Saves the specified information to the file that will be used by the uninstaller Arguments: lpwSectionName - The name of the section where our entry will be saved lpwKeyName - A 1-based number in a string. It should be unique for each entry in the section lpwEntryName - The value or entry to save lpwFileName - The file to save our changes to Return Value: TRUE on success, FALSE otherwise --*/ BOOL SaveEntryToINF( IN LPCWSTR lpwSectionName, IN LPCWSTR lpwKeyName, IN LPCWSTR lpwEntryName, IN LPCWSTR lpwFileName ) { BOOL fReturn = FALSE; // // Write the entry to the INF // fReturn = WritePrivateProfileString(lpwSectionName, lpwKeyName, lpwEntryName, lpwFileName); if (!fReturn) { Print(ERROR, L"[SaveEntryToINF] Failed to save entry: Section: %\nKey Name: %s\n Entry: %s\n File: %s\n"); } return (fReturn ? TRUE : FALSE); } /*++ Routine Description: Deletes the specified registry keys during install Arguments: None Return Value: TRUE on success, FALSE otherwise --*/ BOOL CommonDeleteRegistryKeys() { BOOL fReturn = FALSE, fResult = FALSE; HKEY hKeyRoot = NULL; char szEntry[MAX_PATH] = ""; WCHAR wszEntry[MAX_PATH] = L""; char szKeyPath[MAX_PATH*3] = ""; WCHAR wszKeyPath[MAX_PATH*3] = L""; LPWSTR lpwKeyRoot = NULL, lpwSubKey = NULL; UINT uCount = 0; CRegistry creg; INFCONTEXT InfContext; // // Step through each entry in the queue // while (g_si.DeleteRegistryQueue.GetSize()) { g_si.DeleteRegistryQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE); pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH); // // Loop through all the lines in the delete registry section(s), // and perform the deletion // fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szKeyPath, MAX_PATH, NULL); while (fReturn) { pAnsiToUnicode(szKeyPath, wszKeyPath, MAX_PATH*3); // // Split the key path into two separate parts // lpwKeyRoot = GetNextToken(wszKeyPath, L","); if (NULL == lpwKeyRoot) { break; } Print(TRACE, L"[CommonDeleteRegistryKeys] 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; } lpwSubKey = GetNextToken(NULL, L","); if (NULL == lpwSubKey) { break; } Print(TRACE, L"[CommonDeleteRegistryKeys] Subkey path %s\n", lpwSubKey); // // Verify that the specified key exists // fResult = creg.IsRegistryKeyPresent(hKeyRoot, lpwSubKey); if (fResult) { // // Do a recursive delete on the key // SHDeleteKey(hKeyRoot, lpwSubKey); } fReturn = SetupFindNextLine(&InfContext, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szKeyPath, MAX_PATH, NULL); } } return TRUE; } /*++ Routine Description: Based on the flag, performs a server registration or performs a server removal (unregistration) Arguments: fRegister - A flag to indicate if we should perform a register server Return Value: TRUE on success, FALSE otherwise --*/ BOOL CommonRegisterServers( IN BOOL fRegister ) { int nLen = 0; char szFileName[MAX_PATH] = ""; WCHAR wszFileToRun[MAX_PATH] = L""; WCHAR wszFileName[MAX_PATH] = L""; char szEntry[MAX_PATH] = ""; WCHAR wszEntry[MAX_PATH] = L""; WCHAR wszEntryToSave[MAX_PATH*2] = L""; WCHAR wszKey[10] = L""; WCHAR wszSectionName[MAX_PATH] = L""; BOOL fReturn = FALSE; UINT uCount = 0; LPWSTR lpwDestDir = NULL; INFCONTEXT InfContext; // // Step through each entry in the queue // while (g_si.RegistrationQueue.GetSize()) { g_si.RegistrationQueue.Dequeue(wszEntry, MAX_PATH - 1, FALSE); pUnicodeToAnsi(wszEntry, szEntry, MAX_PATH); // // Get the destination directory // GetNextToken(wszEntry, L"."); lpwDestDir = GetNextToken(NULL, L"."); if (NULL == lpwDestDir) { return FALSE; } // // Loop through all the lines in the approriate section, // spawning off each one // fReturn = SetupFindFirstLineA(g_si.hInf, szEntry, NULL, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL); while (fReturn) { pAnsiToUnicode(szFileName, wszFileName, MAX_PATH); // // Set up the path to the file to (un)register and run it // if (fRegister) { wsprintf(wszFileToRun, L"regsvr32.exe /s \"%s\\%s\\%s\"", g_si.lpwWindowsDirectory, lpwDestDir, wszFileName); } else { wsprintf(wszFileToRun, L"regsvr32.exe /s /u \"%s\"", wszFileName); } Print(TRACE, L"[CommonRegisterServers] Preparing to launch %s\n", wszFileToRun); LaunchProcessAndWait(wszFileToRun, NULL); if (fRegister) { // // Now save an entry to the queue // wsprintf(wszEntryToSave, L"%s\\%s\\%s", g_si.lpwWindowsDirectory, lpwDestDir, wszFileName); wsprintf(wszKey, L"%u", ++uCount); wsprintf(wszSectionName, L"%s.%s", INF_UNREGISTRATIONSW, lpwDestDir); SaveEntryToINF(wszSectionName, wszKey, wszEntryToSave, g_si.lpwUninstallINFPath); } fReturn = SetupFindNextLine(&InfContext, &InfContext) && SetupGetLineTextA(&InfContext, NULL, NULL, NULL, szFileName, MAX_PATH, NULL); } } return TRUE; } /*++ Routine Description: Removes all files from the specified directory, then removes the directory Arguments: lpwDirectoryPath - Full path to the directory pEnumerateParameter - A parameter for misc storage fRemoveDirectory - A flag to indicate if we should remove the directory fRemoveSubDirs - A flag to indicate if we should remove subdirectories Return Value: TRUE on success, FALSE otherwise --*/ BOOL CommonRemoveDirectoryAndFiles( IN LPCWSTR lpwDirectoryPath, IN PVOID pEnumerateParameter, IN BOOL fRemoveDirectory, IN BOOL fRemoveSubDirs ) { CEnumDir ced; // // Remove any files from the directory // ced.EnumerateDirectoryTree(lpwDirectoryPath, DeleteOneFile, fRemoveSubDirs, pEnumerateParameter); // // Now try to remove the directory itself // if (fRemoveDirectory) { SetFileAttributes(lpwDirectoryPath, FILE_ATTRIBUTE_NORMAL); if (!RemoveDirectory(lpwDirectoryPath)) { Print(ERROR, L"[CommonRemoveDirectoryAndFiles] Failed to remove directory %s\n", lpwDirectoryPath); return FALSE; } } return TRUE; } /*++ Routine Description: Enables protected renames in the registry Arguments: None Return Value: TRUE on success, FALSE otherwise --*/ BOOL CommonEnableProtectedRenames() { CRegistry creg; BOOL fReturn = FALSE; fReturn = creg.SetDword(HKEY_LOCAL_MACHINE, REG_SESSION_MANAGER, REG_PROT_RENAMES, (DWORD) 1, TRUE); return (fReturn); } /*++ Routine Description: Installs a catalog file Arguments: lpwCatalogFullPath - Full path to the catalog file lpwNewBaseName - Name catalog should get when it's installed in the store Return Value: NO_ERROR on success --*/ #ifdef WIN2KSE DWORD pInstallCatalogFile( IN LPCWSTR lpwCatalogFullPath, IN LPCWSTR lpwNewBaseName ) { DWORD dwError = 0; dwError = VerifyCatalogFile(lpwCatalogFullPath); if (dwError == NO_ERROR) { dwError = InstallCatalog(lpwCatalogFullPath, lpwNewBaseName, NULL); } return (dwError); } #else DWORD pInstallCatalogFile( IN LPCWSTR lpwCatalogFullPath, IN LPCWSTR lpwNewBaseName ) { DWORD dwError = 0; dwError = pSetupVerifyCatalogFile(lpwCatalogFullPath); if (dwError == NO_ERROR) { dwError = pSetupInstallCatalog(lpwCatalogFullPath, lpwNewBaseName, NULL); } return (dwError); } #endif