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