windows-nt/Source/XPSP1/NT/shell/inc/inststub.h
2020-09-26 16:20:57 +08:00

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;
}