753 lines
27 KiB
C
753 lines
27 KiB
C
#ifndef __cplusplus
|
|
#error Install stub code must be C++!
|
|
#endif
|
|
|
|
#ifndef HINST_THISDLL
|
|
#error HINST_THISDLL must be defined!
|
|
#endif
|
|
|
|
#ifndef ARRAYSIZE
|
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof((a)[0]))
|
|
#endif
|
|
|
|
#include <ccstock.h>
|
|
#include <stubres.h>
|
|
#include <trayp.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#include <runonce.c> // shared runonce code for ShellExecuteRegApp()
|
|
#ifdef __cplusplus
|
|
};
|
|
#endif
|
|
|
|
BOOL CheckWebViewShell();
|
|
|
|
/* This code runs the install/uninstall stubs recorded in the local-machine
|
|
* part of the registry, iff the current user has not had them run in his
|
|
* context yet. Used for populating the user's profile with things like
|
|
* links to applications.
|
|
*/
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
BOOL ProfilesEnabled(void)
|
|
{
|
|
BOOL fEnabled = FALSE;
|
|
|
|
if (staticIsOS(OS_NT)) {
|
|
fEnabled = TRUE;
|
|
}
|
|
else {
|
|
HKEY hkeyLogon;
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Network\\Logon"), 0,
|
|
KEY_QUERY_VALUE, &hkeyLogon) == ERROR_SUCCESS) {
|
|
DWORD fProfiles, cbData = sizeof(fProfiles), dwType;
|
|
if (RegQueryValueEx(hkeyLogon, TEXT("UserProfiles"), NULL, &dwType,
|
|
(LPBYTE)&fProfiles, &cbData) == ERROR_SUCCESS) {
|
|
if (dwType == REG_DWORD || (dwType == REG_BINARY && cbData == sizeof(DWORD)))
|
|
fEnabled = fProfiles;
|
|
}
|
|
RegCloseKey(hkeyLogon);
|
|
}
|
|
}
|
|
return fEnabled;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// %%Function: GetVersionFromString
|
|
//
|
|
// Snarfed from urlmon\download\helpers.cxx.
|
|
//
|
|
// converts version in text format (a,b,c,d) into two dwords (a,b), (c,d)
|
|
// The printed version number is of format a.b.d (but, we don't care)
|
|
// ---------------------------------------------------------------------------
|
|
HRESULT
|
|
GetVersionFromString(LPCTSTR szBuf, LPDWORD pdwFileVersionMS, LPDWORD pdwFileVersionLS)
|
|
{
|
|
LPCTSTR pch = szBuf;
|
|
TCHAR ch;
|
|
USHORT n = 0;
|
|
|
|
USHORT a = 0;
|
|
USHORT b = 0;
|
|
USHORT c = 0;
|
|
USHORT d = 0;
|
|
|
|
enum HAVE { HAVE_NONE, HAVE_A, HAVE_B, HAVE_C, HAVE_D } have = HAVE_NONE;
|
|
|
|
|
|
*pdwFileVersionMS = 0;
|
|
*pdwFileVersionLS = 0;
|
|
|
|
if (!pch) // default to zero if none provided
|
|
return S_OK;
|
|
|
|
if (lstrcmp(pch, TEXT("-1,-1,-1,-1")) == 0) {
|
|
*pdwFileVersionMS = 0xffffffff;
|
|
*pdwFileVersionLS = 0xffffffff;
|
|
}
|
|
|
|
|
|
for (ch = *pch++;;ch = *pch++) {
|
|
|
|
if ((ch == ',') || (ch == '\0')) {
|
|
|
|
switch (have) {
|
|
|
|
case HAVE_NONE:
|
|
a = n;
|
|
have = HAVE_A;
|
|
break;
|
|
|
|
case HAVE_A:
|
|
b = n;
|
|
have = HAVE_B;
|
|
break;
|
|
|
|
case HAVE_B:
|
|
c = n;
|
|
have = HAVE_C;
|
|
break;
|
|
|
|
case HAVE_C:
|
|
d = n;
|
|
have = HAVE_D;
|
|
break;
|
|
|
|
case HAVE_D:
|
|
return E_INVALIDARG; // invalid arg
|
|
}
|
|
|
|
if (ch == '\0') {
|
|
// all done convert a,b,c,d into two dwords of version
|
|
|
|
*pdwFileVersionMS = ((a << 16)|b);
|
|
*pdwFileVersionLS = ((c << 16)|d);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
n = 0; // reset
|
|
|
|
} else if ( (ch < '0') || (ch > '9'))
|
|
return E_INVALIDARG; // invalid arg
|
|
else
|
|
n = n*10 + (ch - '0');
|
|
|
|
|
|
} /* end forever */
|
|
|
|
// NEVERREACHED
|
|
}
|
|
|
|
|
|
// Reg keys and values for install/uninstall stub list. Each subkey under
|
|
// HKLM\Software\InstalledComponents is a component identifier (GUID).
|
|
// Each subkey has values "Path" for the EXE to run to install or uninstall;
|
|
// IsInstalled (dword) indicating whether the component has been installed
|
|
// or uninstalled; and an optional Revision (dword) used to refresh a
|
|
// component without changing its GUID. Locale (string) is used to describe
|
|
// the language/locale for the component; this string is not interpreted by
|
|
// the install stub code, it is just compared between the HKLM and HKCU keys.
|
|
// If it's different between the two, the stub is re-run.
|
|
//
|
|
// HKCU\Software\InstalledComponents contains similar GUID subkeys, but the
|
|
// only values under each subkey are the optional Revision and Locale values,
|
|
// and an optional DontAsk value (also DWORD). Presence of the subkey indicates
|
|
// that the component is installed for that user.
|
|
//
|
|
// If the DontAsk value is present under an HKCU subkey and is non-zero, that
|
|
// means that the user has decided to keep their settings for that component
|
|
// on all machines, even those that have had the component uninstalled, and
|
|
// that they don't want to be asked if they want to run the uninstall stub
|
|
// every time they log on. This implies that for that user, the uninstall
|
|
// stub will never be run for that component unless the user somehow clears
|
|
// the flag.
|
|
//
|
|
// NOTE: mslocusr.dll also knows these registry paths.
|
|
|
|
const TCHAR c_szRegInstalledComponentsKey[] = TEXT("Software\\Microsoft\\Active Setup\\Installed Components");
|
|
const TCHAR c_szRegInstallStubValue[] = TEXT("StubPath");
|
|
const TCHAR c_szRegIsInstalledValue[] = TEXT("IsInstalled");
|
|
const TCHAR c_szRegInstallSequenceValue[] = TEXT("Version");
|
|
const TCHAR c_szRegDontAskValue[] = TEXT("DontAsk");
|
|
const TCHAR c_szRegLocaleValue[] = TEXT("Locale");
|
|
|
|
|
|
UINT ConfirmUninstall(LPCTSTR pszDescription)
|
|
{
|
|
/* The only case where the user wouldn't want settings cleaned up on
|
|
* uninstall would be if they'd roamed to a machine that had had this
|
|
* component uninstalled. If user profiles aren't enabled (which is
|
|
* the case on a fair number of customers' machines), they're certainly
|
|
* not roaming, so there's not much point in asking them. Just pretend
|
|
* they said YES, they want to clean up the settings.
|
|
*/
|
|
if (!ProfilesEnabled())
|
|
return IDYES;
|
|
|
|
/* FEATURE - change to a dialog with a checkbox for
|
|
* the don't-ask value.
|
|
*/
|
|
|
|
TCHAR szTitle[MAX_PATH];
|
|
#ifdef USERSTUB
|
|
LoadString(HINST_THISDLL, IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
|
|
#else
|
|
MLLoadString(IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
|
|
#endif
|
|
|
|
TCHAR szMessageTemplate[MAX_PATH];
|
|
LPTSTR pszMessage = NULL;
|
|
int cchMessage;
|
|
|
|
#ifdef USERSTUB
|
|
LoadString(HINST_THISDLL, IDS_UNINSTALL, szMessageTemplate, ARRAYSIZE(szMessageTemplate));
|
|
#else
|
|
MLLoadString(IDS_UNINSTALL, szMessageTemplate, ARRAYSIZE(szMessageTemplate));
|
|
#endif
|
|
|
|
|
|
cchMessage = (lstrlen(szMessageTemplate)+lstrlen(pszDescription)+4)*sizeof(TCHAR);
|
|
pszMessage = (LPTSTR)LocalAlloc(LPTR, cchMessage);
|
|
if (pszMessage)
|
|
{
|
|
#ifdef USERSTUB
|
|
wsprintf(pszMessage, szMessageTemplate, pszDescription);
|
|
#else
|
|
wnsprintf(pszMessage, cchMessage, szMessageTemplate, pszDescription);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pszMessage = szMessageTemplate;
|
|
}
|
|
|
|
// due to build in UNICODE the following call is broken under win95, user wsprintf above
|
|
//if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING |
|
|
// FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
// (LPVOID)szMessageTemplate,
|
|
// 0,
|
|
// 0,
|
|
// (LPTSTR)&pszMessage,
|
|
// 0, /* min chars to allocate */
|
|
// (va_list *)&pszDescription)) {
|
|
// pszMessage = szMessageTemplate;
|
|
//}
|
|
|
|
|
|
UINT idRet = MessageBox(NULL, pszMessage, szTitle,
|
|
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_SETFOREGROUND | MB_TOPMOST);
|
|
|
|
if (pszMessage != szMessageTemplate)
|
|
LocalFree(pszMessage);
|
|
|
|
return idRet;
|
|
}
|
|
|
|
|
|
HWND hwndProgress = NULL;
|
|
BOOL fTriedProgressDialog = FALSE;
|
|
|
|
INT_PTR ProgressDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return TRUE;
|
|
|
|
case WM_SETCURSOR:
|
|
SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
return TRUE;
|
|
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void SetProgressInfo(HWND hwndProgress, LPCTSTR pszFriendlyName, BOOL fInstalling)
|
|
{
|
|
HWND hwndInstalling = GetDlgItem(hwndProgress, IDC_RUNNING_INSTALL_STUB);
|
|
HWND hwndUninstalling = GetDlgItem(hwndProgress, IDC_RUNNING_UNINSTALL_STUB);
|
|
|
|
ShowWindow(hwndInstalling, fInstalling ? SW_SHOW : SW_HIDE);
|
|
EnableWindow(hwndInstalling, fInstalling);
|
|
ShowWindow(hwndUninstalling, fInstalling ? SW_HIDE : SW_SHOW);
|
|
EnableWindow(hwndUninstalling, !fInstalling);
|
|
SetDlgItemText(hwndProgress, IDC_INSTALL_STUB_NAME, pszFriendlyName);
|
|
}
|
|
|
|
|
|
void IndicateProgress(LPCTSTR pszFriendlyName, BOOL fInstalling)
|
|
{
|
|
if (hwndProgress == NULL && !fTriedProgressDialog) {
|
|
hwndProgress = CreateDialog(HINST_THISDLL, MAKEINTRESOURCE(IDD_InstallStubProgress),
|
|
NULL, ProgressDialogProc);
|
|
}
|
|
|
|
if (hwndProgress != NULL) {
|
|
SetProgressInfo(hwndProgress, pszFriendlyName, fInstalling);
|
|
if (!fTriedProgressDialog) {
|
|
ShowWindow(hwndProgress, SW_RESTORE);
|
|
SetForegroundWindow(hwndProgress);
|
|
}
|
|
}
|
|
fTriedProgressDialog = TRUE;
|
|
}
|
|
|
|
|
|
void CleanupProgressDialog(void)
|
|
{
|
|
if (hwndProgress != NULL) {
|
|
DestroyWindow(hwndProgress);
|
|
hwndProgress = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL RunOneInstallStub( HKEY hklmList, HKEY hkcuList, LPCTSTR pszKeyName,
|
|
LPCTSTR pszCurrentUsername, int iPass )
|
|
{
|
|
BOOL bNextPassNeeded = FALSE;
|
|
/* See if this component is installed or an uninstall tombstone. */
|
|
HKEY hkeyComponent;
|
|
|
|
DWORD err = RegOpenKeyEx(hklmList, pszKeyName, 0, KEY_QUERY_VALUE,
|
|
&hkeyComponent);
|
|
if (err == ERROR_SUCCESS) {
|
|
TCHAR szCmdLine[MAX_PATH];
|
|
DWORD fIsInstalled;
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof(fIsInstalled);
|
|
HKEY hkeyUser = NULL;
|
|
|
|
/* Must have the stub path; if not there, skip this entry. */
|
|
cbData = sizeof(szCmdLine);
|
|
if (SHQueryValueEx(hkeyComponent, c_szRegInstallStubValue,
|
|
NULL, &dwType, (LPBYTE)szCmdLine,
|
|
&cbData) != ERROR_SUCCESS || ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ)) ) {
|
|
RegCloseKey(hkeyComponent);
|
|
return bNextPassNeeded;
|
|
}
|
|
|
|
TCHAR szDescription[MAX_PATH];
|
|
LPTSTR pszDescription = szDescription;
|
|
cbData = sizeof(szDescription);
|
|
if (SHQueryValueEx(hkeyComponent, TEXT(""),
|
|
NULL, &dwType, (LPBYTE)szDescription,
|
|
&cbData) != ERROR_SUCCESS || dwType != REG_SZ) {
|
|
pszDescription = szCmdLine;
|
|
}
|
|
|
|
|
|
if (RegQueryValueEx(hkeyComponent, c_szRegIsInstalledValue,
|
|
NULL, &dwType, (LPBYTE)&fIsInstalled,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
(dwType != REG_DWORD && (dwType != REG_BINARY || cbData != sizeof(DWORD))))
|
|
fIsInstalled = TRUE;
|
|
|
|
/* If it's installed, check the user's profile, and if the
|
|
* component (or its current revision) isn't installed there,
|
|
* run it.
|
|
*/
|
|
if (fIsInstalled) {
|
|
DWORD dwRevisionHi, dwRevisionLo;
|
|
DWORD dwUserRevisionHi = 0;
|
|
DWORD dwUserRevisionLo = 0;
|
|
BOOL fSetRevision;
|
|
TCHAR szRevision[24], szUserRevision[24]; /* 65535,65535,65535,65535\0 */
|
|
TCHAR szLocale[10], szUserLocale[10]; /* usually not very big strings */
|
|
TCHAR szInstallUsername[128+1]; /* 128 is the win95 system username limit */
|
|
|
|
DWORD fIsCloneUser;
|
|
cbData = sizeof(fIsCloneUser);
|
|
if (RegQueryValueEx(hkeyComponent, TEXT("CloneUser"),
|
|
NULL, &dwType, (LPBYTE)&fIsCloneUser,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
(dwType != REG_DWORD && (dwType != REG_BINARY || cbData != sizeof(DWORD))))
|
|
fIsCloneUser = FALSE;
|
|
|
|
cbData = sizeof(szRevision);
|
|
if (RegQueryValueEx(hkeyComponent, c_szRegInstallSequenceValue,
|
|
NULL, &dwType, (LPBYTE)szRevision,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
dwType != REG_SZ ||
|
|
FAILED(GetVersionFromString(szRevision, &dwRevisionHi, &dwRevisionLo))) {
|
|
fSetRevision = FALSE;
|
|
dwRevisionHi = 0;
|
|
dwRevisionLo = 0;
|
|
}
|
|
else {
|
|
fSetRevision = TRUE;
|
|
}
|
|
|
|
cbData = sizeof(szLocale);
|
|
err = RegQueryValueEx(hkeyComponent, c_szRegLocaleValue,
|
|
NULL, &dwType, (LPBYTE)szLocale,
|
|
&cbData);
|
|
if (err != ERROR_SUCCESS || dwType != REG_SZ) {
|
|
szLocale[0] = '\0';
|
|
}
|
|
|
|
err = RegOpenKeyEx(hkcuList, pszKeyName, 0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyUser);
|
|
if (err == ERROR_SUCCESS) {
|
|
cbData = sizeof(szUserRevision);
|
|
if (RegQueryValueEx(hkeyUser, c_szRegInstallSequenceValue,
|
|
NULL, &dwType, (LPBYTE)szUserRevision,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
dwType != REG_SZ ||
|
|
FAILED(GetVersionFromString(szUserRevision, &dwUserRevisionHi, &dwUserRevisionLo))) {
|
|
dwUserRevisionHi = 0;
|
|
dwUserRevisionLo = 0;
|
|
}
|
|
|
|
if (szLocale[0] != '\0') {
|
|
cbData = sizeof(szUserLocale);
|
|
err = RegQueryValueEx(hkeyUser, c_szRegLocaleValue,
|
|
NULL, &dwType, (LPBYTE)szUserLocale,
|
|
&cbData);
|
|
/* If there's a locale string under the user key
|
|
* and it's the same as the machine one, then we
|
|
* blank out the machine one so we won't consider
|
|
* that when running the stub.
|
|
*/
|
|
if (err == ERROR_SUCCESS && dwType == REG_SZ &&
|
|
!lstrcmp(szLocale, szUserLocale)) {
|
|
szLocale[0] = '\0';
|
|
}
|
|
}
|
|
if (fIsCloneUser) {
|
|
/* Clone-user install stub. We need to re-run it if the
|
|
* username we used when we last installed to this profile,
|
|
* or the one it was copied from, is different from the
|
|
* current username.
|
|
*/
|
|
cbData = sizeof(szInstallUsername);
|
|
if (RegQueryValueEx(hkeyUser, TEXT("Username"),
|
|
NULL, &dwType, (LPBYTE)szInstallUsername,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
dwType != REG_SZ) {
|
|
szInstallUsername[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
hkeyUser = NULL;
|
|
}
|
|
|
|
/* Install if:
|
|
*
|
|
* - User doesn't have component installed, OR
|
|
* - Component installed on machine has a revision AND
|
|
* - Machine component revision greater than user's
|
|
* - OR
|
|
* - Component installed on machine has a locale AND
|
|
* - Machine component locale different than user's
|
|
* (this is actually checked above)
|
|
* - OR
|
|
* - Component is a clone-user install stub and the username
|
|
* recorded for the stub is different from the current username
|
|
*/
|
|
if ((hkeyUser == NULL) ||
|
|
(fSetRevision &&
|
|
((dwRevisionHi > dwUserRevisionHi) ||
|
|
((dwRevisionHi == dwUserRevisionHi) &&
|
|
(dwRevisionLo > dwUserRevisionLo)
|
|
)
|
|
)
|
|
) ||
|
|
(szLocale[0] != '\0') ||
|
|
#ifdef UNICODE
|
|
(fIsCloneUser && StrCmpI(szInstallUsername, pszCurrentUsername))
|
|
#else
|
|
(fIsCloneUser && lstrcmpi(szInstallUsername, pszCurrentUsername))
|
|
#endif
|
|
) {
|
|
|
|
if ( (iPass == -1 ) ||
|
|
((iPass == 0) && (*pszKeyName == '<')) ||
|
|
((iPass == 1) && (*pszKeyName != '<') && (*pszKeyName != '>')) ||
|
|
((iPass == 2) && (*pszKeyName == '>')) )
|
|
{
|
|
// the condition meets, run it now.
|
|
#ifdef TraceMsg
|
|
TraceMsg(TF_WARNING, "Running install stub ( %s )", szCmdLine);
|
|
#endif
|
|
IndicateProgress(pszDescription, TRUE);
|
|
ShellExecuteRegApp(szCmdLine, RRA_WAIT | RRA_NOUI);
|
|
if (hkeyUser == NULL) {
|
|
RegCreateKey(hkcuList, pszKeyName, &hkeyUser);
|
|
}
|
|
if (hkeyUser != NULL) {
|
|
if (fSetRevision) {
|
|
RegSetValueEx(hkeyUser, c_szRegInstallSequenceValue,
|
|
0, REG_SZ,
|
|
(LPBYTE)szRevision,
|
|
(lstrlen(szRevision)+1)*sizeof(TCHAR));
|
|
}
|
|
if (szLocale[0]) {
|
|
RegSetValueEx(hkeyUser, c_szRegLocaleValue,
|
|
0, REG_SZ,
|
|
(LPBYTE)szLocale,
|
|
(lstrlen(szLocale)+1)*sizeof(TCHAR));
|
|
}
|
|
if (fIsCloneUser) {
|
|
RegSetValueEx(hkeyUser, TEXT("Username"),
|
|
0, REG_SZ,
|
|
(LPBYTE)pszCurrentUsername,
|
|
(lstrlen(pszCurrentUsername)+1)*sizeof(TCHAR));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// decide if this belong to the next pass
|
|
// if it is in Pass 2, should never get here
|
|
if ( iPass == 0 )
|
|
bNextPassNeeded = TRUE;
|
|
else if ( (iPass == 1 ) && (*pszKeyName == '>') )
|
|
bNextPassNeeded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Component is an uninstall stub. */
|
|
|
|
err = RegOpenKeyEx(hkcuList, pszKeyName, 0,
|
|
KEY_QUERY_VALUE, &hkeyUser);
|
|
if (err == ERROR_SUCCESS) {
|
|
DWORD fDontAsk = 0;
|
|
|
|
/* Check the "Don't Ask" value. If it's present, its value
|
|
* is interpreted as follows:
|
|
*
|
|
* 0 --> ask the user
|
|
* 1 --> do not run the stub
|
|
* 2 --> always run the stub
|
|
*/
|
|
cbData = sizeof(fDontAsk);
|
|
if (RegQueryValueEx(hkeyComponent, c_szRegDontAskValue,
|
|
NULL, &dwType, (LPBYTE)&fDontAsk,
|
|
&cbData) != ERROR_SUCCESS ||
|
|
(dwType != REG_DWORD && (dwType != REG_BINARY || cbData != sizeof(DWORD))) ||
|
|
fDontAsk != 1)
|
|
{
|
|
|
|
if ( (iPass == -1 ) ||
|
|
((iPass == 0) && (*pszKeyName == '>')) ||
|
|
((iPass == 1) && (*pszKeyName != '<') && (*pszKeyName != '>')) ||
|
|
((iPass == 2) && (*pszKeyName == '<')) )
|
|
{
|
|
// uninstall stub has the reversed order comparing with install stub
|
|
if (fDontAsk == 2 || ConfirmUninstall(pszDescription) == IDYES) {
|
|
|
|
#ifdef TraceMsg
|
|
TraceMsg(TF_WARNING, "Running uninstall stub ( %s )", szCmdLine);
|
|
#endif
|
|
IndicateProgress(pszDescription, FALSE);
|
|
ShellExecuteRegApp(szCmdLine, RRA_WAIT | RRA_NOUI);
|
|
|
|
/* Component has been uninstalled. Forget that the
|
|
* user ever had it installed.
|
|
*/
|
|
RegCloseKey(hkeyUser);
|
|
hkeyUser = NULL;
|
|
RegDeleteKey(hkcuList, pszKeyName);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// decide if this belong to the next pass
|
|
// if it is in Pass 2, should never get here
|
|
if ( iPass == 0 )
|
|
bNextPassNeeded = TRUE;
|
|
else if ( (iPass == 1 ) && (*pszKeyName == '<') )
|
|
bNextPassNeeded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hkeyUser != NULL) {
|
|
RegCloseKey(hkeyUser);
|
|
}
|
|
RegCloseKey(hkeyComponent);
|
|
}
|
|
|
|
return bNextPassNeeded;
|
|
}
|
|
|
|
|
|
const TCHAR c_szIE40GUID_STUB[] = TEXT("{89820200-ECBD-11cf-8B85-00AA005B4383}");
|
|
const TCHAR c_szBlockIE4Stub[] = TEXT("NoIE4StubProcessing");
|
|
|
|
extern "C" void RunInstallUninstallStubs2(LPCTSTR pszStubToRun)
|
|
{
|
|
HKEY hklmList = NULL, hkcuList = NULL;
|
|
LONG err;
|
|
|
|
TCHAR szUsername[128+1]; /* 128 is the win95 limit, good default */
|
|
LPTSTR pszCurrentUser = szUsername;
|
|
|
|
/* As far as clone-user install stubs are concerned, we only want profile
|
|
* usernames.
|
|
*/
|
|
if (!ProfilesEnabled()) {
|
|
*pszCurrentUser = '\0';
|
|
}
|
|
else {
|
|
DWORD cbData = sizeof(szUsername);
|
|
if (!GetUserName(szUsername, &cbData)) {
|
|
if (cbData > sizeof(szUsername)) {
|
|
cbData++; /* allow for null char just in case */
|
|
pszCurrentUser = (LPTSTR)LocalAlloc(LPTR, cbData+1);
|
|
if (pszCurrentUser == NULL || !GetUserName(pszCurrentUser, &cbData)) {
|
|
if (pszCurrentUser != NULL)
|
|
LocalFree(pszCurrentUser);
|
|
pszCurrentUser = szUsername;
|
|
*pszCurrentUser = '\0';
|
|
}
|
|
}
|
|
else {
|
|
szUsername[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TraceMsg
|
|
TraceMsg(TF_WARNING, "Running install/uninstall stubs.");
|
|
#endif
|
|
|
|
err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegInstalledComponentsKey, 0,
|
|
KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hklmList);
|
|
|
|
if (err == ERROR_SUCCESS) {
|
|
DWORD dwDisp;
|
|
err = RegCreateKeyEx(HKEY_CURRENT_USER, c_szRegInstalledComponentsKey, 0,
|
|
TEXT(""), REG_OPTION_NON_VOLATILE,
|
|
KEY_READ | KEY_WRITE, NULL, &hkcuList, &dwDisp);
|
|
}
|
|
|
|
if (err == ERROR_SUCCESS) {
|
|
if (pszStubToRun != NULL) {
|
|
// here we call with pass number -1 means no pass order enforced
|
|
RunOneInstallStub(hklmList, hkcuList, pszStubToRun, pszCurrentUser, -1);
|
|
}
|
|
else {
|
|
DWORD cbKeyName, iKey, iPass;
|
|
TCHAR szKeyName[80];
|
|
BOOL bNextPassNeeded = TRUE;
|
|
HANDLE hMutex;
|
|
|
|
// This mutex check is to ensure if explore restarted due abnormal active desktop shutdown, and setup resume
|
|
// per-user stubs should not be processed till setup is done.
|
|
if (CheckWebViewShell())
|
|
{
|
|
hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, TEXT("Ie4Setup.Mutext"));
|
|
if (hMutex)
|
|
{
|
|
CloseHandle(hMutex);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// check if we want to block the stub processing
|
|
cbKeyName = sizeof(szKeyName);
|
|
if ((RegQueryValueEx(hklmList, c_szBlockIE4Stub, NULL, NULL,
|
|
(LPBYTE)szKeyName, &cbKeyName) == ERROR_SUCCESS) &&
|
|
(*CharUpper(szKeyName) == 'Y') )
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
/* we will do TWO passes to meet the ordering requirement when run component stubs.
|
|
Any KeyName with '*' as the first char, get run in the 1st Pass. the rest run 2nd pass */
|
|
for ( iPass = 0; ((iPass<3) && bNextPassNeeded); iPass++ )
|
|
{
|
|
bNextPassNeeded = FALSE;
|
|
|
|
// APPCOMPAT: in 2nd pass, we do want to special case of IE4.0 base browser stub
|
|
// to run first. The reason we did not use '<' for this is to 1) reserve < stuff
|
|
// for pre-ie4 stubs and this whole thing should redo in sorted fashion. For now,
|
|
// we hard code this IE4.0 base browser GUID
|
|
if ( iPass == 1 )
|
|
{
|
|
if ( RunOneInstallStub(hklmList, hkcuList, c_szIE40GUID_STUB, pszCurrentUser, iPass) )
|
|
bNextPassNeeded = TRUE;
|
|
}
|
|
|
|
/* Enumerate components that are installed on the local machine. */
|
|
for (iKey = 0; ; iKey++)
|
|
{
|
|
LONG lEnum;
|
|
|
|
cbKeyName = ARRAYSIZE(szKeyName);
|
|
|
|
// WARNING (Unicode, Davepl) I'm assuming that the data is UNICODE,
|
|
// but I'm not sure who put it there yet... double check.
|
|
|
|
if ((lEnum = RegEnumKey(hklmList, iKey, szKeyName, cbKeyName)) == ERROR_MORE_DATA)
|
|
{
|
|
// ERROR_MORE_DATA means the value name or data was too large
|
|
// skip to the next item
|
|
#ifdef TraceMsg
|
|
TraceMsg( DM_ERROR, "Cannot run oversize entry in InstalledComponents");
|
|
#endif
|
|
continue;
|
|
}
|
|
else if( lEnum != ERROR_SUCCESS )
|
|
{
|
|
// could be ERROR_NO_MORE_ENTRIES, or some kind of failure
|
|
// we can't recover from any other registry problem, anyway
|
|
break;
|
|
}
|
|
|
|
// in case the user say NO when we try to run the IE4 stub first time,
|
|
// we should not re-process this stub again.
|
|
if ( (iPass == 1) && (!lstrcmpi(szKeyName, c_szIE40GUID_STUB)) )
|
|
continue;
|
|
|
|
if ( RunOneInstallStub(hklmList, hkcuList, szKeyName, pszCurrentUser, iPass) )
|
|
bNextPassNeeded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
if (hklmList != NULL)
|
|
RegCloseKey(hklmList);
|
|
if (hkcuList != NULL)
|
|
RegCloseKey(hkcuList);
|
|
|
|
if (pszCurrentUser != szUsername)
|
|
LocalFree(pszCurrentUser);
|
|
|
|
CleanupProgressDialog();
|
|
}
|
|
|
|
// Check shell32.dll's version and see if it is the one which supports the integrated WebView
|
|
BOOL CheckWebViewShell()
|
|
{
|
|
HINSTANCE hInstShell32;
|
|
DLLGETVERSIONPROC fpGetDllVersion;
|
|
BOOL pWebViewShell = FALSE;
|
|
|
|
hInstShell32 = LoadLibrary(TEXT("Shell32.dll"));
|
|
if (hInstShell32)
|
|
{
|
|
fpGetDllVersion = (DLLGETVERSIONPROC)GetProcAddress(hInstShell32, "DllGetVersion");
|
|
pWebViewShell = (fpGetDllVersion != NULL);
|
|
FreeLibrary(hInstShell32);
|
|
}
|
|
return pWebViewShell;
|
|
}
|