From 52ec79b07fcc2579198f26c415262b5dbe5947cc Mon Sep 17 00:00:00 2001 From: "curt%netscape.com" Date: Sat, 23 Nov 2002 02:05:24 +0000 Subject: [PATCH] Allow for multiple client installations. (Bug #176791, r=jbetak, sr=dveditz) --- xpinstall/packager/win_gre/config.it | 11 +- xpinstall/wizard/windows/setup/ifuncns.c | 54 ++++-- xpinstall/wizard/windows/setup/setup.h | 1 + xpinstall/wizard/windows/uninstall/extra.c | 171 ++++++++++++++---- xpinstall/wizard/windows/uninstall/extra.h | 6 +- xpinstall/wizard/windows/uninstall/parser.h | 1 - .../wizard/windows/uninstall/uninstall.h | 3 +- 7 files changed, 194 insertions(+), 53 deletions(-) diff --git a/xpinstall/packager/win_gre/config.it b/xpinstall/packager/win_gre/config.it index 5e91492df30d..a7023399f260 100644 --- a/xpinstall/packager/win_gre/config.it +++ b/xpinstall/packager/win_gre/config.it @@ -502,6 +502,15 @@ Filename=install_wizard.log Source=[SETUP PATH] Destination=[SETUP PATH]\Uninstall +; Legal values for the Overwrite Name key are TRUE, FALSE and ENUMERATE. +; ENUMERATE is used for shared installs and means: +; 1) Starting with 01, increment though the NameXX named values in the +; Windows registry (where Name is the value from Name= and XX is +; the integer value being incremented) until a NameXX is found with +; the same value as that in Name Value=. +; 2) If no NameXX is found with that value, create a new name/value pair +; named NameXX where XX is the next unused integer value +; [Windows Registry0] Root Key=HKEY_LOCAL_MACHINE Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\$ProductName$ ($UserAgentShort$) @@ -556,7 +565,7 @@ Decrypt Key=TRUE Decrypt Name=FALSE Decrypt Name Value=TRUE Overwrite Key=TRUE -Overwrite Name=TRUE +Overwrite Name=ENUMERATE Timing=post smartupdate [Windows Registry4] diff --git a/xpinstall/wizard/windows/setup/ifuncns.c b/xpinstall/wizard/windows/setup/ifuncns.c index 78f98944a561..3d1e74697089 100644 --- a/xpinstall/wizard/windows/setup/ifuncns.c +++ b/xpinstall/wizard/windows/setup/ifuncns.c @@ -1913,19 +1913,23 @@ HRESULT ProcessWinReg(DWORD dwTiming, char *szSectionPrefix) char szBuf[MAX_BUF]; char szKey[MAX_BUF]; char szName[MAX_BUF]; + char szShortName[MAX_BUF]; char szValue[MAX_BUF]; char szDecrypt[MAX_BUF]; char szOverwriteKey[MAX_BUF]; char szOverwriteName[MAX_BUF]; char szSection[MAX_BUF]; HKEY hRootKey; + BOOL bDone; BOOL bDnu; BOOL bOverwriteKey; BOOL bOverwriteName; BOOL bOSDetected; DWORD dwIndex; + DWORD dwNameIndex = 1; DWORD dwType; DWORD dwSize; + const DWORD dwUpperLimit = 100; __int64 iiNum; dwIndex = 0; @@ -1960,7 +1964,30 @@ HRESULT ProcessWinReg(DWORD dwTiming, char *szSectionPrefix) else lstrcpy(szName, szBuf); - if(lstrcmpi(szOverwriteName, "FALSE") == 0) + if(lstrcmpi(szOverwriteName, "ENUMERATE") == 0) + { + bOverwriteName = FALSE; + lstrcpy(szShortName, szName); + wsprintf(szName, "%s%02d", szShortName, dwNameIndex++); + + bDone = FALSE; + while(!bDone && (dwNameIndex < dwUpperLimit)) + { + if(WinRegNameExists(hRootKey, szKey, szName)) + { + GetWinReg(hRootKey, szKey, szName, szBuf, sizeof(szBuf)); + if(lstrcmpi(szBuf, sgProduct.szAppPath) == 0) + bDone = TRUE; + else + wsprintf(szName, "%s%02d", szShortName, dwNameIndex++); + } + else + bDone = TRUE; + } + if(dwNameIndex >= dwUpperLimit) + return FO_ERROR_INCR_EXCEEDS_LIMIT; + } + else if(lstrcmpi(szOverwriteName, "FALSE") == 0) bOverwriteName = FALSE; else bOverwriteName = TRUE; @@ -1985,6 +2012,7 @@ HRESULT ProcessWinReg(DWORD dwTiming, char *szSectionPrefix) szBuf, sizeof(szBuf), szFileIniConfig); + if(lstrcmpi(szBuf, "TRUE") == 0) bDnu = TRUE; else @@ -2002,8 +2030,7 @@ HRESULT ProcessWinReg(DWORD dwTiming, char *szSectionPrefix) * If there are any, then compare against the global OS value to * make sure there's a match. */ bOSDetected = TRUE; - if((*szBuf != '\0') && - ((gSystemInfo.dwOSType & ParseOSType(szBuf)) == 0)) + if( (*szBuf != '\0') && ((gSystemInfo.dwOSType & ParseOSType(szBuf)) == 0) ) bOSDetected = FALSE; if(bOSDetected) @@ -2015,17 +2042,18 @@ HRESULT ProcessWinReg(DWORD dwTiming, char *szSectionPrefix) szBuf, sizeof(szBuf), szFileIniConfig); - if(ParseRegType(szBuf, &dwType)) - { - /* create/set windows registry key here (string value)! */ - SetWinReg(hRootKey, szKey, bOverwriteKey, szName, bOverwriteName, + + if(ParseRegType(szBuf, &dwType)) + { + /* create/set windows registry key here (string value)! */ + SetWinReg(hRootKey, szKey, bOverwriteKey, szName, bOverwriteName, dwType, (CONST LPBYTE)szValue, lstrlen(szValue), TRUE, bDnu); - } - else - { - iiNum = _atoi64(szValue); - /* create/set windows registry key here (binary/dword value)! */ - SetWinReg(hRootKey, szKey, bOverwriteKey, szName, bOverwriteName, + } + else + { + iiNum = _atoi64(szValue); + /* create/set windows registry key here (binary/dword value)! */ + SetWinReg(hRootKey, szKey, bOverwriteKey, szName, bOverwriteName, dwType, (CONST LPBYTE)&iiNum, dwSize, TRUE, bDnu); } } diff --git a/xpinstall/wizard/windows/setup/setup.h b/xpinstall/wizard/windows/setup/setup.h index c7ce994c5440..886c6b3baa60 100644 --- a/xpinstall/wizard/windows/setup/setup.h +++ b/xpinstall/wizard/windows/setup/setup.h @@ -177,6 +177,7 @@ typedef int PRInt32; #define FO_ERROR_DESTINATION_CONFLICT 2 #define FO_ERROR_CHANGE_DIR 3 #define FO_ERROR_WRITE 4 +#define FO_ERROR_INCR_EXCEEDS_LIMIT 5 /* Mode of Setup to run in */ #define NORMAL 0 diff --git a/xpinstall/wizard/windows/uninstall/extra.c b/xpinstall/wizard/windows/uninstall/extra.c index 9a319f3054e0..26b7c256fd4a 100644 --- a/xpinstall/wizard/windows/uninstall/extra.c +++ b/xpinstall/wizard/windows/uninstall/extra.c @@ -802,7 +802,9 @@ HRESULT InitUninstallGeneral() return(1); if((ugUninstall.szUninstallFilename = NS_GlobalAlloc(MAX_BUF)) == NULL) return(1); - if((ugUninstall.szAppID = NS_GlobalAlloc(MAX_BUF)) == NULL) + if((ugUninstall.szClientAppID = NS_GlobalAlloc(MAX_BUF)) == NULL) + return(1); + if((ugUninstall.szClientAppPath = NS_GlobalAlloc(MAX_BUF)) == NULL) return(1); return(0); @@ -822,7 +824,8 @@ void DeInitUninstallGeneral() FreeMemory(&(ugUninstall.szCompanyName)); FreeMemory(&(ugUninstall.szProductName)); FreeMemory(&(ugUninstall.szWrMainKey)); - FreeMemory(&(ugUninstall.szAppID)); + FreeMemory(&(ugUninstall.szClientAppID)); + FreeMemory(&(ugUninstall.szClientAppPath)); DeleteObject(ugUninstall.definedFont); } @@ -943,7 +946,13 @@ void ParseCommandLine(LPSTR lpszCmdLine) // Set the App ID { if((i + 1) < iArgC) - GetArgV(lpszCmdLine, ++i, ugUninstall.szAppID, MAX_BUF); + GetArgV(lpszCmdLine, ++i, ugUninstall.szClientAppID, MAX_BUF); + } + else if((lstrcmpi(szArgVBuf, "-app_path") == 0) || (lstrcmpi(szArgVBuf, "/app_path") == 0)) + // Set the App Path + { + if((i + 1) < iArgC) + GetArgV(lpszCmdLine, ++i, ugUninstall.szClientAppPath, MAX_BUF); } else if((lstrcmpi(szArgVBuf, "-reg_path") == 0) || (lstrcmpi(szArgVBuf, "/reg_path") == 0)) // Set the alternative Windows registry path @@ -1339,6 +1348,8 @@ BOOL CheckLegacy(HWND hDlg) return(FALSE); } +// This function looks up the path for the application being uninstalled, NOT the client +// app in a shared install environment. HRESULT GetAppPath() { char szTmpAppPath[MAX_BUF]; @@ -1449,9 +1460,8 @@ DWORD CleanupAppList() if(lstrcmp(siALTmp->szAppID, szDefaultApp) == 0) bFoundDefaultApp = TRUE; - // ProcessAppItem returns true if the App is installed - if(ProcessAppItem(ugUninstall.hWrMainRoot, szKey, siALTmp->szAppID)) - dwAppCount++; + // ProcessAppItem returns the # of installations of the App + dwAppCount = dwAppCount + ProcessAppItem(ugUninstall.hWrMainRoot, szKey, siALTmp->szAppID); siALPrev = siALTmp; siALTmp = siALTmp->Next; @@ -1475,53 +1485,104 @@ DWORD CleanupAppList() // Removes the app item if it is the app identified with the /app command-line option // If an app item is not installed this removes it from the app list. -// Returns TRUE if the app item is installed, FALSE if the app is not installed. -BOOL ProcessAppItem(HKEY hkRootKey, LPSTR szKeyAppList, LPSTR szAppID) +// Returns the # of installations of the app item found in the AppList. +DWORD ProcessAppItem(HKEY hkRootKey, LPSTR szKeyAppList, LPSTR szAppID) { + DWORD dwCount = 0; + DWORD dwIndex = 1; + const DWORD dwUpperLimit = 100; + BOOL bDefaultApp; char szBuf[MAX_BUF]; char szKey[MAX_BUF]; + char szName[MAX_BUF]; char szUninstKey[MAX_BUF]; - char szDefaultApp[MAX_BUF_TINY]; - GetPrivateProfileString("General", "Default AppID", "", szDefaultApp, MAX_BUF_TINY, szFileIniUninstall); + GetPrivateProfileString("General", "Default AppID", "", szBuf, sizeof(szBuf), szFileIniUninstall); + bDefaultApp = (lstrcmp(szAppID, szBuf) == 0); wsprintf(szKey, "%s\\%s", szKeyAppList, szAppID); - GetWinReg(hkRootKey, szKey, "PathToExe", szBuf, sizeof(szBuf)); - if(lstrcmp(szAppID, ugUninstall.szAppID) == 0) - { - // This is the app that the user said on the command-line to uninstall, - // so it needs to be removed from the AppList - RegDeleteKey(hkRootKey, szKey); - if(lstrcmp(szAppID, szDefaultApp) == 0) - { - wsprintf(szKey, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\GRE (%s)", ugUninstall.szUserAgent); + if(lstrcmp(szAppID, ugUninstall.szClientAppID) == 0) // This is the app that the user said + { // on the command-line to uninstall. + + if((ugUninstall.szClientAppPath[0] == '\0') || (bDefaultApp)) //If we didn't get an /app_path or this + { // is the default app, just remove it. RegDeleteKey(hkRootKey, szKey); + if(bDefaultApp) + { + wsprintf(szKey, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\GRE (%s)", ugUninstall.szUserAgent); + RegDeleteKey(hkRootKey, szKey); + } + + return 0; } - return FALSE; + wsprintf(szName, "PathToExe%02d", dwIndex); + while( (WinRegNameExists(hkRootKey, szKey, szName)) // Since we got an /app_path we need to cycle + && (dwIndex < dwUpperLimit) ) // through the list looking for that instance. + { + GetWinReg(hkRootKey, szKey, szName, szBuf, MAX_BUF); + if( (lstrcmp(szBuf, ugUninstall.szClientAppPath) == 0) || (!FileExists(szBuf)) ) + RemovePathToExeXX(hkRootKey, szKey, dwIndex, dwUpperLimit); + else + dwCount++; + + wsprintf(szName, "PathToExe%02d", ++dwIndex); + } + + if(dwCount == 0) + RegDeleteKey(hkRootKey, szKey); + + return dwCount; } - if(FileExists(szBuf)) - // Any app the user is not uninstalling that is still on the machine - // should be counted. - return TRUE; - - if(lstrcmp(szAppID, szDefaultApp) == 0) - { - // The Default App does not have any installed files registered. An entry in - // the Windows Add/Remove products list indicates that the Default App is installed, - // however, and needs to be counted. + if(bDefaultApp) // The Default App does not have an installed file registered. However, an entry in + { // the Windows Add/Remove products list indicates that the Default App is installed + // and needs to be counted. wsprintf(szUninstKey, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s (%s)",ugUninstall.szProductName, ugUninstall.szUserAgent); GetWinReg(hkRootKey, szUninstKey, "UninstallString", szBuf, sizeof(szBuf)); if(szBuf[0] != '\0') - return TRUE; + return 1; } - // The only entries left in the AppList are orphaned entries. Remove them. - RegDeleteKey(hkRootKey, szKey); - return FALSE; + wsprintf(szName, "PathToExe%02d", dwIndex); + while(WinRegNameExists(hkRootKey, szKey, szName)) // Count the entries which can be verified by the + { // existence of the file pointed to by PathToExeXX + GetWinReg(hkRootKey, szKey, szName, szBuf, MAX_BUF); + if(FileExists(szBuf)) + { + dwCount++; + } + else // This is an orphaned entry. Remove it from the list. + RemovePathToExeXX(hkRootKey, szKey, dwIndex, dwUpperLimit); // When we remove a PathToExeXX registry setting we must + // reenumerate the list so there are no gaps in the count. + wsprintf(szName, "PathToExe%02d", ++dwIndex); + } + + if(dwCount == 0) + RegDeleteKey(hkRootKey, szKey); + + return dwCount; +} + +void RemovePathToExeXX(HKEY hkRootKey, LPSTR szKey, DWORD dwIndex, const DWORD dwUpperLimit) +{ + char szBuf[MAX_BUF_TINY]; + char szName[MAX_BUF_TINY]; + char szNextName[MAX_BUF_TINY]; + + wsprintf(szName, "PathToExe%02d", dwIndex++); + wsprintf(szNextName, "PathToExe%02d", dwIndex); + while(WinRegNameExists(hkRootKey, szKey, szNextName) && (dwIndex < dwUpperLimit)) + { + GetWinReg(hkRootKey, szKey, szNextName, szBuf, MAX_BUF); + SetWinReg(hkRootKey, szKey, szName, REG_SZ, szBuf, lstrlen(szBuf)); + lstrcpy(szName, szNextName); + wsprintf(szNextName, "PathToExe%02d", ++dwIndex); + } + + DeleteWinRegValue(hkRootKey, szKey, szName); } HRESULT GetUninstallLogPath() @@ -1708,7 +1769,7 @@ HRESULT ParseUninstallIni(LPSTR lpszCmdLine) // what version of GRE to clean up so we can't do anything--including CleanupAppList() // so don't change the order of the if statement. // (We should add UI warning the user when a required UserAgent is not supplied.) - // CleanupAppList() returns the number of installed apps dependant on this shared app. + // CleanupAppList() returns the number of installations of apps dependant on this shared app. if((ugUninstall.szUserAgent[0] == '\0') || (CleanupAppList() != 0)) ugUninstall.bUninstallFiles = FALSE; } @@ -2180,6 +2241,46 @@ HRESULT FileExists(LPSTR szFile) } } +BOOL WinRegNameExists(HKEY hkRootKey, LPSTR szKey, LPSTR szName) +{ + HKEY hkResult; + DWORD dwErr; + DWORD dwSize; + char szBuf[MAX_BUF]; + BOOL bNameExists = FALSE; + + szBuf[0] = '/0'; + if((dwErr = RegOpenKeyEx(hkRootKey, szKey, 0, KEY_READ, &hkResult)) == ERROR_SUCCESS) + { + dwSize = sizeof(szBuf); + dwErr = RegQueryValueEx(hkResult, szName, 0, NULL, szBuf, &dwSize); + + if((*szBuf != '\0') && (dwErr == ERROR_SUCCESS)) + bNameExists = TRUE; + + RegCloseKey(hkResult); + } + + return(bNameExists); +} + +void DeleteWinRegValue(HKEY hkRootKey, LPSTR szKey, LPSTR szName) +{ + HKEY hkResult; + DWORD dwErr; + + dwErr = RegOpenKeyEx(hkRootKey, szKey, 0, KEY_WRITE, &hkResult); + if(dwErr == ERROR_SUCCESS) + { + if(*szName == '\0') + dwErr = RegDeleteValue(hkResult, NULL); + else + dwErr = RegDeleteValue(hkResult, szName); + + RegCloseKey(hkResult); + } +} + void DeInitialize() { DeInitDlgUninstall(&diUninstall); diff --git a/xpinstall/wizard/windows/uninstall/extra.h b/xpinstall/wizard/windows/uninstall/extra.h index 5d83d27c4736..91944d379935 100644 --- a/xpinstall/wizard/windows/uninstall/extra.h +++ b/xpinstall/wizard/windows/uninstall/extra.h @@ -93,8 +93,10 @@ void SetUninstallRunMode(LPSTR szMode); void Delay(DWORD dwSeconds); HRESULT GetAppPath(); DWORD CleanupAppList(); -BOOL ProcessAppItem(HKEY hkRoot, LPSTR szKeyAppList, LPSTR szAppID); +DWORD ProcessAppItem(HKEY hkRoot, LPSTR szKeyAppList, LPSTR szAppID); +void RemovePathToExeXX(HKEY hkRoot, LPSTR szKey, DWORD dwIndex, DWORD dwUpperLimit); HRESULT GetUninstallLogPath(); - +BOOL WinRegNameExists(HKEY hkRootKey, LPSTR szKey, LPSTR szName); +void DeleteWinRegValue(HKEY hkRootKey, LPSTR szKey, LPSTR szName); #endif diff --git a/xpinstall/wizard/windows/uninstall/parser.h b/xpinstall/wizard/windows/uninstall/parser.h index c365c20f29fb..b23362444fe3 100644 --- a/xpinstall/wizard/windows/uninstall/parser.h +++ b/xpinstall/wizard/windows/uninstall/parser.h @@ -35,7 +35,6 @@ void ParseForCopyFile(LPSTR szString, LPSTR szKeyStr, LPSTR szFile, DWORD void ParseForWinRegInfo(LPSTR szString, LPSTR szKeyStr, LPSTR szRootKey, DWORD dwRootKeyBufSize, LPSTR szKey, DWORD dwKeyBufSize, LPSTR szName, DWORD dwNameBufSize); void ParseForUninstallCommand(LPSTR szString, LPSTR szKeyStr, LPSTR szFile, DWORD dwFileBufSize, LPSTR szParam, DWORD dwParamBufSize); void DeleteWinRegKey(HKEY hkRootKey, LPSTR szKey, BOOL bAbsoluteDelete); -void DeleteWinRegValue(HKEY hkRootKey, LPSTR szKey, LPSTR szName); DWORD GetLogFile(LPSTR szTargetPath, LPSTR szInFilename, LPSTR szOutBuf, DWORD dwOutBufSize); void RemoveUninstaller(LPSTR szUninstallFilename); DWORD DecrementSharedFileCounter(char *file); diff --git a/xpinstall/wizard/windows/uninstall/uninstall.h b/xpinstall/wizard/windows/uninstall/uninstall.h index d9ca8ad7f40c..82110b322ba0 100644 --- a/xpinstall/wizard/windows/uninstall/uninstall.h +++ b/xpinstall/wizard/windows/uninstall/uninstall.h @@ -134,7 +134,8 @@ typedef struct uninstallStruct LPSTR szWrKey; LPSTR szUserAgent; LPSTR szDefaultComponent; - LPSTR szAppID; + LPSTR szClientAppID; + LPSTR szClientAppPath; BOOL bVerbose; BOOL bUninstallFiles; BOOL bSharedInst;