#include "stdafx.h"
#include "theapp.h"
#include "netconn.h"
#include "netapi.h"
#include "prnutil.h"
#include "install.h"
#include "mydocs.h"
#include "comctlwrap.h"
#include "icsinst.h"
#include "defconn.h"
#include "initguid.h"
DEFINE_GUID(CLSID_FolderItem, 0xfef10fa2, 0x355e, 0x4e06, 0x93, 0x81, 0x9b, 0x24, 0xd7, 0xf7, 0xcc, 0x88);
DEFINE_GUID(CLSID_SharedAccessConnectionManager,            0xBA126AE0,0x2166,0x11D1,0xB1,0xD0,0x00,0x80,0x5F,0xC1,0x27,0x0E); // this doesn't exist in the shell tree

#include "resource.h"
#include "newapi.h"
#include "shgina.h"

#include "hnetcfg.h"
#include "netconp.h"
#include "hnetbcon.h" // ICSLapCtl.h
#include "Lm.h"
#include "htmlhelp.h"

#include "nla.h"
#include "netinet.h"
#include "netip.h"
#include "netras.h"
#include "netutil.h"

// include files necessary for showing diagrams using ShowHTMLDialog. TinQian
#include <urlmon.h>
#include <mshtmhst.h>
#include <mshtml.h>
#include <atlbase.h>

// Debugging #defines:

// Uncomment NO_CHECK_DOMAIN when you want to test the wizard on a domain machine - just don't apply changes ;)
// #define NO_CHECK_DOMAIN

// Uncomment NO_CONFIG to neuter the wizard for UI-only testing
// #define NO_CONFIG

// Uncomment FAKE_ICS to simulate the "ICS machine" state
// #define FAKE_ICS

// Uncomment FAKE_UNPLUGGED to simulate unplugged connections
// #define FAKE_UNPLUGGED

// Uncomment FAKE_REBOOTREQUIRED to simulate reboot required
// #define FAKE_REBOOTREQUIRED

// Delay Load ole32.dll function CoSetProxyBlanket since it isn't on W95 Gold
// and it's only used by the wizard on NT

#define CoSetProxyBlanket CoSetProxyBlanket_NT

EXTERN_C STDAPI CoSetProxyBlanket_NT(IUnknown* pProxy, DWORD dwAuthnSvc, DWORD dwAuthzSvc, OLECHAR* pServerPrincName, DWORD dwAuthnLevel, DWORD dwImpLevel, RPC_AUTH_IDENTITY_HANDLE pAuthInfo, DWORD dwCapabilities);

// Functions not in any include file yet
extern int AdapterIndexFromClass(LPTSTR szClass, BOOL bSkipClass);

#define LWS_IGNORERETURN 0x0002

#define MAX_HNW_PAGES 30

#define MAX_WORKGROUPS  20

#define CONN_EXTERNAL     0x00000001
#define CONN_INTERNAL     0x00000002
#define CONN_UNPLUGGED    0x00000004

// Return value to use for sharing configuration conflict

#define HNETERRORSTART          0x200
#define E_ANOTHERADAPTERSHARED  MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, HNETERRORSTART+1)
#define E_ICSADDRESSCONFLICT    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, HNETERRORSTART+2)

class CEnsureSingleInstance
{
public:
    CEnsureSingleInstance(LPCTSTR szCaption);
    ~CEnsureSingleInstance();

    BOOL ShouldExit() { return m_fShouldExit;}

private:
    BOOL m_fShouldExit;
    HANDLE m_hEvent;
};

CEnsureSingleInstance::CEnsureSingleInstance(LPCTSTR szCaption)
{
    // Create an event
    m_hEvent = CreateEvent(NULL, TRUE, FALSE, szCaption);

    // If any weird errors occur, default to running the instance
    m_fShouldExit = FALSE;

    if (NULL != m_hEvent)
    {
        // If our event isn't signaled, we're the first instance
        m_fShouldExit = (WAIT_OBJECT_0 == WaitForSingleObject(m_hEvent, 0));

        if (m_fShouldExit)
        {
            // app should exit after calling ShouldExit()

            // Find and show the caption'd window
            HWND hwndActivate = FindWindow(NULL, szCaption);
            if (IsWindow(hwndActivate))
            {
                SetForegroundWindow(hwndActivate);
            }
        }
        else
        {
            // Signal that event
            SetEvent(m_hEvent);
        }
    }
}

CEnsureSingleInstance::~CEnsureSingleInstance()
{
    if (NULL != m_hEvent)
    {
        CloseHandle(m_hEvent);
    }
}

typedef struct _tagHOMENETSETUPINFO
{
    HWND hwnd;

    DWORD cbSize;
    DWORD dwFlags;

    // Data for NT - NetConnections are temporarily represented by their corresponding GUIDs to cross the
    // thread boundary for asynchronous configuration.
    BOOL  fAsync;
    GUID  guidExternal;
    GUID* prgguidInternal;
    DWORD cguidInternal;
    UINT  umsgAsyncNotify;

    INetConnection* pncExternal;
    INetConnection** prgncInternal;
    DWORD cncInternal;

    // Data for Win9x
    const NETADAPTER*   pNA;    // list of adapters.
    UINT                cNA;    // count of entries in pNA.
    RASENTRYNAME*       pRas;   // list of RAS connectoids.
    UINT                cRas;   // count of RAS connectoids.
    UINT                ipaExternal;
    UINT                ipaInternal;

    // Data for both NT and Win9x
    TCHAR szComputer[CNLEN + 1];
    TCHAR szComputerDescription[256];
    TCHAR szWorkgroup[LM20_DNLEN + 1];

    // Out-data
    BOOL        fRebootRequired;
} HOMENETSETUPINFO, *PHOMENETSETUPINFO;

// Function prototypes
void HelpCenter(HWND hwnd, LPCWSTR pszTopic);
void BoldControl(HWND hwnd, int id);
void ShowControls(HWND hwndParent, const int *prgControlIDs, DWORD nControls, int nCmdShow);
HRESULT ConfigureHomeNetwork(PHOMENETSETUPINFO pInfo);
DWORD WINAPI ConfigureHomeNetworkThread(void* pData);
HRESULT ConfigureHomeNetworkSynchronous(PHOMENETSETUPINFO pInfo);
HRESULT ConfigureICSBridgeFirewall(PHOMENETSETUPINFO pInfo);
HRESULT EnableSimpleSharing();
HRESULT GetConnections(HDPA* phdpa);
int FreeConnectionDPACallback(LPVOID pFreeMe, LPVOID pData);
HRESULT GetConnectionsFolder(IShellFolder** ppsfConnections);
//HRESULT GetConnectionIconIndex(GUID& guidConnection, IShellFolder* psfConnections, int* pIndex, HIMAGELIST imgList);
HRESULT GetDriveNameAndIconIndex(LPWSTR pszDrive, LPWSTR pszDisplayName, DWORD cchDisplayName, int* pIndex);
void    W9xGetNetTypeName(BYTE bNicType, WCHAR* pszBuff, UINT cchBuff);
BOOL    W9xIsValidAdapter(const NETADAPTER* pNA, DWORD dwFlags);
BOOL    W9xIsAdapterDialUp(const NETADAPTER* pAdapter);
BOOL    IsEqualConnection(INetConnection* pnc1, INetConnection* pnc2);
void GetTitleFont(LPTSTR pszFaceName, DWORD cch);
LONG GetTitlePointSize(void);
BOOL FormatMessageString(UINT idTemplate, LPTSTR pszStrOut, DWORD cchSize, ...);
int DisplayFormatMessage(HWND hwnd, UINT idCaption, UINT idFormatString, UINT uType, ...);
HRESULT SetProxyBlanket(IUnknown * pUnk);
HRESULT MakeUniqueShareName(LPCTSTR pszBaseName, LPTSTR pszUniqueName, DWORD cchName);
HRESULT WriteSetupInfoToRegistry(PHOMENETSETUPINFO pInfo);
HRESULT ReadSetupInfoFromRegistry(PHOMENETSETUPINFO pInfo);
HRESULT ShareWellKnownFolders(PHOMENETSETUPINFO pInfo);
HRESULT ShareAllPrinters();
HRESULT DeleteSetupInfoFromRegistry();
HRESULT IsUserLocalAdmin(HANDLE TokenHandle, BOOL* pfIsAdmin);
BOOL AllPlatformGetComputerName(LPWSTR pszName, DWORD cchName);
BOOL AllPlatformSetComputerName(LPCWSTR pszName);
void FreeInternalConnections(PHOMENETSETUPINFO pInfo);
void FreeInternalGUIDs(PHOMENETSETUPINFO pInfo);
void FreeExternalConnection(PHOMENETSETUPINFO pInfo);
HRESULT GetConnectionByGUID(HDPA hdpaConnections, const GUID* pguid, INetConnection** ppnc);
HRESULT GUIDsToConnections(PHOMENETSETUPINFO pInfo);
HRESULT GetConnectionGUID(INetConnection* pnc, GUID* pguid);
HRESULT ConnectionsToGUIDs(PHOMENETSETUPINFO pInfo);
BOOL  IsValidNameSyntax(LPCWSTR pszName, NETSETUP_NAME_TYPE type);
void  STDMETHODCALLTYPE ConfigurationLogCallback(LPCWSTR pszLogEntry, LPARAM lParam);

// Home network wizard class
class CHomeNetworkWizard : public IHomeNetworkWizard
{
    friend HRESULT CHomeNetworkWizard_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);

public:
    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObj);
    STDMETHOD_(ULONG, AddRef) ();
    STDMETHOD_(ULONG, Release) ();

    // IHomeNetworkWizard
    STDMETHOD(ConfigureSilently)(LPCWSTR pszPublicConnection, DWORD hnetFlags, BOOL* pfRebootRequired);
    STDMETHOD(ShowWizard)(HWND hwnd, BOOL* pfRebootRequired);

protected:
    CHomeNetworkWizard();
    HRESULT Initialize();
    HRESULT Uninitialize();

private:
    // Shared functions
    void DestroyConnectionList(HWND hwndList);
    void InitializeConnectionList(HWND hwndList, DWORD dwFlags);
    void FillConnectionList(HWND hwndList, INetConnection* pncExcludeFromList, DWORD dwFlags);
    BOOL ShouldShowConnection(INetConnection* pnc, INetConnection* pncExcludeFromList, DWORD dwFlags);
    BOOL IsConnectionICSPublic(INetConnection* pnc);
    BOOL IsConnectionUnplugged(INetConnection* pnc);
    BOOL W9xAddAdapterToList(const NETADAPTER* pNA, const WCHAR* pszDesc, UINT uiAdapterIndex, UINT uiDialupIndex, HWND hwndList, DWORD dwFlags);
    UINT W9xEnumRasEntries(void);
    HRESULT GetConnectionByName(LPCWSTR pszName, INetConnection** ppncOut);
    HRESULT GetInternalConnectionArray(INetConnection* pncExclude, INetConnection*** pprgncArray, DWORD* pcncArray);
    DWORD GetConnectionCount(INetConnection* pncExclude, DWORD dwFlags);
    void ReplaceStaticWithLink(HWND hwndStatic, UINT idcLinkControl, UINT idsLinkText);
    BOOL IsMachineOnDomain();
    BOOL IsMachineWrongOS();
    BOOL IsICSIPInUse( WCHAR** ppszHost, PDWORD pdwSize );

    // Per-Page functions
    // Welcome
    static INT_PTR CALLBACK WelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void WelcomeSetTitleFont(HWND hwnd);

    // No home network hardware
    static INT_PTR CALLBACK NoHardwareWelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Page with instructions for manual configuration of the network
    void ManualRefreshConnectionList();
    static INT_PTR CALLBACK ManualConfigPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // User has some network hardware unplugged
    BOOL UnpluggedFillList(HWND hwnd);
    static INT_PTR CALLBACK UnpluggedPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Found ICS (Internet connection sharing)
    static INT_PTR CALLBACK FoundIcsPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void FoundIcsSetText(HWND hwnd);
    BOOL GetICSMachine(LPTSTR pszICSMachineName, DWORD cch);

    // Connect
    static INT_PTR CALLBACK ConnectPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void ConnectSetDefault(HWND hwnd);
    void ConnectNextPage(HWND hwnd);

    // Show Me Links
    void ShowMeLink(HWND hwnd, LPCWSTR pszTopic);

    // Connect other (alternative methods for connection)
    static INT_PTR CALLBACK ConnectOtherPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Public
    static INT_PTR CALLBACK PublicPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void PublicSetActive(HWND hwnd);
    void PublicSetControlState(HWND hwnd);
    void PublicNextPage(HWND hwnd);
    void PublicGetControlPositions(HWND hwnd);
    void PublicResetControlPositions(HWND hwnd);
    void PublicMoveControls(HWND hwnd, BOOL fItemPreselected);

    // File sharing
    static INT_PTR CALLBACK EdgelessPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void EdgelessSetActive(HWND hwnd);

    // ICS conflict
    void ICSConflictSetActive(HWND hwnd);
    static INT_PTR CALLBACK ICSConflictPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // BridgeWarning (do you want to manually configure the bridge? Are you crazy !?)
    static INT_PTR CALLBACK BridgeWarningPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Private
    static INT_PTR CALLBACK PrivatePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void PrivateNextPage(HWND hwnd);
    void PrivateSetControlState(HWND hwnd);

    // Name (computer and workgroup)
    static INT_PTR CALLBACK NamePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void NameInitDialog(HWND hwnd);
    void NameSetControlState(HWND hwnd);
    HRESULT NameNextPage(HWND hwnd);

    // Workgroup name
    void WorkgroupSetControlState(HWND hwnd);
    HRESULT WorkgroupNextPage(HWND hwnd);
    static INT_PTR CALLBACK WorkgroupPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Summary
    static INT_PTR CALLBACK SummaryPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void SummarySetActive(HWND hwnd);

    // Progress (while the configuration is taking place)
    static INT_PTR CALLBACK ProgressPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Almost done (after configuration but before the "completed" page)
    static INT_PTR CALLBACK AlmostDonePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Choose disk drive
    void FillDriveList(HWND hwndList);
    void ChooseDiskSetControlState(HWND hwnd);
    static INT_PTR CALLBACK ChooseDiskPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Insert disk
    static HRESULT GetSourceFilePath(LPSTR pszSource, DWORD cch);
    static INT_PTR CALLBACK InsertDiskPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Floppy and XP CD "run the wizard" instructions
    static INT_PTR CALLBACK InstructionsPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Finish
    static INT_PTR CALLBACK FinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Error finish
    static INT_PTR CALLBACK ErrorFinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // No Hardware Finish
    // An alternate finish if they have a LAN card ONLY, and its used for an INet connection,
    // so therefore they have no LAN cards connecting to other computers...
    static INT_PTR CALLBACK NoHardwareFinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Can't Run Wizard Pages
    // An alternate welcome page when the user isn't an adminm or doesn't have permissions,
    static INT_PTR CALLBACK CantRunWizardPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    static CHomeNetworkWizard* GetThis(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    // Get Icon Index for network connections based on GUIDs
    HRESULT GetConnectionIconIndex(GUID& guidConnection, IShellFolder* psfConnections, int* pIndex);

    // Helpers
    UINT PopPage()
    {
        ASSERT(_iPageStackTop);
        return _rguiPageStack[--_iPageStackTop];
    }

    void PushPage(UINT uiPageId)
    {
        ASSERT(_iPageStackTop < MAX_HNW_PAGES);
        _rguiPageStack[_iPageStackTop++] = uiPageId;
    }

    // Data
    HDPA                _hdpaConnections;
    HOMENETSETUPINFO    _hnetInfo;

    // even though CNLEN+1 is the limit of a name buffer, friendly names can be longer
    TCHAR               _szICSMachineName[MAX_PATH];   // Machine on the network doing ICS, if applicable

    UINT                _rguiPageStack[MAX_HNW_PAGES];  // Stackopages
    int                 _iPageStackTop;                 // Current top of the stack

    BOOL                _fManualBridgeConfig;           // The user wants to manuall configure the bridge
    BOOL                _fICSClient;// This computer will connect through an ICS machine or another sharing device

    // Shell image lists - never free these
    HIMAGELIST          _himlSmall;
    HIMAGELIST          _himlLarge;

    LONG                _cRef;

    BOOL                _fShowPublicPage;
    BOOL                _fShowSharingPage;
    BOOL                _fNoICSQuestion;
    BOOL                _fNoHomeNetwork;
    BOOL                _fExternalOnly;

    UINT                _iDrive;        // Ordinal of removable drive for floppy creation
    WCHAR               _szDrive[256];  // Name of removable drive
    BOOL                _fCancelCopy;
    BOOL                _fFloppyInstructions; // Show the floppy, as opposed to CD, instructions

    // data structure used by show me links
    HINSTANCE hinstMSHTML;
    SHOWHTMLDIALOGEXFN * pfnShowHTMLDialog;
    IHTMLWindow2 * showMeDlgWnd, * pFrameWindow;

    // Network Connection folder and folder view call back.  Used by Connection List Views.
    IShellFolder *_psfConnections;
    IShellFolderViewCB *_pConnViewCB;

    struct PUBLICCONTROLPOSITIONS
    {
        RECT _rcSelectMessage;
        RECT _rcListLabel;
        RECT _rcList;
        RECT _rcHelpIcon;
        RECT _rcHelpText;
    } PublicControlPositions;
};

int FreeConnectionDPACallback(LPVOID pFreeMe, LPVOID pData)
{
    ((INetConnection*) pFreeMe)->Release();
    return 1;
}

void InitHnetInfo(HOMENETSETUPINFO* pInfo)
{
    ZeroMemory(pInfo, sizeof (HOMENETSETUPINFO));
    pInfo->cbSize = sizeof (HOMENETSETUPINFO);
    pInfo->ipaExternal = -1;
    pInfo->ipaInternal = -1;
}


// Creation function
HRESULT HomeNetworkWizard_RunFromRegistry(HWND hwnd, BOOL* pfRebootRequired)
{
    HOMENETSETUPINFO setupInfo;

    InitHnetInfo(&setupInfo);

    HRESULT hr = ReadSetupInfoFromRegistry(&setupInfo);

    if (S_OK == hr)
    {
        setupInfo.dwFlags = HNET_SHAREPRINTERS | HNET_SHAREFOLDERS;
        setupInfo.hwnd = hwnd;
        hr = ConfigureHomeNetwork(&setupInfo);
        *pfRebootRequired = setupInfo.fRebootRequired;
    }

    DeleteSetupInfoFromRegistry();

    return hr;
}

HRESULT HomeNetworkWizard_ShowWizard(HWND hwnd, BOOL* pfRebootRequired)
{
    if (*pfRebootRequired)
        *pfRebootRequired = FALSE;

    HRESULT hr = HomeNetworkWizard_RunFromRegistry(hwnd, pfRebootRequired);

    if (S_FALSE == hr)
    {
        IUnknown* punk;
        hr = CHomeNetworkWizard_CreateInstance(NULL, &punk, NULL);

        if (SUCCEEDED(hr))
        {
            IHomeNetworkWizard* pwizard;
            hr = punk->QueryInterface(IID_PPV_ARG(IHomeNetworkWizard, &pwizard));

            if (SUCCEEDED(hr))
            {
                hr = pwizard->ShowWizard(hwnd, pfRebootRequired);
                pwizard->Release();
            }

            punk->Release();
        }
    }

    return hr;
}

HRESULT CHomeNetworkWizard_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    *ppunk = NULL;

    if (punkOuter)
        return CLASS_E_NOAGGREGATION;

    CHomeNetworkWizard* pwiz = new CHomeNetworkWizard();
    if (!pwiz)
        return E_OUTOFMEMORY;

    HRESULT hr = pwiz->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
    pwiz->Release();
    return hr;
}

// IUnknown
HRESULT CHomeNetworkWizard::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IHomeNetworkWizard))
    {
        AddRef();
        *ppvObj = static_cast<IHomeNetworkWizard *>(this);
    }
    else
    {
        *ppvObj = NULL;
    }

    return *ppvObj ? S_OK : E_NOINTERFACE;
}

ULONG CHomeNetworkWizard::AddRef()
{
    return (ULONG) InterlockedIncrement(&_cRef);
}

ULONG CHomeNetworkWizard::Release()
{
    InterlockedDecrement(&_cRef);

    if (_cRef == 0)
    {
        delete this;
        return 0;
    }

    return (ULONG) _cRef;
}


HRESULT CHomeNetworkWizard::GetConnectionByName(LPCWSTR pszName, INetConnection** ppncOut)
{
    *ppncOut = NULL;
    DWORD cItems = DPA_GetPtrCount(_hdpaConnections);
    DWORD iItem = 0;
    while ((iItem < cItems) && (NULL == *ppncOut))
    {
        INetConnection* pnc = (INetConnection*) DPA_GetPtr(_hdpaConnections, iItem);

        NETCON_PROPERTIES* pncprops;
        HRESULT hr = pnc->GetProperties(&pncprops);
        if (SUCCEEDED(hr))
        {
            if (0 == StrCmpIW(pszName, pncprops->pszwName))
            {
                *ppncOut = pnc;
                (*ppncOut)->AddRef();
            }

            NcFreeNetconProperties(pncprops);
        }

        iItem ++;
    }

    return (*ppncOut) ? S_OK : E_FAIL;
}

HRESULT CHomeNetworkWizard::GetInternalConnectionArray(INetConnection* pncExclude, INetConnection*** pprgncArray, DWORD* pcncArray)
{
    HRESULT hr = S_OK;
    *pprgncArray = NULL;

    DWORD cTotalConnections = DPA_GetPtrCount(_hdpaConnections);
    DWORD cInternalConnections = GetConnectionCount(pncExclude, CONN_INTERNAL);

    if (cInternalConnections)
    {
        (*pprgncArray) = (INetConnection**) LocalAlloc(LPTR, (cInternalConnections + 1) * sizeof (INetConnection*));
        // Note that we allocated an extra entry since this is a null-terminated array
        if (*pprgncArray)
        {
            DWORD nInternalConnection = 0;
            for (DWORD n = 0; n < cTotalConnections; n++)
            {
                INetConnection* pnc = (INetConnection*) DPA_GetPtr(_hdpaConnections, n);
                if (ShouldShowConnection(pnc, pncExclude, CONN_INTERNAL))
                {
                    pnc->AddRef();
                    (*pprgncArray)[nInternalConnection++] = pnc;
                }
            }

            ASSERT(nInternalConnection == cInternalConnections);
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        *pcncArray = cInternalConnections;
    }

    return hr;
}

HRESULT CHomeNetworkWizard::ConfigureSilently(LPCWSTR pszPublicConnection, DWORD hnetFlags, BOOL* pfRebootRequired)
{
    // Never set workgroup name
    hnetFlags &= (~HNET_SETWORKGROUPNAME);
    
    if (!g_fRunningOnNT)
        return E_NOTIMPL;

    HRESULT hr = Initialize();

    if (SUCCEEDED(hr))
    {
        _hnetInfo.dwFlags = hnetFlags;
        // Calculate what the external and internal adapters will be...
        if (pszPublicConnection)
        {
            hr = GetConnectionByName(pszPublicConnection, &_hnetInfo.pncExternal);
        }
        else
        {
            _hnetInfo.pncExternal = NULL;
        }

        if (SUCCEEDED(hr))
        {
            // Get all LAN connections except the public connection
            if (_hnetInfo.dwFlags & HNET_BRIDGEPRIVATE)
            {
                hr = GetInternalConnectionArray(_hnetInfo.pncExternal, &(_hnetInfo.prgncInternal), &_hnetInfo.cncInternal);
            }

            if (SUCCEEDED(hr))
            {
                hr = ConfigureHomeNetwork(&_hnetInfo);
                *pfRebootRequired = _hnetInfo.fRebootRequired;
            }
        }

        Uninitialize();
    }

    return hr;
}


CHomeNetworkWizard::CHomeNetworkWizard() :
    _cRef(1)
{}

HRESULT CHomeNetworkWizard::Initialize()
{
    _fExternalOnly       = FALSE;
    _fNoICSQuestion      = FALSE;
    _hdpaConnections     = NULL;
    _iPageStackTop       = 0;
    _fManualBridgeConfig = FALSE;
    _fICSClient          = FALSE;
    _psfConnections      = NULL;
    _pConnViewCB         = NULL;

    InitHnetInfo(&_hnetInfo);
    *_szICSMachineName   = 0;

    HRESULT hr;
    
    if (g_fRunningOnNT)
    {
        hr = GetConnections(&_hdpaConnections);
    }
    else
    {
        _hnetInfo.cNA = EnumCachedNetAdapters(&_hnetInfo.pNA);
        hr            = S_OK;
        
        if ( _hnetInfo.cNA > 0 )
        {
            _hnetInfo.ipaInternal = 0;
        }
    }

    // Get the shell image lists - never free these
    if (!Shell_GetImageLists(&_himlLarge, &_himlSmall))
    {
        hr = E_FAIL;
    }

    // variables used by displaying show me links
    hinstMSHTML = NULL;
    pfnShowHTMLDialog = NULL;
    showMeDlgWnd = NULL; 
    pFrameWindow = NULL;

    return hr;
}

HRESULT CHomeNetworkWizard::Uninitialize()
{
    if (g_fRunningOnNT)
    {
        if (_hdpaConnections)
        {
            DPA_DestroyCallback(_hdpaConnections, FreeConnectionDPACallback, NULL);
            _hdpaConnections = NULL;
        }

        // Free public lan information
        FreeExternalConnection(&_hnetInfo);

        // Free private lan information
        FreeInternalConnections(&_hnetInfo);
    
        if (_psfConnections != NULL)
            _psfConnections->Release();

        if (_pConnViewCB != NULL)
            _pConnViewCB->Release();
    }
    else
    {
        if (_hnetInfo.pNA)
        {
            FlushNetAdapterCache();
            _hnetInfo.pNA = NULL;
        }

        if (_hnetInfo.pRas)
        {
            LocalFree(_hnetInfo.pRas);
            _hnetInfo.pRas = NULL;
        }
    }

    // release resources used by show me links
    if (hinstMSHTML)
        FreeLibrary(hinstMSHTML);

    if (showMeDlgWnd != NULL)
        showMeDlgWnd->Release();
    
    if (pFrameWindow != NULL)
        pFrameWindow->Release();

    return S_OK;
}

// TODO: Move the formatting functions here to a util file or something...

BOOL FormatMessageString(UINT idTemplate, LPTSTR pszStrOut, DWORD cchSize, ...)
{
    BOOL fResult = FALSE;

    va_list vaParamList;

    TCHAR szFormat[1024];
    if (LoadString(g_hinst, idTemplate, szFormat, ARRAYSIZE(szFormat)))
    {
        va_start(vaParamList, cchSize);

        fResult = FormatMessage(FORMAT_MESSAGE_FROM_STRING, szFormat, 0, 0, pszStrOut, cchSize, &vaParamList);

        va_end(vaParamList);
    }

    return fResult;
}

int DisplayFormatMessage(HWND hwnd, UINT idCaption, UINT idFormatString, UINT uType, ...)
{
    int iResult = IDCANCEL;
    TCHAR szError[512]; *szError = 0;
    TCHAR szCaption[256];
    TCHAR szFormat[512]; *szFormat = 0;

    // Load and format the error body
    if (LoadString(g_hinst, idFormatString, szFormat, ARRAYSIZE(szFormat)))
    {
        va_list arguments;
        va_start(arguments, uType);

        if (FormatMessage(FORMAT_MESSAGE_FROM_STRING, szFormat, 0, 0, szError, ARRAYSIZE(szError), &arguments))
        {
            // Load the caption
            if (LoadString(g_hinst, idCaption, szCaption, ARRAYSIZE(szCaption)))
            {
                iResult = MessageBox(hwnd, szError, szCaption, uType);
            }
        }

        va_end(arguments);
    }
    return iResult;
}

BOOL CHomeNetworkWizard::GetICSMachine(LPTSTR pszICSMachineName, DWORD cch)
{
#ifdef FAKE_ICS
    lstrcpyn(pszICSMachineName, L"COMPNAME", cch);
    return TRUE;
#endif

    SetCursor(LoadCursor(NULL, IDC_WAIT));

    BOOL fICSInstalled = FALSE;

    HRESULT hr = S_OK;

    INetConnectionManager* pSharedAccessConnectionManager; 
    hr = CoCreateInstance(CLSID_SharedAccessConnectionManager, NULL, CLSCTX_LOCAL_SERVER, IID_INetConnectionManager, reinterpret_cast<void**>(&pSharedAccessConnectionManager));
    if(SUCCEEDED(hr))
    {
        IEnumNetConnection* pEnumerator;
        hr = pSharedAccessConnectionManager->EnumConnections(NCME_DEFAULT, &pEnumerator);
        if(SUCCEEDED(hr))
        {
            INetConnection* pNetConnection;
            ULONG ulFetched;
            hr = pEnumerator->Next(1, &pNetConnection, &ulFetched); // HNW only cares about >= 1 beacon
            if(SUCCEEDED(hr) && 1 == ulFetched)
            {
                fICSInstalled = TRUE; 

                // found the beacon, now recover the machine name if supported
                
                INetSharedAccessConnection* pNetSharedAccessConnection;
                hr = pNetConnection->QueryInterface(IID_INetSharedAccessConnection, reinterpret_cast<void**>(&pNetSharedAccessConnection));
                if(SUCCEEDED(hr))
                {
                    IUPnPService* pOSInfoService;
                    hr = pNetSharedAccessConnection->GetService(SAHOST_SERVICE_OSINFO, &pOSInfoService);
                    if(SUCCEEDED(hr))
                    {
                        
                        VARIANT Variant;
                        VariantInit(&Variant);
                        BSTR VariableName;
                        VariableName = SysAllocString(L"OSMachineName");
                        if(NULL != VariableName)
                        {
                            hr = pOSInfoService->QueryStateVariable(VariableName, &Variant);
                            if(SUCCEEDED(hr))
                            {
                                if(V_VT(&Variant) == VT_BSTR)
                                {
                                    lstrcpyn(pszICSMachineName, V_BSTR(&Variant), cch);
                                }
                                else
                                {
                                    hr = E_UNEXPECTED;
                                }
                                VariantClear(&Variant);
                            }
                            SysFreeString(VariableName);
                        }
                        else
                        {
                            hr = E_OUTOFMEMORY;
                        }
                        pOSInfoService->Release();
                    }
                    pNetSharedAccessConnection->Release();
                }

                if(FAILED(hr))
                {
                    if (!LoadString(g_hinst, IDS_UNIDENTIFIED_ICS_DEVICE, pszICSMachineName, cch))
                        *pszICSMachineName = TEXT('\0');
                }
                pNetConnection->Release();
            }
            pEnumerator->Release();
        }
        pSharedAccessConnectionManager->Release();
    }
    return fICSInstalled;
}

void CHomeNetworkWizard::InitializeConnectionList(HWND hwndList, DWORD dwFlags)
{
    // Set up the columns of the list
    LVCOLUMN lvc;
    lvc.mask = LVCF_SUBITEM | LVCF_WIDTH;

    lvc.iSubItem = 0;
    lvc.cx = 10;
    ListView_InsertColumn(hwndList, 0, &lvc);

    lvc.iSubItem = 1;
    lvc.cx = 10;
    ListView_InsertColumn(hwndList, 1, &lvc);

    DWORD dwStyles = LVS_EX_FULLROWSELECT;
    if (dwFlags & CONN_INTERNAL)
        dwStyles |= LVS_EX_CHECKBOXES;

    // Consider disabling the list or something for CONN_UNPLUGGED

    ListView_SetExtendedListViewStyleEx(hwndList, dwStyles, dwStyles);

    ListView_SetImageList(hwndList, _himlSmall, LVSIL_SMALL);
       
    _psfConnections = NULL;
    _pConnViewCB = NULL;

    if (g_fRunningOnNT)
    {
        HRESULT hr = GetConnectionsFolder(&_psfConnections);
    
        if (SUCCEEDED(hr))
        {
            IShellView *pConnView = NULL;
        
            hr = _psfConnections->CreateViewObject(hwndList, IID_IShellView, reinterpret_cast<LPVOID *>(&pConnView));
            if (SUCCEEDED(hr))
            {
    
                hr = _psfConnections->QueryInterface(IID_IShellFolderViewCB, reinterpret_cast<LPVOID *>(&_pConnViewCB));
                if (SUCCEEDED(hr))
                {
                    HWND hWndParent = GetParent(hwndList);
                    hr = _pConnViewCB->MessageSFVCB(SFVM_HWNDMAIN, NULL, reinterpret_cast<LPARAM>(hWndParent));
                }                
            }

            if (pConnView != NULL)
                pConnView->Release();
        }
    }
}

BOOL CHomeNetworkWizard::IsConnectionUnplugged(INetConnection* pnc)
{
    BOOL fUnplugged = FALSE;
    
    if ( g_fRunningOnNT )
    {
        HRESULT            hr;
        NETCON_PROPERTIES* pncprops;
        
        hr = pnc->GetProperties(&pncprops);
     
        if (SUCCEEDED(hr))
        {
            ASSERT(pncprops);
        
            fUnplugged = (NCS_MEDIA_DISCONNECTED == pncprops->Status);

            NcFreeNetconProperties(pncprops);
        }
    }

    return fUnplugged;
}

BOOL CHomeNetworkWizard::ShouldShowConnection(INetConnection* pnc, INetConnection* pncExcludeFromList, DWORD dwFlags)
{
    BOOL fShow = FALSE;

    if (!IsEqualConnection(pnc, pncExcludeFromList))
    {
        NETCON_PROPERTIES* pprops;
        HRESULT hr = pnc->GetProperties(&pprops);

        // Is this the kind of connection we want to show based on whether its external or internal list?
        if (SUCCEEDED(hr))
        {
            // Note: The bridge is a virtual and not a real connection. If it exists, it will have
            // NCM_BRIDGE, and so it won't get shown here, which is correct.
            if (dwFlags & CONN_EXTERNAL)
            {
                if ((pprops->MediaType == NCM_LAN) ||
                    (pprops->MediaType == NCM_PHONE) ||
                    (pprops->MediaType == NCM_TUNNEL) ||
                    (pprops->MediaType == NCM_ISDN) ||
                    (pprops->MediaType == NCM_PPPOE))
                {
                    fShow = TRUE;
                }
            }

            if (dwFlags & CONN_INTERNAL)
            {
                if (pprops->MediaType == NCM_LAN)
                {
                    // Note: In this case pncExcludeFromList is the shared adapter.  
                    // If this is a VPN(NCM_TUNNEL) connection then we want to make 
                    // sure its pszPrerequisiteEntry is connected.
                    
                    BOOL    fAssociated;
                    HRESULT hr;
                    
                    hr = HrConnectionAssociatedWithSharedConnection( pnc, pncExcludeFromList, &fAssociated );
                
                    if ( SUCCEEDED(hr) )
                    {
                        fShow = !fAssociated;
                    }
                    else
                    {
                        fShow = TRUE;
                    }
                }
            }

            if (dwFlags & CONN_UNPLUGGED)
            {
                if (IsConnectionUnplugged(pnc))
                {
                    fShow = TRUE;
                }
            }

            NcFreeNetconProperties(pprops);
        }
    }

    return fShow;
}

BOOL CHomeNetworkWizard::IsConnectionICSPublic(INetConnection* pnc)
{
    BOOL fShared = FALSE;

    NETCON_PROPERTIES* pprops;
    HRESULT hr = pnc->GetProperties(&pprops);

    // Is this the kind of connection we want to show based on whether its external or internal list?
    if (SUCCEEDED(hr))
    {
        // Note: Don't check pprops->MediaType == NCM_SHAREDACCESSHOST. SHAREDACCESSHOST is the connectoid
        // representing a host's shared connection from the point of view of an ICS client, and not what we're
        // interested in.
        if (pprops->dwCharacter & NCCF_SHARED)
        {
            fShared = TRUE;
        }

        NcFreeNetconProperties(pprops);
    }

    return fShared;
}

void CHomeNetworkWizard::FillConnectionList(HWND hwndList, INetConnection* pncExcludeFromList, DWORD dwFlags)
{
    DestroyConnectionList(hwndList);

    BOOL fSelected = FALSE; // Has an entry in the list been selected

    if (g_fRunningOnNT)
    {
        HRESULT hr = 0;

        if (_pConnViewCB != NULL)
            _pConnViewCB->MessageSFVCB(DVM_REFRESH, TRUE, TRUE);
    
        if (SUCCEEDED(hr))
        {
            // Enumerate each net connection
            DWORD cItems = DPA_GetPtrCount(_hdpaConnections);

            for (DWORD iItem = 0; iItem < cItems; iItem ++)
            {                        
                
                INetConnection* pnc = (INetConnection*) DPA_GetPtr(_hdpaConnections, iItem);

                ASSERT(pnc);

                // Is this the kind of connection we want to show?
                if (ShouldShowConnection(pnc, pncExcludeFromList, dwFlags))
                {
                    NETCON_PROPERTIES* pncprops;

                    hr = pnc->GetProperties(&pncprops);

                    if (SUCCEEDED(hr))
                    {
                        LVITEM lvi = {0};
                        
                        if ((dwFlags & CONN_EXTERNAL) && fSelected)
                        {
                            // If we have a default public adapter, insert other adapters
                            // after it so the default appears at the top of the list.
                            lvi.iItem = 1;
                        }
                        
                        lvi.mask = LVIF_PARAM | LVIF_TEXT;
                        lvi.pszText = pncprops->pszwName;
                        lvi.lParam = (LPARAM) pnc; // We addref this guy when/if we actually add this item

                        // Get the icon index for this connection
                        int iIndex;        

                        hr = GetConnectionIconIndex(pncprops->guidId, _psfConnections, &iIndex);
                        
                        if (SUCCEEDED(hr))
                        {
                            lvi.iImage = iIndex;
                            if (-1 != lvi.iImage)
                               lvi.mask |= LVIF_IMAGE;
                        }

                        // Its ok if the icon stuff failed for now
                        hr = S_OK;

                        int iItem = ListView_InsertItem(hwndList, &lvi);

                        if (-1 != iItem)
                        {
                            pnc->AddRef();

                            ListView_SetItemText(hwndList, iItem, 1, pncprops->pszwDeviceName);

                            if (dwFlags & CONN_EXTERNAL)
                            {
                                // Select the connection that is already ICS public, if applicable, or
                                // Use network location awareness to guess at a connection type - TODO
                                if (pncprops->dwCharacter & NCCF_SHARED || NLA_INTERNET_YES == GetConnectionInternetType(&pncprops->guidId))
                                {
                                    ListView_SetItemState(hwndList, iItem, LVIS_SELECTED, LVIS_SELECTED);
                                    fSelected = TRUE;
                                }
                            }

                            if (dwFlags & CONN_INTERNAL)
                            {
                                ListView_SetItemState(hwndList, iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
                                fSelected = TRUE;
                            }
                        }

                        NcFreeNetconProperties(pncprops);
                    }
                }
            }
        }

    }
    else
    {
        if (_hnetInfo.cNA && _hnetInfo.pNA)
        {
            const NETADAPTER* pNA = _hnetInfo.pNA;

            for (UINT i = 0; i < _hnetInfo.cNA; i++, pNA++)
            {
                // Check if the NIC is working.

                if (W9xIsValidAdapter(pNA, dwFlags))
                {
                    fSelected = W9xAddAdapterToList(pNA, pNA->szDisplayName, i, 0, hwndList, dwFlags);
                }
                else if (W9xIsAdapterDialUp(pNA))
                {
                    _hnetInfo.cRas = W9xEnumRasEntries();

                    for (UINT j = 0; j < _hnetInfo.cRas; j++)
                    {
                        // W9xAddAdapterToList ALWAYS adds the adapter regardless of the
                        // state of the connection.  So we do not have the equivalent of
                        // the Whistler "ShouldShowConnection".  Here we need to check
                        // the flags to exclude listing inappropriate adapters.
                    
                        if ( ~CONN_UNPLUGGED & dwFlags )    // Never show RAS Entry's as Unplugged
                        {
                            fSelected = W9xAddAdapterToList( pNA, 
                                                             _hnetInfo.pRas[j].szEntryName, 
                                                             i, j, 
                                                             hwndList, 
                                                             dwFlags );
                        }
                    }
                }
            }
        }
    }

    ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
    ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
}

BOOL  CHomeNetworkWizard::W9xAddAdapterToList(const NETADAPTER* pNA, const WCHAR* pszDesc, UINT uiAdapterIndex, UINT uiDialupIndex, HWND hwndList, DWORD dwFlags)
{
    BOOL fSelected = FALSE;

    LVITEM lvi = {0};
    lvi.mask = LVIF_PARAM | LVIF_TEXT;

    WCHAR szNicType[MAX_PATH];
    W9xGetNetTypeName(pNA->bNetType, szNicType, ARRAYSIZE(szNicType));
    lvi.pszText = szNicType;

    lvi.lParam = MAKELONG(LOWORD(uiAdapterIndex), LOWORD(uiDialupIndex));

    int iItem = ListView_InsertItem(hwndList, &lvi);

    if (-1 != iItem)
    {
        ListView_SetItemText(hwndList, iItem, 1, (LPWSTR)pszDesc);

        if (dwFlags & CONN_EXTERNAL)
        {
            if (NETTYPE_DIALUP == pNA->bNetType ||
                NETTYPE_PPTP   == pNA->bNetType ||
                NETTYPE_ISDN   == pNA->bNetType    )
            {
                ListView_SetItemState(hwndList, iItem, LVIS_SELECTED, LVIS_SELECTED);
                fSelected = TRUE;
            }

        }

        if (dwFlags & CONN_INTERNAL)
        {
            if (NETTYPE_LAN  == pNA->bNetType ||
                NETTYPE_IRDA == pNA->bNetType ||    // BUGBUG internal? (edwardp) 5/31/00
                NETTYPE_TV   == pNA->bNetType    )  // BUGBUG internal? (edwardp) 5/31/00
            {
                ListView_SetItemState(hwndList, iItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
                fSelected = TRUE;
            }
        }
    }

    return fSelected;
}

UINT CHomeNetworkWizard::W9xEnumRasEntries()
{
    UINT uiRet = 0;

    if (!_hnetInfo.pRas)
    {
        DWORD cDUNs = 0;
        DWORD cb    = 0;

        RasEnumEntries(NULL, NULL, NULL, &cb, &cDUNs);

        if (cb > 0)
        {
            _hnetInfo.pRas = (RASENTRYNAME*)LocalAlloc(LPTR, cb);

            if (_hnetInfo.pRas)
            {
                _hnetInfo.pRas->dwSize = sizeof(RASENTRYNAME);

                if (RasEnumEntries(NULL, NULL, _hnetInfo.pRas, &cb, &cDUNs) == 0)
                {
                    uiRet = cDUNs;
                }
            }
        }
    }

    return uiRet;
}

typedef struct
{
    LPCWSTR idPage;
    DLGPROC pDlgProc;
    LPCWSTR pHeading;
    LPCWSTR pSubHeading;
    DWORD dwFlags;
} WIZPAGE;

#define MAKEWIZPAGE(name, dlgproc, dwFlags)   \
    { MAKEINTRESOURCE(IDD_WIZ_##name##), dlgproc, MAKEINTRESOURCE(IDS_HEADER_##name##), MAKEINTRESOURCE(IDS_SUBHEADER_##name##), dwFlags }

INT_PTR MyPropertySheet(LPCPROPSHEETHEADER pHeader);
HPROPSHEETPAGE MyCreatePropertySheetPage(LPPROPSHEETPAGE psp);


HRESULT DoesUserHaveHNetPermissions(BOOL* pfHasPermission)
{
    HRESULT hr;

    if (g_fRunningOnNT)
    {
        // TODO: We need to check this stuff in once the net team RI's
        INetConnectionUiUtilities* pNetConnUiUtil;
        *pfHasPermission = FALSE;

        hr = CoCreateInstance(CLSID_NetConnectionUiUtilities, NULL, CLSCTX_INPROC, IID_PPV_ARG(INetConnectionUiUtilities, &pNetConnUiUtil));

        if (SUCCEEDED(hr))
        {
            if (pNetConnUiUtil->UserHasPermission(NCPERM_ShowSharedAccessUi) &&
#if 0 // NYI
                pNetConnUiUtil->UserHasPermission(NCPERM_AllowNetBridge_NLA) &&
#endif
                pNetConnUiUtil->UserHasPermission(NCPERM_ICSClientApp) &&
                pNetConnUiUtil->UserHasPermission(NCPERM_PersonalFirewallConfig))
            {
                *pfHasPermission = TRUE;
            }

            pNetConnUiUtil->Release();
        }
        else
        {
            TraceMsg(TF_WARNING, "Could not cocreate CLSID_NetConnectionUIUtilities");
        }
    }
    else
    {
        // Windows 9x
        *pfHasPermission = TRUE;
        hr = S_OK;
    }

    return hr;
}

BOOL CHomeNetworkWizard::IsMachineOnDomain()
{
    BOOL fDomain = FALSE;

#ifdef NO_CHECK_DOMAIN
    return fDomain;
#endif

    NETSETUP_JOIN_STATUS njs;

    //
    // Make sure to initialize pszName to NULL.  On W9x NetJoinInformation returns NERR_Success
    // but doesn't allocate pszName.  On retail builds the stack garbage in pszName happened to
    // be the this pointer for CHomeNetworkWizard and NetApiBufferFreeWrap called LocalFree
    // on it.
    //
    LPWSTR pszName = NULL;  // init to NULL! See comment above.
    if (NERR_Success == NetGetJoinInformation(NULL, &pszName, &njs))
    {
        fDomain = (NetSetupDomainName == njs);
        NetApiBufferFree(pszName);
    }

    return fDomain;
}

BOOL CHomeNetworkWizard::IsMachineWrongOS()
{
    BOOL fWrongOS = TRUE;

    if (IsOS(OS_WINDOWS))
    {
        if (IsOS(OS_WIN98ORGREATER))
        {
            fWrongOS = FALSE;
        }
    }
    else
    {
        if (IsOS(OS_WHISTLERORGREATER))
        {
            fWrongOS = FALSE;
        }
    }

    return fWrongOS;
}

// The page indices for the possible start pages (three error and one real start page)
#define PAGE_NOTADMIN      0
#define PAGE_NOPERMISSIONS 1
#define PAGE_NOHARDWARE    2
#define PAGE_WRONGOS       3
#define PAGE_DOMAIN        4
#define PAGE_WELCOME       5
#define PAGE_CONNECT       9
#define PAGE_FINISH        25

HRESULT CHomeNetworkWizard::ShowWizard(HWND hwnd, BOOL* pfRebootRequired)
{
    HRESULT hr = S_OK;
    TCHAR szCaption[256];
    LoadString(g_hinst, IDS_WIZ_CAPTION, szCaption, ARRAYSIZE(szCaption));
    CEnsureSingleInstance ESI(szCaption);

    if (!ESI.ShouldExit())
    {
        if (g_fRunningOnNT)
        {
            LinkWindow_RegisterClass_NT();
        }

        *pfRebootRequired = FALSE;
        hr = Initialize();
        if (SUCCEEDED(hr))
        {
            WIZPAGE c_wpPages[] =
            {
                // Error start pages
                MAKEWIZPAGE(NOTADMIN,          CantRunWizardPageProc,     PSP_HIDEHEADER),
                MAKEWIZPAGE(NOPERMISSIONS,     CantRunWizardPageProc,     PSP_HIDEHEADER),
                MAKEWIZPAGE(NOHARDWARE,        NoHardwareWelcomePageProc, PSP_HIDEHEADER),
                MAKEWIZPAGE(WRONGOS,           CantRunWizardPageProc,     PSP_HIDEHEADER),
                MAKEWIZPAGE(DOMAINWELCOME,     CantRunWizardPageProc,     PSP_HIDEHEADER),
                // Real start page
                MAKEWIZPAGE(WELCOME,           WelcomePageProc,           PSP_HIDEHEADER),
                MAKEWIZPAGE(MANUALCONFIG,      ManualConfigPageProc,      0),
                MAKEWIZPAGE(UNPLUGGED,         UnpluggedPageProc,         0),
                MAKEWIZPAGE(FOUNDICS,          FoundIcsPageProc,          0),
                MAKEWIZPAGE(CONNECT,           ConnectPageProc,           0),
                MAKEWIZPAGE(CONNECTOTHER,      ConnectOtherPageProc,      0),
                MAKEWIZPAGE(PUBLIC,            PublicPageProc,            0),
                MAKEWIZPAGE(EDGELESS,          EdgelessPageProc,           0),
                MAKEWIZPAGE(ICSCONFLICT,       ICSConflictPageProc,       0),
                MAKEWIZPAGE(BRIDGEWARNING,     BridgeWarningPageProc,     0),
                MAKEWIZPAGE(PRIVATE,           PrivatePageProc,           0),
                MAKEWIZPAGE(NAME,              NamePageProc,              0),
                MAKEWIZPAGE(WORKGROUP,         WorkgroupPageProc,         0),
                MAKEWIZPAGE(SUMMARY,           SummaryPageProc,           0),
                MAKEWIZPAGE(PROGRESS,          ProgressPageProc,          0),
                MAKEWIZPAGE(ALMOSTDONE,        AlmostDonePageProc,        0),
                MAKEWIZPAGE(CHOOSEDISK,        ChooseDiskPageProc,        0),
                MAKEWIZPAGE(INSERTDISK,        InsertDiskPageProc,        0),
                MAKEWIZPAGE(FLOPPYINST,        InstructionsPageProc,      0),
                MAKEWIZPAGE(CDINST,            InstructionsPageProc,      0),
                MAKEWIZPAGE(FINISH,            FinishPageProc,            PSP_HIDEHEADER),
                MAKEWIZPAGE(CONFIGERROR,       ErrorFinishPageProc,       PSP_HIDEHEADER),
                MAKEWIZPAGE(NOHARDWAREFINISH,  NoHardwareFinishPageProc,  PSP_HIDEHEADER),
            };

            // Sanity check to make sure we haven't added new error pages without updating the
            // welcome page number
            ASSERT(c_wpPages[PAGE_WELCOME].idPage == MAKEINTRESOURCE(IDD_WIZ_WELCOME));
            ASSERT(c_wpPages[PAGE_CONNECT].idPage == MAKEINTRESOURCE(IDD_WIZ_CONNECT));
            ASSERT(c_wpPages[PAGE_FINISH].idPage  == MAKEINTRESOURCE(IDD_WIZ_FINISH));

            if (!g_fRunningOnNT)
            {
                c_wpPages[PAGE_WELCOME].idPage = MAKEINTRESOURCE(IDD_WIZ_WIN9X_WELCOME);
                c_wpPages[PAGE_FINISH].idPage  = MAKEINTRESOURCE(IDD_WIZ_WIN9X_FINISH);

                CICSInst* pICS = new CICSInst;
                if (pICS)
                {
                    if (!pICS->IsInstalled())
                    {
                        c_wpPages[PAGE_CONNECT].idPage = MAKEINTRESOURCE(IDD_WIZ_WIN9X_CONNECT);
                        _fNoICSQuestion = TRUE;
                    }

                    delete pICS;
                }
            }

            HPROPSHEETPAGE rghpage[MAX_HNW_PAGES];
            int cPages;

            for (cPages = 0; cPages < ARRAYSIZE(c_wpPages); cPages ++)
            {
                PROPSHEETPAGE psp = { 0 };

                psp.dwSize = SIZEOF(PROPSHEETPAGE);
                psp.hInstance = g_hinst;
                psp.lParam = (LPARAM)this;
                psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | PSP_USEHEADERTITLE |
                                            PSP_USEHEADERSUBTITLE | c_wpPages[cPages].dwFlags;
                psp.pszTemplate = c_wpPages[cPages].idPage;
                psp.pfnDlgProc = c_wpPages[cPages].pDlgProc;
                psp.pszTitle = MAKEINTRESOURCE(IDS_WIZ_CAPTION);
                psp.pszHeaderTitle = c_wpPages[cPages].pHeading;
                psp.pszHeaderSubTitle = c_wpPages[cPages].pSubHeading;

                rghpage[cPages] = MyCreatePropertySheetPage(&psp);
            }

            ASSERT(cPages < MAX_HNW_PAGES);

            PROPSHEETHEADER psh = { 0 };
            psh.dwSize = SIZEOF(PROPSHEETHEADER);
            psh.hwndParent = hwnd;
            psh.hInstance = g_hinst;
            psh.dwFlags = PSH_USEICONID | PSH_WIZARD | PSH_WIZARD97 | PSH_WATERMARK | PSH_STRETCHWATERMARK | PSH_HEADER;
            psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
            psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
            psh.nPages = cPages;
            psh.phpage = rghpage;
            psh.pszIcon = MAKEINTRESOURCE(IDI_APPICON);

            // Check for administrator and policy (permissions)
            BOOL fUserIsAdmin = FALSE;
            BOOL fUserHasPermissions = FALSE;

            IsUserLocalAdmin(NULL, &fUserIsAdmin);
            DoesUserHaveHNetPermissions(&fUserHasPermissions);

            if (!fUserIsAdmin)
            {
                // Not admin error page
                psh.nStartPage = PAGE_NOTADMIN;
            }
            else if (!fUserHasPermissions)
            {
                // No permissions error page
                psh.nStartPage = PAGE_NOPERMISSIONS;
            }
            else if (GetConnectionCount(NULL, CONN_INTERNAL) < 1)
            {
                if ( g_fRunningOnNT && ( GetConnectionCount(NULL, CONN_EXTERNAL) > 0 ) )
                {
                    TraceMsg(TF_WARNING, "External Adapters Only");

                    psh.nStartPage = PAGE_WELCOME;
                    _fExternalOnly  = TRUE;
                }
                else
                {
                    // No hardware error page
                    psh.nStartPage = PAGE_NOHARDWARE;
                }
            }
            else if (IsMachineWrongOS())
            {
                psh.nStartPage = PAGE_WRONGOS;
            }
            else if (IsMachineOnDomain())
            {
                psh.nStartPage = PAGE_DOMAIN;
            }
            else
            {
                // Run the real wizard
                psh.nStartPage = PAGE_WELCOME;
            }

            INT_PTR iReturn = MyPropertySheet(&psh);
            *pfRebootRequired = ((iReturn == ID_PSRESTARTWINDOWS) || (iReturn == ID_PSREBOOTSYSTEM));

            Uninitialize();
        }
    }

    return hr;
}

void GetTitleFont(LPTSTR pszFaceName, DWORD cch)
{
    if (!LoadString(g_hinst, IDS_TITLE_FONT, pszFaceName, cch))
    {
        lstrcpyn(pszFaceName, TEXT("Verdana"), cch);
    }
}

LONG GetTitlePointSize()
{
    LONG lPointSize = 0;
    TCHAR szPointSize[20];

    if (LoadString(g_hinst, IDS_TITLE_POINTSIZE, szPointSize, ARRAYSIZE(szPointSize)))
    {
        lPointSize = StrToInt(szPointSize);
    }

    if (!lPointSize)
    {
        lPointSize = 12;
    }

    return lPointSize;
}

void CHomeNetworkWizard::WelcomeSetTitleFont(HWND hwnd)
{
    HWND hwndTitle = GetDlgItem(hwnd, IDC_TITLE);

    // Get the existing font
    HFONT hfontOld = (HFONT) SendMessage(hwndTitle, WM_GETFONT, 0, 0);

    LOGFONT lf = {0};
    if (GetObject(hfontOld, sizeof(lf), &lf))
    {
        GetTitleFont(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName));

        HDC hDC = GetDC(hwndTitle);
        if (hDC)
        {
            lf.lfHeight = -MulDiv(GetTitlePointSize(), GetDeviceCaps(hDC, LOGPIXELSY), 72);
            lf.lfWeight = FW_BOLD;

            HFONT hfontNew = CreateFontIndirect(&lf);
            if (hfontNew)
            {
                SendMessage(hwndTitle, WM_SETFONT, (WPARAM) hfontNew, FALSE);

                // Don't do this, its shared.
                // DeleteObject(hfontOld);
            }

            ReleaseDC(hwndTitle, hDC);
        }
    }

    LONG lStyle = GetWindowLong(GetParent(hwnd), GWL_STYLE);
    SetWindowLong(GetParent(hwnd), GWL_STYLE, lStyle & ~WS_SYSMENU);
}

INT_PTR CHomeNetworkWizard::WelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->WelcomeSetTitleFont(hwnd);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
                return TRUE;
            case PSN_WIZNEXT:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_MANUALCONFIG);
                if (!g_fRunningOnNT)
                    pthis->PushPage(IDD_WIZ_WIN9X_WELCOME);
                else
                    pthis->PushPage(IDD_WIZ_WELCOME);
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::NoHardwareWelcomePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->WelcomeSetTitleFont(hwnd);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_HARDWAREREQ);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, 0);
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"network.chm%3A%3A/hnw_requirements.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}

void CHomeNetworkWizard::ReplaceStaticWithLink(HWND hwndStatic, UINT idcLinkControl, UINT idsLinkText)
{
    if (g_fRunningOnNT)
    {
        RECT rcStatic;
        HWND hwndParent = GetParent(hwndStatic);
        if (GetWindowRect(hwndStatic, &rcStatic) &&
            MapWindowPoints(NULL, hwndParent, (LPPOINT) &rcStatic, 2))
        {
            WCHAR szLinkText[256];
            if (LoadString(g_hinst, idsLinkText, szLinkText, ARRAYSIZE(szLinkText)))
            {
                HWND hwndLink = CreateWindowEx(0, TEXT("SysLink"), szLinkText, WS_CHILD | WS_TABSTOP | LWS_IGNORERETURN,
                    rcStatic.left, rcStatic.top, (rcStatic.right - rcStatic.left), (rcStatic.bottom - rcStatic.top),
                    hwndParent, NULL, g_hinst, NULL);

                if (hwndLink)
                {
                    SetWindowLongPtr(hwndLink, GWLP_ID, (LONG_PTR) idcLinkControl);
                    ShowWindow(hwndLink, SW_SHOW);
                    ShowWindow(hwndStatic, SW_HIDE);
                }
            }
        }
    }
}

void CHomeNetworkWizard::ManualRefreshConnectionList()
{
    if (g_fRunningOnNT)
    {
        // Refresh connection DPA in case the user plugged in more connections
        HDPA hdpaConnections2;
        if (SUCCEEDED(GetConnections(&hdpaConnections2)))
        {
            // Replace the real list with our new one
            DPA_DestroyCallback(_hdpaConnections, FreeConnectionDPACallback, NULL);
            _hdpaConnections = hdpaConnections2;

            // Ensure we remove our other holds to the INetConnections
            // Free public lan information
            FreeExternalConnection(&_hnetInfo);

            // Free private lan information
            FreeInternalConnections(&_hnetInfo);
        }
    }
}

INT_PTR CHomeNetworkWizard::ManualConfigPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_INSTALLATION);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                return TRUE;
            case PSN_WIZNEXT:
                // pthis->ManualRefreshConnectionList();
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_UNPLUGGED);
                pthis->PushPage(IDD_WIZ_MANUALCONFIG);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            if (IsOS(OS_PERSONAL))
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_checklistP.htm");
                            }
                            else
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_checklistW.htm");
                            }
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}

// Returns TRUE if there are some unplugged connections, FALSE o/w
BOOL CHomeNetworkWizard::UnpluggedFillList(HWND hwnd)
{
    BOOL fSomeUnpluggedConnections = FALSE;

    HWND hwndList = GetDlgItem(hwnd, IDC_CONNLIST);
    FillConnectionList(hwndList, NULL, CONN_UNPLUGGED);

    // and if there actually are unplugged connections...
    if (0 != ListView_GetItemCount(hwndList))
    {
        // Show this page
        fSomeUnpluggedConnections = TRUE;
    }

    return fSomeUnpluggedConnections;
}

INT_PTR CHomeNetworkWizard::UnpluggedPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->InitializeConnectionList(GetDlgItem(hwnd, IDC_CONNLIST), CONN_UNPLUGGED);
        SendDlgItemMessage(hwnd, IDC_IGNORE, BM_SETCHECK, BST_UNCHECKED, 0);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    // If we don't need to show this page
                    if (!pthis->UnpluggedFillList(hwnd))
                    {
                        // Don't push ourselves on the stack
                        // But navigate to the next page...
                        SetWindowLongPtr( hwnd, DWLP_MSGRESULT, 
                                          (pthis->_fExternalOnly) ? IDD_WIZ_CONNECTOTHER : IDD_WIZ_FOUNDICS );
                    }
                }
                return TRUE;
            case PSN_WIZNEXT:
                {
                    BOOL fStillUnplugged = pthis->UnpluggedFillList(hwnd);
                    int idNext;
                    if (!fStillUnplugged)
                    {
                        // User fixed the problem. Go forward and don't store this
                        // error page on the pagestack
                        
                        idNext = (pthis->_fExternalOnly) ? IDD_WIZ_CONNECTOTHER : IDD_WIZ_FOUNDICS;
                    }
                    else if (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_IGNORE, BM_GETCHECK, 0, 0))
                    {
                        // User wants to go on, but store this page on the pagestack
                        // so they can "back" up to it.
                        pthis->PushPage(IDD_WIZ_UNPLUGGED);
                        idNext = (pthis->_fExternalOnly) ? IDD_WIZ_CONNECTOTHER : IDD_WIZ_FOUNDICS;
                    }
                    else
                    {
                        // User still has disconnected net hardware they don't want to ignore. Tell them and keep them on this page.
                        DisplayFormatMessage(hwnd, IDS_WIZ_CAPTION, IDS_STILLUNPLUGGED, MB_ICONERROR | MB_OK);
                        idNext = -1;
                    }

                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, idNext);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            }
        }
        return FALSE;
    case WM_DESTROY:
        pthis->DestroyConnectionList(GetDlgItem(hwnd, IDC_CONNLIST));
        return TRUE;
    }

    return FALSE;
}



void CHomeNetworkWizard::PublicSetControlState(HWND hwnd)
{
    BOOL fSelection = ListView_GetSelectedCount(GetDlgItem(hwnd, IDC_CONNLIST));
    PropSheet_SetWizButtons(GetParent(hwnd), fSelection ? PSWIZB_BACK | PSWIZB_NEXT : PSWIZB_BACK);
}

void FreeExternalConnection(PHOMENETSETUPINFO pInfo)
{
    if (pInfo->pncExternal)
    {
        pInfo->pncExternal->Release();
        pInfo->pncExternal = NULL;
    }
}

void CHomeNetworkWizard::PublicNextPage(HWND hwnd)
{
    FreeExternalConnection(&_hnetInfo);

    // Get the selected external adapter
    HWND hwndList = GetDlgItem(hwnd, IDC_CONNLIST);

    int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);

    // We can assert here since Next should be disabled if there is no selection!
    ASSERT(-1 != iItem);

    LVITEM lvi = {0};
    lvi.iItem = iItem;
    lvi.mask = LVIF_PARAM;

    if (ListView_GetItem(hwndList, &lvi))
    {
        if (g_fRunningOnNT)
        {
            _hnetInfo.pncExternal = (INetConnection*) (lvi.lParam);
            _hnetInfo.pncExternal->AddRef();
        }
        else
        {
            _hnetInfo.ipaExternal = lvi.lParam;
        }
    }

    // Do the real wizard navigation

    UINT idPage = IDD_WIZ_NAME;

    if (g_fRunningOnNT)
    {
        idPage = _fShowSharingPage ? IDD_WIZ_EDGELESS : IDD_WIZ_ICSCONFLICT;
    }

    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, idPage);
}

void CHomeNetworkWizard::FoundIcsSetText(HWND hwnd)
{
    if (GetICSMachine(_szICSMachineName, ARRAYSIZE(_szICSMachineName)))
    {
        TCHAR szMsg[256];
        if (FormatMessageString(IDS_ICSMSG, szMsg, ARRAYSIZE(szMsg), _szICSMachineName))
        {
            SetWindowText(GetDlgItem(hwnd, IDC_ICSMSG), szMsg);
        }
    }
    else
    {
        // No ICS beacon - ask the user how they connect
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) _fNoICSQuestion ? IDD_WIZ_WIN9X_CONNECT : IDD_WIZ_CONNECT);
    }
}

INT_PTR CHomeNetworkWizard::FoundIcsPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        SendMessage(GetDlgItem(hwnd, IDC_SHARECONNECT), BM_SETCHECK, BST_CHECKED, 0);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                pthis->FoundIcsSetText(hwnd);
                return TRUE;
            case PSN_WIZNEXT:
                {
                    UINT idNext;
                    if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_SHARECONNECT), BM_GETCHECK, 0, 0))
                    {
                        // This machine should be an ICS Client and won't have a public connection
                        pthis->_hnetInfo.dwFlags = HNET_SHAREPRINTERS |
                                                   HNET_SHAREFOLDERS |
                                                   HNET_ICSCLIENT;

                        pthis->_fShowPublicPage = FALSE;
                        pthis->_fShowSharingPage = FALSE;
                        pthis->_fICSClient = TRUE;

                        idNext = g_fRunningOnNT ? IDD_WIZ_ICSCONFLICT : IDD_WIZ_NAME;
                    }
                    else
                    {
                        idNext = pthis->_fNoICSQuestion ? IDD_WIZ_WIN9X_CONNECT : IDD_WIZ_CONNECT;
                    }

                    pthis->PushPage(IDD_WIZ_FOUNDICS);
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, idNext);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

void CHomeNetworkWizard::ConnectSetDefault(HWND hwnd)
{
    UINT idSelect = IDC_ICSHOST;

    if ((!g_fRunningOnNT) || (GetConnectionCount(NULL, CONN_INTERNAL | CONN_EXTERNAL) == 1))
    {
        idSelect = IDC_ICSCLIENT;
    }

    SendDlgItemMessage(hwnd, idSelect, BM_SETCHECK, BST_CHECKED, 0);
}

void CHomeNetworkWizard::ConnectNextPage(HWND hwnd)
{
    _fICSClient = FALSE;
    _fNoHomeNetwork = FALSE;

    if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_ICSHOST), BM_GETCHECK, 0, 0))
    {
        // This machine will need a firewalled, public connection, and should be ICS Host.
        _hnetInfo.dwFlags = HNET_SHARECONNECTION |
                            HNET_FIREWALLCONNECTION |
                            HNET_SHAREPRINTERS |
                            HNET_SHAREFOLDERS;

        _fShowPublicPage = TRUE;
        _fShowSharingPage = FALSE;
    }
    else if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_ICSCLIENT), BM_GETCHECK, 0, 0))
    {
        // This machine should be an ICS Client and won't have a public connection
        _hnetInfo.dwFlags = HNET_SHAREPRINTERS |
                            HNET_SHAREFOLDERS |
                            HNET_ICSCLIENT;

        _fShowPublicPage = FALSE;
        _fShowSharingPage = FALSE;
        _fICSClient = TRUE;
    }
    else if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_ALLCOMPUTERSDIRECT), BM_GETCHECK, 0, 0))
    {
        // This machine will need a public connection, and we should ask before sharing files
        _hnetInfo.dwFlags = HNET_FIREWALLCONNECTION |
                            HNET_SHAREPRINTERS |
                            HNET_SHAREFOLDERS;

        _fShowPublicPage = TRUE;
        _fShowSharingPage = TRUE;
    }
    else if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_NOHOMENETWORK), BM_GETCHECK, 0, 0))
    {
        // This machine needs a firewalled, public connection and not much else
        _hnetInfo.dwFlags = HNET_FIREWALLCONNECTION |
                            HNET_SHAREPRINTERS |
                            HNET_SHAREFOLDERS;

        _fShowPublicPage = TRUE;
        _fShowSharingPage = FALSE;
        _fNoHomeNetwork = TRUE;
    }
    else if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_NOINTERNET), BM_GETCHECK, 0, 0))
    {
        // No internet box
        _hnetInfo.dwFlags = HNET_SHAREPRINTERS |
                            HNET_SHAREFOLDERS;
                         
        _fShowPublicPage = FALSE;
        _fShowSharingPage = FALSE;
    }

    UINT idNext;
    if (g_fRunningOnNT)
    {
        if (BST_CHECKED == SendMessage(GetDlgItem(hwnd, IDC_OTHER), BM_GETCHECK, 0, 0))
        {
            idNext = IDD_WIZ_CONNECTOTHER;        
        }
        else
        {
            idNext = _fShowPublicPage ? IDD_WIZ_PUBLIC : IDD_WIZ_ICSCONFLICT;
        }
    }
    else
    {
        // For now - TODO: Ed and I need to figure out what we need to do downlevel (9x + 2k)
        idNext = IDD_WIZ_NAME;
    }

    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, idNext);
}

INT_PTR CHomeNetworkWizard::ConnectPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_HNCONFIG);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_SHOWMESTATIC1), IDC_SHOWMELINK1, IDS_SHOWME);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_SHOWMESTATIC2), IDC_SHOWMELINK2, IDS_SHOWME);
        pthis->ConnectSetDefault(hwnd);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                return TRUE;
            case PSN_WIZNEXT:
                {
                    pthis->PushPage(pthis->_fNoICSQuestion ? IDD_WIZ_WIN9X_CONNECT : IDD_WIZ_CONNECT);
                    pthis->ConnectNextPage(hwnd);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            if (IsOS(OS_PERSONAL))
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_howto_connectP.htm");
                            }
                            else
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_howto_connectW.htm");
                            }

                        }
                        return TRUE;
                     case IDC_SHOWMELINK1:
                        {
                            pthis->ShowMeLink(hwnd, L"ntart.chm::/hn_showme1.htm");
                        }
                        return TRUE;
                     case IDC_SHOWMELINK2:
                        {
                            pthis->ShowMeLink(hwnd, L"ntart.chm::/hn_showme2.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::ConnectOtherPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        if ( pthis->_fExternalOnly )
        {
            TraceMsg(TF_WARNING, "External Adapters Only");
        }

        SendDlgItemMessage(hwnd, IDC_ALLCOMPUTERSDIRECT, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_HNCONFIG);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_SHOWMESTATIC3), IDC_SHOWMELINK3, IDS_SHOWME);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_SHOWMESTATIC4), IDC_SHOWMELINK4, IDS_SHOWME);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_SHOWMESTATIC5), IDC_SHOWMELINK5, IDS_SHOWME);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                return TRUE;
            case PSN_WIZNEXT:
                {
                    pthis->PushPage(IDD_WIZ_CONNECTOTHER);
                    pthis->ConnectNextPage(hwnd);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"netcfg.chm%3A%3A/share_conn_overvw.htm");
                        }
                        return TRUE;
                    case IDC_SHOWMELINK3:
                        {
 
                            pthis->ShowMeLink(hwnd, L"ntart.chm::/hn_showme3.htm");
                        }
                        return TRUE;
                    case IDC_SHOWMELINK4:
                        {
 
                            pthis->ShowMeLink(hwnd, L"ntart.chm::/hn_showme4.htm");
                        }
                        return TRUE;
                    case IDC_SHOWMELINK5:
                        {
 
                            pthis->ShowMeLink(hwnd, L"ntart.chm::/hn_showme5.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}


void CHomeNetworkWizard::DestroyConnectionList(HWND hwndList)
{
    if (g_fRunningOnNT)
    {
        int nItems = ListView_GetItemCount(hwndList);

        for (int iItem = 0; iItem < nItems; iItem ++)
        {
            // Get our stashed INetConnection for each item in the list and free it
            LVITEM lvi = {0};
            lvi.mask = LVIF_PARAM;
            lvi.iItem = iItem;

            if (ListView_GetItem(hwndList, &lvi))
            {
                ((INetConnection*) lvi.lParam)->Release();
            }
        }
    }

    ListView_DeleteAllItems(hwndList);
}

inline void _SetDlgItemRect(HWND hwnd, UINT id, RECT* pRect)
{
    SetWindowPos(GetDlgItem(hwnd, id), NULL, pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top, SWP_NOZORDER | SWP_SHOWWINDOW);
}

void _OffsetDlgItem(HWND hwnd, UINT id, int xOffset, int yOffset, BOOL fAdjustWidth, BOOL fAdjustHeight)
{
    RECT rc;
    HWND hwndControl = GetDlgItem(hwnd, id);
    GetWindowRect(hwndControl, &rc);
    MapWindowPoints(NULL, hwnd, (LPPOINT) &rc, 2);
    OffsetRect(&rc, xOffset, yOffset);

    if (fAdjustWidth)
    {
        rc.right -= xOffset;
    }

    if (fAdjustHeight)
    {
        rc.bottom -= yOffset;
    }

    _SetDlgItemRect(hwnd, id, &rc);
}

void CHomeNetworkWizard::PublicMoveControls(HWND hwnd, BOOL fItemPreselected)
{
    // We need to move controls around on this page depending on whether or not an item is preselected or not
    // Reset the dialog so that all controls are in their default positions
    PublicResetControlPositions(hwnd);

    if (fItemPreselected)
    {
        // We are transitioning from the "default" position to one where an item is preselected.
        // The only work we do here is to hide the help icon and move over the help text a bit to the left

        int xOffset = (PublicControlPositions._rcHelpIcon.left) - (PublicControlPositions._rcHelpText.left);
        UINT idHelp = IsWindowVisible(GetDlgItem(hwnd, IDC_HELPSTATIC)) ? IDC_HELPSTATIC : IDC_HELPLINK;
        _OffsetDlgItem(hwnd, idHelp, xOffset, 0, TRUE, FALSE);
        ShowWindow(GetDlgItem(hwnd, IDC_HELPICON), SW_HIDE);
    }
    else
    {
        // We are transitioning from the "default" position to the one where we don't have a preselection
        // We need to hide the "we've automatically selected..." message and move up the list label,
        // and expand the connection list.

        int yOffset = (PublicControlPositions._rcSelectMessage.top) - (PublicControlPositions._rcListLabel.top);
        _OffsetDlgItem(hwnd, IDC_LISTLABEL, 0, yOffset, FALSE, FALSE);
        _OffsetDlgItem(hwnd, IDC_CONNLIST, 0, yOffset, FALSE, TRUE);
        ShowWindow(GetDlgItem(hwnd, IDC_SELECTMSG), SW_HIDE);
    }
}

void CHomeNetworkWizard::PublicSetActive(HWND hwnd)
{
    
    HCURSOR hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
    
    FreeExternalConnection(&_hnetInfo);

    HWND hwndList = GetDlgItem(hwnd, IDC_CONNLIST);
    FillConnectionList(hwndList, NULL, CONN_EXTERNAL);

    // Auto-select if there is only one connection listed
    if (ListView_GetItemCount(hwndList) == 1
    #ifdef DEBUG
        && !(GetKeyState(VK_CONTROL) < 0) // don't do this if CTRL is down for debugging
    #endif
       )
    {
        ListView_SetItemState(hwndList, 0, LVIS_SELECTED, LVIS_SELECTED);

        // PublicNextPage will set DWLP_MSGRESULT and tell the wizard to skip this page
        // and go on to the next.
        PublicNextPage(hwnd);
    }
    else
    {
        // If there is a selected item
        int iSelectedItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
        if (-1 != iSelectedItem)
        {
            // Read the item name and set the alternate "Windows recommends this connection." text
            WCHAR szItem[256], szMsg[256];
            ListView_GetItemText(hwndList, iSelectedItem, 0, szItem, ARRAYSIZE(szItem));
            FormatMessageString(IDS_RECOMMENDEDCONN, szMsg, ARRAYSIZE(szMsg), szItem);
            SetDlgItemText(hwnd, IDC_SELECTMSG, szMsg);

            BoldControl(hwnd, IDC_SELECTMSG);
        }

        PublicMoveControls(hwnd, (-1 != iSelectedItem));
        PublicSetControlState(hwnd);
    }

    if(NULL != hOldCursor)
    {
        SetCursor(hOldCursor);
    }
}

void CHomeNetworkWizard::PublicGetControlPositions(HWND hwnd)
{
    // Remember the default positions of the controls that will move as we reorganize this dialog
    GetWindowRect(GetDlgItem(hwnd, IDC_SELECTMSG), &PublicControlPositions._rcSelectMessage);
    GetWindowRect(GetDlgItem(hwnd, IDC_LISTLABEL), &PublicControlPositions._rcListLabel);
    GetWindowRect(GetDlgItem(hwnd, IDC_CONNLIST), &PublicControlPositions._rcList);
    GetWindowRect(GetDlgItem(hwnd, IDC_HELPICON), &PublicControlPositions._rcHelpIcon);
    GetWindowRect(GetDlgItem(hwnd, IDC_HELPSTATIC), &PublicControlPositions._rcHelpText);

    // We actually need them in client coords
    // Map 2 points (1 rect) at a time since Mirrored points get screwed up with more
    MapWindowPoints(NULL, hwnd, (LPPOINT) &PublicControlPositions._rcSelectMessage, 2);
    MapWindowPoints(NULL, hwnd, (LPPOINT) &PublicControlPositions._rcListLabel, 2);
    MapWindowPoints(NULL, hwnd, (LPPOINT) &PublicControlPositions._rcList, 2);
    MapWindowPoints(NULL, hwnd, (LPPOINT) &PublicControlPositions._rcHelpIcon, 2);
    MapWindowPoints(NULL, hwnd, (LPPOINT) &PublicControlPositions._rcHelpText, 2);
}

void CHomeNetworkWizard::PublicResetControlPositions(HWND hwnd)
{
    // Set the controls back to their default positions
    _SetDlgItemRect(hwnd, IDC_SELECTMSG, &PublicControlPositions._rcSelectMessage);
    _SetDlgItemRect(hwnd, IDC_LISTLABEL, &PublicControlPositions._rcListLabel);
    _SetDlgItemRect(hwnd, IDC_CONNLIST, &PublicControlPositions._rcList);
    _SetDlgItemRect(hwnd, IDC_HELPICON, &PublicControlPositions._rcHelpIcon);

    UINT idHelp = IsWindowVisible(GetDlgItem(hwnd, IDC_HELPSTATIC)) ? IDC_HELPSTATIC : IDC_HELPLINK;
    _SetDlgItemRect(hwnd, idHelp, &PublicControlPositions._rcHelpText);
}


INT_PTR CHomeNetworkWizard::PublicPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->InitializeConnectionList(GetDlgItem(hwnd, IDC_CONNLIST), CONN_EXTERNAL);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_SELECTPUBLIC);
        pthis->PublicGetControlPositions(hwnd);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    pthis->PublicSetActive(hwnd);
                    return TRUE;
                }
            case PSN_WIZNEXT:
                pthis->PushPage(IDD_WIZ_PUBLIC);
                pthis->PublicNextPage(hwnd);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case LVN_ITEMCHANGED:
                pthis->PublicSetControlState(hwnd);
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"network.chm%3A%3A/hnw_determine_internet_connection.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    case WM_DESTROY:
        pthis->DestroyConnectionList(GetDlgItem(hwnd, IDC_CONNLIST));
        return TRUE;
    }

    return FALSE;
}

void CHomeNetworkWizard::EdgelessSetActive(HWND hwnd)
{
    PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK | PSWIZB_NEXT);
//    _hnetInfo.dwFlags &= (~(HNET_SHAREFOLDERS | HNET_SHAREPRINTERS));

    if (!ShouldShowConnection(_hnetInfo.pncExternal, NULL, CONN_INTERNAL))
    {
        // External connection is a modem or such - no file sharing necessary (user already said they didn't have a home network)
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_ICSCONFLICT);
    }
}

INT_PTR CHomeNetworkWizard::EdgelessPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_RECOMMENDED);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                pthis->EdgelessSetActive(hwnd);
                return TRUE;
            case PSN_WIZNEXT:
                pthis->_hnetInfo.dwFlags |= (HNET_SHAREFOLDERS | HNET_SHAREPRINTERS);
                pthis->PushPage(IDD_WIZ_EDGELESS);
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) IDD_WIZ_BRIDGEWARNING);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            if (IsOS(OS_PERSONAL))
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_nohost_computerP.htm");
                            }
                            else
                            {
                                HelpCenter(hwnd, L"network.chm%3A%3A/hnw_nohost_computerW.htm");
                            }
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }
    return FALSE;
}

BOOL CHomeNetworkWizard::IsICSIPInUse( WCHAR** ppszHost, PDWORD pdwSize )
{
    HRESULT          hr;
    INetConnection** ppArray = NULL;
    DWORD            dwItems = 0;
    BOOL             bExists = FALSE;

    if ( ppszHost )
        *ppszHost = NULL;

    if ( pdwSize )
        *pdwSize = 0;

    hr = GetInternalConnectionArray( _hnetInfo.pncExternal, &ppArray, &dwItems );
    
    if ( SUCCEEDED(hr) )
    {
        hr = E_FAIL;
    
        for( DWORD i=0; i<dwItems; i++ )
        {
            if ( S_OK != hr )
            {
                hr = HrLookupForIpAddress( ppArray[i], 
                                           DEFAULT_SCOPE_ADDRESS, 
                                           &bExists, 
                                           ppszHost, 
                                           pdwSize );
            }
                                       
            ppArray[i]->Release();
            
        }   //  for( DWORD i=0; i<dwItems; i++ )
        
        LocalFree( ppArray );

    }   //  if ( SUCCEEDED(hr) )
        
    return bExists;
}

void CHomeNetworkWizard::ICSConflictSetActive(HWND hwnd)
{
    WCHAR* pszConflictingHost = NULL;
    DWORD  dwSize = 0;

    PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK | PSWIZB_NEXT);

    static const int _KnownControls[] = 
    {
        IDC_KNOWNCONFLICT1,
        IDC_KNOWNCONFLICT2,
        IDC_KNOWNCONFLICT3,
        IDC_KNOWNCONFLICT4,
        IDC_KNOWNCONFLICT5,
        IDC_KNOWNCONFLICT6,
        IDC_KNOWNCONFLICT7,
        IDC_KNOWNCONFLICT8,
        IDC_COMPUTERNAME
    };

    static const int _UnknownControls[] = 
    {
        IDC_UNKNOWNCONFLICT1,
        IDC_UNKNOWNCONFLICT2
    };
    
    if ((_hnetInfo.dwFlags & HNET_SHARECONNECTION) && IsICSIPInUse(&pszConflictingHost, &dwSize))
    {
        // We show and hide controls depending on if we already know about an ICS machine name
        WCHAR szICSHost[MAX_PATH];
        if (GetICSMachine(szICSHost, ARRAYSIZE(szICSHost)))
        {
            // We know this is a UPnP ICS host - show the "known conflict" set of controls
            ShowControls(hwnd, _KnownControls, ARRAYSIZE(_KnownControls), SW_SHOWNORMAL);
            ShowControls(hwnd, _UnknownControls, ARRAYSIZE(_UnknownControls), SW_HIDE);
            SetDlgItemText(hwnd, IDC_COMPUTERNAME, szICSHost);
        }
        else
        {
            // We have no idea what's hogging our IP - show a very generic set of controls
            ShowControls(hwnd, _UnknownControls, ARRAYSIZE(_UnknownControls), SW_SHOWNORMAL);
            ShowControls(hwnd, _KnownControls, ARRAYSIZE(_KnownControls), SW_HIDE);
        }
    }
    else
    {
        // Go on to the next screen
        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_BRIDGEWARNING);
    }

        
    if ( pszConflictingHost )
        delete [] pszConflictingHost;
}

INT_PTR CHomeNetworkWizard::ICSConflictPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_ICSCONFLICT);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;
            WCHAR*  pszConflictingHost = NULL;
            DWORD   dwSize = 0;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                pthis->ICSConflictSetActive(hwnd);
                return TRUE;
            case PSN_WIZNEXT:
                if (pthis->IsICSIPInUse(&pszConflictingHost, &dwSize))
                {
                    DisplayFormatMessage(hwnd, IDS_WIZ_CAPTION, IDS_STILLICSCONFLICT, MB_ICONERROR | MB_OK);
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, -1);
                }
                else
                {
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) IDD_WIZ_BRIDGEWARNING);
                }
                if ( pszConflictingHost )
                    delete [] pszConflictingHost;
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"network.chm%3A%3A/hnw_change_ics_host.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }
    return FALSE;
}

DWORD CHomeNetworkWizard::GetConnectionCount(INetConnection* pncExclude, DWORD dwFlags)
{
    DWORD dwCount = 0;

    if (g_fRunningOnNT)
    {
        DWORD cItems = DPA_GetPtrCount(_hdpaConnections);
        for (DWORD iItem = 0; iItem < cItems; iItem ++)
        {
            INetConnection* pnc = (INetConnection*) DPA_GetPtr(_hdpaConnections, iItem);
            if (ShouldShowConnection(pnc, pncExclude, dwFlags))
            {
                dwCount++;
            }
        }
    }
    else
    {
        const NETADAPTER* pNA = _hnetInfo.pNA;

        for (UINT i = 0; i < _hnetInfo.cNA; i++, pNA++)
        {
            // Check if the NIC is working.

            if (W9xIsValidAdapter(pNA, dwFlags))
            {
                dwCount++;
            }
        }
    }

    return dwCount;
}

INT_PTR CHomeNetworkWizard::BridgeWarningPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        SendDlgItemMessage(hwnd, IDC_AUTOBRIDGE, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_BRIDGE);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    if (pthis->_fNoHomeNetwork)
                    {
                        FreeInternalConnections(&pthis->_hnetInfo);
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_NAME);
                    }
                    else
                    {
                        DWORD nInternal = pthis->GetConnectionCount(pthis->_hnetInfo.pncExternal, CONN_INTERNAL);
                        if (1 < nInternal)
                        {
                            // We show this page if there are two or more internal connections (which is this case)
                            PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                        }
                        else if ((pthis->_hnetInfo.dwFlags & HNET_SHARECONNECTION) && (0 == nInternal))
                        {
                            // We are sharing the public connection, and there are no other connections left
                            // over for home networking. Show error page
                            pthis->_fManualBridgeConfig = FALSE;
                            SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_NOHARDWAREFINISH);
                        }
                        else
                        {
                            // There are either zero or one internal connections. If zero, then we aren't sharing the connection
                            // Skip the bridge warning page and go to the private page
                            pthis->_fManualBridgeConfig = FALSE;
                            SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_PRIVATE);
                        }
                    }

                    return TRUE;
                }
            case PSN_WIZNEXT:
                {
                    pthis->_fManualBridgeConfig = (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_MANUALBRIDGE, BM_GETCHECK, 0, 0));
                    pthis->PushPage(IDD_WIZ_BRIDGEWARNING);
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_PRIVATE);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"netcfg.chm%3A%3A/hnw_understanding_bridge.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}

void FreeInternalConnections(PHOMENETSETUPINFO pInfo)
{
    // Delete any existing private connection information

    if (pInfo->prgncInternal)
    {
        for (DWORD i = 0; i < pInfo->cncInternal; i++)
        {
            INetConnection* pnc = pInfo->prgncInternal[i];
            pnc->Release();
        }

        LocalFree(pInfo->prgncInternal);
        pInfo->prgncInternal = NULL;
    }

    pInfo->cncInternal = 0;
}

void FreeInternalGUIDs(PHOMENETSETUPINFO pInfo)
{
    if (pInfo->prgguidInternal)
    {
        LocalFree(pInfo->prgguidInternal);
        pInfo->prgguidInternal = NULL;
    }

    pInfo->cguidInternal = 0;
}

DWORD _ListView_GetCheckedCount(HWND hwndList)
{
    int nItems = ListView_GetItemCount(hwndList);
    DWORD nCheckedItems = 0;
    if (-1 != nItems)
    {
        for(int iItem = 0; iItem < nItems; iItem ++)
        {
            if (ListView_GetCheckState(hwndList, iItem))
            {
                nCheckedItems ++;
            }
        }
    }

    return nCheckedItems;
}

void CHomeNetworkWizard::PrivateSetControlState(HWND hwnd)
{
    BOOL fEnableNext = TRUE;

    // If the user is sharing a connection, they must specify at least one private connection
    if (_hnetInfo.dwFlags & HNET_SHARECONNECTION)
    {
        fEnableNext = (0 != _ListView_GetCheckedCount(GetDlgItem(hwnd, IDC_CONNLIST)));
    }

    PropSheet_SetWizButtons(GetParent(hwnd), fEnableNext ? PSWIZB_BACK | PSWIZB_NEXT : PSWIZB_BACK);
}


void CHomeNetworkWizard::PrivateNextPage(HWND hwnd)
{
    FreeInternalConnections(&_hnetInfo);

    // Figure out the number of connections we'll need
    HWND hwndList = GetDlgItem(hwnd, IDC_CONNLIST);
    int nItems = ListView_GetItemCount(hwndList);
    DWORD nCheckedItems = _ListView_GetCheckedCount(hwndList);

    if (nCheckedItems)
    {
        _hnetInfo.prgncInternal = (INetConnection**) LocalAlloc(LPTR, (nCheckedItems + 1) * sizeof (INetConnection*));
        // Alloc one extra INetConnection* and Null-terminate this array so we can pass it to HNet config api

        if (_hnetInfo.prgncInternal)
        {
            _hnetInfo.cncInternal = 0;
            // Get the INetConnection for each checked item
            for (int iItem = 0; iItem < nItems; iItem ++)
            {
                if (ListView_GetCheckState(hwndList, iItem))
                {
                    LVITEM lvi = {0};
                    lvi.iItem = iItem;
                    lvi.mask = LVIF_PARAM;
                    ListView_GetItem(hwndList, &lvi);

                    if (g_fRunningOnNT)
                    {
                        _hnetInfo.prgncInternal[_hnetInfo.cncInternal] = (INetConnection*) lvi.lParam;
                        _hnetInfo.prgncInternal[_hnetInfo.cncInternal]->AddRef();
                    }
                    else
                    {
                        // TODO W9x
                    }
                    _hnetInfo.cncInternal ++;
                }
            }

            // Assert since if we messed something up there might not be enough space allocated in the buffer!
            ASSERT(nCheckedItems == _hnetInfo.cncInternal);
        }
    }

    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_NAME);
}

INT_PTR CHomeNetworkWizard::PrivatePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->InitializeConnectionList(GetDlgItem(hwnd, IDC_CONNLIST), CONN_INTERNAL);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_BRIDGE);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    FreeInternalConnections(&(pthis->_hnetInfo));

                    HWND hwndList = GetDlgItem(hwnd, IDC_CONNLIST);
                    pthis->FillConnectionList(hwndList, pthis->_hnetInfo.pncExternal, CONN_INTERNAL);

                    // If the user hasn't select manual bridge and/or there is less than 2 items,
                    // then _fManualBridgeConfig will be FALSE and we'll autobridge
                    if (!pthis->_fManualBridgeConfig)
                    {
                        // PrivateNextPage will set DWLP_MSGRESULT and tell the wizard to skip this page
                        // and go on to the next.
                        pthis->PrivateNextPage(hwnd);
                    }

                    pthis->PrivateSetControlState(hwnd);
                    return TRUE;
                }
            case PSN_WIZNEXT:
                pthis->PrivateNextPage(hwnd);
                pthis->PushPage(IDD_WIZ_PRIVATE);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"netcfg.chm%3A%3A/hnw_understanding_bridge.htm");
                        }
                        return TRUE;
                }
            case LVN_ITEMCHANGED:
                pthis->PrivateSetControlState(hwnd);
                return TRUE;
            }
        }
        return FALSE;
    case WM_DESTROY:
        pthis->DestroyConnectionList(GetDlgItem(hwnd, IDC_CONNLIST));
        return FALSE;
    }

    return FALSE;
}

void CHomeNetworkWizard::NameSetControlState(HWND hwnd)
{
    BOOL fEnableNext = (0 != GetWindowTextLength(GetDlgItem(hwnd, IDC_COMPUTERNAME)));
    PropSheet_SetWizButtons(GetParent(hwnd), fEnableNext ? PSWIZB_BACK | PSWIZB_NEXT : PSWIZB_BACK);
}

HRESULT CHomeNetworkWizard::NameNextPage(HWND hwnd)
{
    HRESULT hr = E_FAIL;
    GetDlgItemText(hwnd, IDC_COMPUTERDESC, _hnetInfo.szComputerDescription, ARRAYSIZE(_hnetInfo.szComputerDescription));
    GetDlgItemText(hwnd, IDC_COMPUTERNAME, _hnetInfo.szComputer, ARRAYSIZE(_hnetInfo.szComputer));
    
    // There are two errors that we show for computer name: INVALID and DUPLICATE
    // TODO: We only detect duplicate for NT so far!!!
    UINT idError = IDS_COMPNAME_INVALID;

    // Test to see if the name is more than 15 OEM bytes
    int iBytes = WideCharToMultiByte(CP_OEMCP, 0, _hnetInfo.szComputer, -1, NULL, 0, NULL, NULL) - 1;
        
    if (iBytes <= LM20_DNLEN)
    {
        if (IsValidNameSyntax(_hnetInfo.szComputer, NetSetupMachine))
        {
            if (g_fRunningOnNT)
            {
                SetCursor(LoadCursor(NULL, IDC_WAIT));
                NET_API_STATUS nas = NetValidateName(NULL, _hnetInfo.szComputer, NULL, NULL, NetSetupMachine);
                if (ERROR_DUP_NAME == nas)
                {
                    ASSERT(E_FAIL == hr);
                    idError = IDS_COMPNAME_DUPLICATE;
                }
                else if (NERR_InvalidComputer == nas)
                {
                    ASSERT(E_FAIL == hr);
                    idError = IDS_COMPNAME_INVALID;
                }
                else
                {
                    // if there is any other failure we just go ahead.  If the Client For MS Networks is not installed we 
                    // can't validate the name, but it should be fine to use what we have
                
                    hr = S_OK;
                    _hnetInfo.dwFlags |= HNET_SETCOMPUTERNAME;
                }
            }
            else
            {
                // TODO: Win9x!!!
                hr = S_OK;
                _hnetInfo.dwFlags |= HNET_SETCOMPUTERNAME;
            }
        }
    }
    else
    {
        ASSERT(E_FAIL == hr);
        idError = IDS_COMPNAME_TOOMANYBYTES;
    }

    // If the computer name didn't validate, don't change pages and show an error.
    if(FAILED(hr))
    {
        SetFocus(GetDlgItem(hwnd, IDC_COMPUTERNAME));
        PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK);
        DisplayFormatMessage(hwnd, IDS_WIZ_CAPTION, idError, MB_ICONERROR | MB_OK, _hnetInfo.szComputer);
    }

    return hr;
}

void CHomeNetworkWizard::NameInitDialog(HWND hwnd)
{
    // Limit the edit fields
    SendDlgItemMessage(hwnd, IDC_COMPUTERDESC, EM_SETLIMITTEXT, ARRAYSIZE(_hnetInfo.szComputerDescription) - 1, NULL);
    SendDlgItemMessage(hwnd, IDC_COMPUTERNAME, EM_SETLIMITTEXT, ARRAYSIZE(_hnetInfo.szComputer) - 1, NULL);

    // Set the current name as the default

    WCHAR szComputerName[ARRAYSIZE(_hnetInfo.szComputer)];
    *szComputerName = 0;

    WCHAR szDescription[ARRAYSIZE(_hnetInfo.szComputerDescription)];
    *szDescription = 0;

    if (g_fRunningOnNT)
    {
        SERVER_INFO_101_NT* psv101 = NULL;
        if (NERR_Success == NetServerGetInfo_NT(NULL, 101, (LPBYTE*) &psv101))
        {
            if (psv101->sv101_comment && psv101->sv101_comment[0])
            {
                StrCpyN(szDescription, psv101->sv101_comment, ARRAYSIZE(szDescription));
            }

            ASSERT(psv101->sv101_name);
            StrCpyN(szComputerName, psv101->sv101_name, ARRAYSIZE(szComputerName));
            NetApiBufferFree(psv101);
        }
    }
    else
    {
        AllPlatformGetComputerName(szComputerName, ARRAYSIZE(szComputerName));

        CRegistry reg;
        if (reg.OpenKey(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\VxD\\VNETSUP", KEY_READ))
        {
            reg.QueryStringValue(L"Comment", szDescription, ARRAYSIZE(szDescription));
        }
    }

    SetDlgItemText(hwnd, IDC_COMPUTERNAME, szComputerName);
    SetDlgItemText(hwnd, IDC_COMPUTERDESC, szDescription);

    WCHAR szNameMessage[256];
    if (FormatMessageString(IDS_CURRENTNAME, szNameMessage, ARRAYSIZE(szNameMessage), szComputerName))
    {
        SetDlgItemText(hwnd, IDC_CURRENTNAME, szNameMessage);
    }
}

INT_PTR CHomeNetworkWizard::NamePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->NameInitDialog(hwnd);
        pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_COMPNAME);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    // Show the ISP Warning if this computer connects directly to the Internet
                    int nShowISPWarning = (NULL != pthis->_hnetInfo.pncExternal) ? SW_SHOW : SW_HIDE;
                    ShowWindow(GetDlgItem(hwnd, IDC_ISPWARN1), nShowISPWarning);
                    ShowWindow(GetDlgItem(hwnd, IDC_ISPWARN2), nShowISPWarning);

                    pthis->NameSetControlState(hwnd);
                }
                return TRUE;
            case PSN_WIZNEXT:
                if (SUCCEEDED(pthis->NameNextPage(hwnd)))
                {
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) IDD_WIZ_WORKGROUP);
                    pthis->PushPage(IDD_WIZ_NAME);
                }
                else
                {
                    // else not changing pages; don't push
                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) -1);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        {
                            HelpCenter(hwnd, L"network.chm%3A%3A/hnw_comp_name_description.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    case WM_COMMAND:
        {
            switch (HIWORD(wParam))
            {
            case EN_CHANGE:
                if (LOWORD(wParam) == IDC_COMPUTERNAME)
                {
                    pthis->NameSetControlState(hwnd);
                }
                return FALSE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

void CHomeNetworkWizard::WorkgroupSetControlState(HWND hwnd)
{
    BOOL fNext = (0 != SendDlgItemMessage(hwnd, IDC_WORKGROUP, WM_GETTEXTLENGTH, 0, 0));
    PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK | (fNext ? PSWIZB_NEXT : 0));
}

HRESULT CHomeNetworkWizard::WorkgroupNextPage(HWND hwnd)
{
    HRESULT hr = E_FAIL;

    UINT idError = IDS_WORKGROUP_INVALID;
    
    if (GetDlgItemText(hwnd, IDC_WORKGROUP, _hnetInfo.szWorkgroup, ARRAYSIZE(_hnetInfo.szWorkgroup)))
    {
        // Test to see if the name is more than 15 OEM bytes
        int iBytes = WideCharToMultiByte(CP_OEMCP, 0, _hnetInfo.szWorkgroup, -1, NULL, 0, NULL, NULL) - 1;
        
        if (iBytes <= LM20_DNLEN)
        {
            // Remove any preceding blanks
            size_t szLen  = wcslen( _hnetInfo.szWorkgroup ) + 1;
            LPWSTR szTemp = new WCHAR[ szLen ];
            
            if ( szTemp )
            {
                WCHAR* pch;
                
                for ( pch = _hnetInfo.szWorkgroup; *pch && (L' ' == *pch); )
                    pch++;
                    
                wcsncpy( szTemp, pch, szLen );
                wcsncpy( _hnetInfo.szWorkgroup, szTemp, szLen );
            
                delete [] szTemp;
            }
        
            // Use the computer name check for workgroups too
            if (IsValidNameSyntax(_hnetInfo.szWorkgroup, NetSetupWorkgroup))
            {
                if (g_fRunningOnNT)
                {
                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    NET_API_STATUS nas = NetValidateName(NULL, _hnetInfo.szWorkgroup, NULL, NULL, NetSetupWorkgroup);
                    if (NERR_InvalidWorkgroupName != nas) // we only put up a invalid name dialog if the name was invalid.  
                    {
                        hr = S_OK;
                        _hnetInfo.dwFlags |= HNET_SETWORKGROUPNAME;
                    }
                }
                else
                {
                    // TODO: Win9x!!!
                    hr = S_OK;
                    _hnetInfo.dwFlags |= HNET_SETWORKGROUPNAME;
                }
            }
        }
        else
        {
            ASSERT(E_FAIL == hr);
            idError = IDS_WORKGROUP_TOOMANYBYTES;
        }

        // If the computer name didn't validate, don't change pages and show an error.
        if(FAILED(hr))
        {
            SetFocus(GetDlgItem(hwnd, IDC_WORKGROUP));
            PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK);
            DisplayFormatMessage(hwnd, IDS_WIZ_CAPTION, idError, MB_ICONERROR | MB_OK, _hnetInfo.szWorkgroup);
        }
    }

    return hr;
}

INT_PTR CHomeNetworkWizard::WorkgroupPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            WCHAR szWorkgroup[LM20_DNLEN + 1]; *szWorkgroup = 0;
            LoadString(g_hinst, IDS_DEFAULT_WORKGROUP1, szWorkgroup, ARRAYSIZE(szWorkgroup));
            SetDlgItemText(hwnd, IDC_WORKGROUP, szWorkgroup);
            SendDlgItemMessage(hwnd, IDC_WORKGROUP, EM_LIMITTEXT, ARRAYSIZE(pthis->_hnetInfo.szWorkgroup) - 1, 0);
        }
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    pthis->WorkgroupSetControlState(hwnd);
                    pthis->_hnetInfo.dwFlags &= (~HNET_SETWORKGROUPNAME);
                }
                return TRUE;
            case PSN_WIZNEXT:
                {
                    if (SUCCEEDED(pthis->WorkgroupNextPage(hwnd)))
                    {
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) IDD_WIZ_SUMMARY);
                        pthis->PushPage(IDD_WIZ_WORKGROUP);
                    }
                    else
                    {
                        // else not changing pages; don't push
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) -1);
                    }
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            }
        }
        return FALSE;
    case WM_COMMAND:
        {
            switch (HIWORD(wParam))
            {
            case EN_CHANGE:
                if (LOWORD(wParam) == IDC_WORKGROUP)
                {
                    pthis->WorkgroupSetControlState(hwnd);
                }
                return FALSE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::SummaryPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            return TRUE;
        }
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_QUERYINITIALFOCUS:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) GetDlgItem(hwnd, IDC_TITLE));
                return TRUE;
            case PSN_SETACTIVE:
                pthis->SummarySetActive(hwnd);
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZNEXT:
                pthis->PushPage(IDD_WIZ_SUMMARY);
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_PROGRESS);
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

#define WM_CONFIGDONE   (WM_USER + 0x100)

INT_PTR CHomeNetworkWizard::ProgressPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            return TRUE;
        }
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    PropSheet_CancelToClose(GetParent(hwnd));
                    PropSheet_SetWizButtons(pnmh->hwndFrom, 0);

                    pthis->_hnetInfo.fAsync = TRUE;
                    pthis->_hnetInfo.hwnd = hwnd;
                    pthis->_hnetInfo.umsgAsyncNotify = WM_CONFIGDONE;

                    ConfigureHomeNetwork(&(pthis->_hnetInfo));

                    HWND hwndAnimate = GetDlgItem(hwnd, IDC_PROGRESS);
                    Animate_Open(hwndAnimate, g_fRunningOnNT ? IDA_CONFIG : IDA_LOWCOLORCONFIG);
                    Animate_Play(hwndAnimate, 0, -1, -1);
                }

                return TRUE;
            }
        }
        return FALSE;
    case WM_CONFIGDONE:
        {
            Animate_Stop(GetDlgItem(hwnd, IDC_PROGRESS));

            // The config thread has finished. We assert that the thread has freed/nulled out
            // all of his INetConnection*'s since otherwise the UI thread will try to use/free them!
            ASSERT(NULL == pthis->_hnetInfo.pncExternal);
            ASSERT(NULL == pthis->_hnetInfo.prgncInternal);

            if (pthis->_hnetInfo.fRebootRequired)
            {
                PropSheet_RebootSystem(GetParent(hwnd));
            }

            // The HRESULT from the configuration is stored in wParam
            HRESULT hr = (HRESULT) wParam;
            UINT idFinishPage;

            if (SUCCEEDED(hr))
            {
                idFinishPage = IDD_WIZ_ALMOSTDONE;
            }
            else
            {
                idFinishPage = IDD_WIZ_CONFIGERROR;
            }

            PropSheet_SetCurSelByID(GetParent(hwnd), idFinishPage);
        }
        return TRUE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::AlmostDonePageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            SendDlgItemMessage(hwnd, IDC_CREATEDISK, BM_SETCHECK, BST_CHECKED, 0);
            return TRUE;
        }
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    if (g_fRunningOnNT)
                    {
                        PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
                    }
                    else
                    {
                        // Skip this page on 9x
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_WIN9X_FINISH);
                    }
                }
                return TRUE;
            case PSN_WIZNEXT:
                {
                    // This page only shows on NT
                    ASSERT(g_fRunningOnNT);
                    pthis->_fFloppyInstructions = TRUE;
                    pthis->PushPage(IDD_WIZ_ALMOSTDONE);

                    UINT idNext = IDD_WIZ_FINISH;
                    if (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_CREATEDISK, BM_GETCHECK, 0, 0))
                    {
                        idNext = IDD_WIZ_CHOOSEDISK;
                    }
                    else if (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_HAVEDISK, BM_GETCHECK, 0, 0))
                    {
                        idNext = IDD_WIZ_FLOPPYINST;
                    }
                    else if (BST_CHECKED == SendDlgItemMessage(hwnd, IDC_HAVECD, BM_GETCHECK, 0, 0))
                    {
                        idNext = IDD_WIZ_CDINST;
                        pthis->_fFloppyInstructions = FALSE;
                    }

                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LONG_PTR) idNext);
                }
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

void CHomeNetworkWizard::FillDriveList(HWND hwndList)
{
    ListView_SetImageList(hwndList, _himlLarge, LVSIL_NORMAL);

    ListView_DeleteAllItems(hwndList);

    WCHAR szDrive[] = L"A:\\";
    for (UINT i = 0; i < 26; i++)
    {
        szDrive[0] = L'A' + i;

        if (DRIVE_REMOVABLE == GetDriveType(szDrive))
        {
            LVITEM lvi = {0};
            lvi.mask = LVIF_PARAM | LVIF_TEXT;
            lvi.lParam = i;

            int iIndex;
            WCHAR szDriveDisplay[256];
            HRESULT hr = GetDriveNameAndIconIndex(szDrive, szDriveDisplay, ARRAYSIZE(szDriveDisplay), &iIndex);
            if (SUCCEEDED(hr))
            {
                lvi.iImage = iIndex;
                if (-1 != lvi.iImage)
                   lvi.mask |= LVIF_IMAGE;

                lvi.pszText = szDriveDisplay;

                int iItem = ListView_InsertItem(hwndList, &lvi);
            }
        }
    }

    ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
    ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE);
}

void CHomeNetworkWizard::ChooseDiskSetControlState(HWND hwnd)
{
    BOOL fSelection = ListView_GetSelectedCount(GetDlgItem(hwnd, IDC_DEVICELIST));
    PropSheet_SetWizButtons(GetParent(hwnd), fSelection ? PSWIZB_BACK | PSWIZB_NEXT : PSWIZB_BACK);
}

INT_PTR CHomeNetworkWizard::ChooseDiskPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        ASSERT(g_fRunningOnNT);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_QUERYINITIALFOCUS:
                SetFocus(GetDlgItem(hwnd, IDC_DEVICELIST));
                return TRUE;
            case PSN_SETACTIVE:
                {
                    HWND hwndList = GetDlgItem(hwnd, IDC_DEVICELIST);
                    pthis->FillDriveList(hwndList);
                    pthis->ChooseDiskSetControlState(hwnd);

                    int cDrives = ListView_GetItemCount(hwndList);

                    if (0 >= cDrives)
                    {
                        // There are no removable drives or an error occurred
                        DisplayFormatMessage(hwnd, IDS_WIZ_CAPTION, IDS_NOREMOVABLEDRIVES, MB_ICONINFORMATION | MB_OK);
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                    }
                    else if (1 == cDrives)
                    {
                        // One drive - autoselect it and go to the next page
                        LVITEM lvi = {0};
                        lvi.mask = LVIF_PARAM;
                        ListView_GetItem(hwndList, &lvi);
                        pthis->_iDrive = lvi.lParam;
                        ListView_GetItemText(hwndList, lvi.iItem, 0, pthis->_szDrive, ARRAYSIZE(pthis->_szDrive));
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_INSERTDISK);
                    }
                }
                return TRUE;
            case PSN_WIZNEXT:
                {
                    HWND hwndList = GetDlgItem(hwnd, IDC_DEVICELIST);
                    pthis->_iDrive = 0;
                    LVITEM lvi = {0};
                    lvi.mask = LVIF_PARAM;
                    lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
                    if (-1 != lvi.iItem && ListView_GetItem(hwndList, &lvi))
                    {
                        pthis->_iDrive = lvi.lParam;
                        ListView_GetItemText(hwndList, lvi.iItem, 0, pthis->_szDrive, ARRAYSIZE(pthis->_szDrive));
                        pthis->PushPage(IDD_WIZ_CHOOSEDISK);
                        SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_INSERTDISK);
                    }
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case LVN_ITEMCHANGED:
                pthis->ChooseDiskSetControlState(hwnd);
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

#define SOURCE_FILE "%windir%\\system32\\netsetup.exe"
HRESULT CHomeNetworkWizard::GetSourceFilePath(LPSTR pszSource, DWORD cch)
{
    HRESULT hr = E_FAIL;

    if (ExpandEnvironmentStringsA(SOURCE_FILE, pszSource, cch))
    {
        DWORD c = lstrlenA(pszSource);
        if (c + 2 <= cch)
        {
            // Add double NULL since we'll be passing this to SHFileOperation
            pszSource[c + 1] = '\0';
            hr = S_OK;
        }
    }

    return hr;
}

INT_PTR CHomeNetworkWizard::InsertDiskPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        ASSERT(g_fRunningOnNT);
        return TRUE;
    case WM_COMMAND:
        if (IDC_FORMAT == LOWORD(wParam))
        {
            SHFormatDrive(hwnd, pthis->_iDrive, 0, 0);
            return TRUE;
        }
        return FALSE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    SetDlgItemText(hwnd, IDC_DISK, pthis->_szDrive);
                    PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZNEXT:
                {
                    CHAR szSource[MAX_PATH];
                    if (SUCCEEDED(GetSourceFilePath(szSource, ARRAYSIZE(szSource))))
                    {
                        // Double-null since we'll be passing this to SHFileOperation
                        CHAR szDest[] = "a:\\netsetup.exe\0";
                        szDest[0] = 'A' + pthis->_iDrive;

                        SHFILEOPSTRUCTA shfo = {0};
                        shfo.wFunc = FO_COPY;
                        shfo.pFrom = szSource;
                        shfo.pTo = szDest;
                        shfo.fFlags = FOF_SIMPLEPROGRESS | FOF_NOCONFIRMATION;
                        CHAR szTitle[256];
                        LoadStringA(g_hinst, IDS_COPYING, szTitle, ARRAYSIZE(szTitle));
                        shfo.lpszProgressTitle = szTitle;

                        int i = SHFileOperationA(&shfo);
                        if (i || shfo.fAnyOperationsAborted)
                        {
                            SetWindowLongPtr(hwnd, DWLP_MSGRESULT, -1);
                        }
                        else
                        {
                            SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_FLOPPYINST);
                        }
                    }
                }
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::InstructionsPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        ASSERT(g_fRunningOnNT);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                {
                    PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);

                    // If we aren't going to need to reboot
                    if (!pthis->_hnetInfo.fRebootRequired)
                    {
                        // Then we don't want to tell the user to reboot in the text
                        UINT idNoReboot = IDS_CD_NOREBOOT;

                        if (pthis->_fFloppyInstructions)
                        {
                            // We're on the floppy instructions page
                            idNoReboot = IDS_FLOPPY_NOREBOOT;
                        }

                        WCHAR szLine[256];
                        LoadString(g_hinst, idNoReboot, szLine, ARRAYSIZE(szLine));
                        SetDlgItemText(hwnd, IDC_INSTRUCTIONS, szLine);
                    }
                }
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZNEXT:
                pthis->PushPage(pthis->_fFloppyInstructions ? IDD_WIZ_FLOPPYINST : IDD_WIZ_CDINST);
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, IDD_WIZ_FINISH);
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}



void _AddLineToBuffer(LPCWSTR pszLine, LPWSTR pszBuffer, DWORD cchBuffer, DWORD* piChar)
{
    lstrcpyn(pszBuffer + (*piChar), pszLine, cchBuffer - (*piChar));
    *piChar += lstrlen(pszLine);
    lstrcpyn(pszBuffer + (*piChar), L"\r\n", cchBuffer - (*piChar));
    *piChar += 2;
}

void CHomeNetworkWizard::SummarySetActive(HWND hwnd)
{
    WCHAR szText[2048];
    WCHAR szLine[256];
    DWORD iChar = 0;

    // Fill the list with some information based on what things we're going to do to
    // configure their home network.

    if (_hnetInfo.pncExternal)
    {
        // "Internet connection settings:"
        LoadString(g_hinst, IDS_SUMMARY_INETSETTINGS, szLine, ARRAYSIZE(szLine));
        _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);

        NETCON_PROPERTIES* pncprops;
        HRESULT hr = _hnetInfo.pncExternal->GetProperties(&pncprops);
        if (SUCCEEDED(hr))
        {
            // Internet connection:\t%1
            if (FormatMessageString(IDS_SUMMARY_INETCON, szLine, ARRAYSIZE(szLine), pncprops->pszwName))
            {
                _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
            }

            // Internet connection sharing:\tenabled
            if (_hnetInfo.dwFlags & HNET_SHARECONNECTION)
            {
                LoadString(g_hinst, IDS_SUMMARY_ICSENABLED, szLine, ARRAYSIZE(szLine));
                _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
            }

            // Personal firewall:\tenabled
            if (_hnetInfo.dwFlags & HNET_FIREWALLCONNECTION)
            {
                LoadString(g_hinst, IDS_SUMMARY_FIREWALLENABLED, szLine, ARRAYSIZE(szLine));
                _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
            }

            NcFreeNetconProperties(pncprops);
        }

        LoadString(g_hinst, IDS_SUMMARY_UNDERLINE, szLine, ARRAYSIZE(szLine));
        _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
    }
    else
    {
        if (_fICSClient)
        {
            // "Internet connection settings:"
            LoadString(g_hinst, IDS_SUMMARY_INETSETTINGS, szLine, ARRAYSIZE(szLine));
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);

            if (*_szICSMachineName)
            {
                // Connecting via ICS through:\t%1
                if (FormatMessageString(IDS_sUMMARY_CONNECTTHROUGH, szLine, ARRAYSIZE(szLine), _szICSMachineName))
                {
                    _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
                }
            }
            else
            {
                // Connecting through another device or computer.
                LoadString(g_hinst, IDS_SUMMARY_CONNECTTHROUGH2, szLine, ARRAYSIZE(szLine));
                _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
            }

            LoadString(g_hinst, IDS_SUMMARY_UNDERLINE, szLine, ARRAYSIZE(szLine));
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
        }
        else
        {
            // Not connecting to the Internet - display nothing
        }
    }

    // Home network settings:
    LoadString(g_hinst, IDS_SUMMARY_HNETSETTINGS, szLine, ARRAYSIZE(szLine));
    _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);

    if (_hnetInfo.dwFlags & HNET_SETCOMPUTERNAME)
    {
        // Computer description:\t%1
        if (FormatMessageString(IDS_SUMMARY_COMPDESC, szLine, ARRAYSIZE(szLine), _hnetInfo.szComputerDescription))
        {
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
        }

        // Computer name:\t%1
        if (FormatMessageString(IDS_SUMMARY_COMPNAME, szLine, ARRAYSIZE(szLine), _hnetInfo.szComputer))
        {
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
        }
    }

    if (_hnetInfo.dwFlags & HNET_SETWORKGROUPNAME)
    {
        // Workgroup name:\t%1
        if (FormatMessageString(IDS_SUMMARY_WORKGROUP, szLine, ARRAYSIZE(szLine), _hnetInfo.szWorkgroup))
        {
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
        }
    }

    // The Shared Documents folder and any printers connected to this computer have been shared.
    _AddLineToBuffer(L"", szText, ARRAYSIZE(szText), &iChar);
    LoadString(g_hinst, IDS_SUMMARY_SHARING, szLine, ARRAYSIZE(szLine));
    _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
    LoadString(g_hinst, IDS_SUMMARY_UNDERLINE, szLine, ARRAYSIZE(szLine));
    _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);

    if ((_hnetInfo.prgncInternal) && (_hnetInfo.cncInternal))
    {
        if (_hnetInfo.cncInternal > 1)
        {
            // Bridged connections:\r\n
            LoadString(g_hinst, IDS_SUMMARY_BRIDGESETTINGS, szLine, ARRAYSIZE(szLine));
            _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);

            // Now list the connections...
            for (DWORD i = 0; i < _hnetInfo.cncInternal; i++)
            {
                NETCON_PROPERTIES* pncprops;
                HRESULT hr = _hnetInfo.prgncInternal[i]->GetProperties(&pncprops);
                if (SUCCEEDED(hr))
                {
                    _AddLineToBuffer(pncprops->pszwName, szText, ARRAYSIZE(szText), &iChar);
                    NcFreeNetconProperties(pncprops);
                }
            }
        }
        else // Single internal connection case
        {
            // Home network connection:\t%1
            NETCON_PROPERTIES* pncprops;
            HRESULT hr = _hnetInfo.prgncInternal[0]->GetProperties(&pncprops);
            if (SUCCEEDED(hr))
            {
                if (FormatMessageString(IDS_SUMMARY_HOMENETCON, szLine, ARRAYSIZE(szLine), pncprops->pszwName))
                {
                    _AddLineToBuffer(szLine, szText, ARRAYSIZE(szText), &iChar);
                }

                NcFreeNetconProperties(pncprops);
            }
        }
    }

    ASSERT(iChar < ARRAYSIZE(szText));

    UINT iTabDistance = 150;
    SendDlgItemMessage(hwnd, IDC_CHANGELIST, EM_SETTABSTOPS, (WPARAM) 1, (LPARAM) &iTabDistance);
    SetDlgItemText(hwnd, IDC_CHANGELIST, szText);
}

INT_PTR CHomeNetworkWizard::FinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            pthis->WelcomeSetTitleFont(hwnd);
            pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC), IDC_HELPLINK, IDS_HELP_SHARING);
            pthis->ReplaceStaticWithLink(GetDlgItem(hwnd, IDC_HELPSTATIC2), IDC_HELPLINK2, IDS_HELP_SHAREDDOCS);
            return TRUE;
        }

    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_CancelToClose(GetParent(hwnd));
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_FINISH);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZFINISH:
                return TRUE;
            case NM_CLICK:
            case NM_RETURN:
                switch ((int) wParam)
                {
                    case IDC_HELPLINK:
                        // help on sharing
                        {
                            if (IsOS(OS_PERSONAL))
                            {
                                HelpCenter(hwnd, L"filefold.chm%3A%3A/sharing_files_overviewP.htm");
                            }
                            else
                            {
                                HelpCenter(hwnd, L"filefold.chm%3A%3A/sharing_files_overviewW.htm");
                            }
                        }
                        return TRUE;
                    case IDC_HELPLINK2:
                        // help on Shared Documents
                        {
                            HelpCenter(hwnd, L"filefold.chm%3A%3A/windows_shared_documents.htm");
                        }
                        return TRUE;
                }
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::ErrorFinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->WelcomeSetTitleFont(hwnd);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_FINISH);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZFINISH:
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}


INT_PTR CHomeNetworkWizard::NoHardwareFinishPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        {
            pthis->WelcomeSetTitleFont(hwnd);
            return TRUE;
        }
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK);
                return TRUE;
            case PSN_WIZBACK:
                SetWindowLongPtr(hwnd, DWLP_MSGRESULT, pthis->PopPage());
                return TRUE;
            case PSN_WIZFINISH:
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

INT_PTR CHomeNetworkWizard::CantRunWizardPageProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = GetThis(hwnd, uMsg, wParam, lParam);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pthis->WelcomeSetTitleFont(hwnd);
        return TRUE;
    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR) lParam;

            switch (pnmh->code)
            {
            case PSN_SETACTIVE:
                PropSheet_SetWizButtons(pnmh->hwndFrom, 0);
                return TRUE;
            }
        }
        return FALSE;
    }

    return FALSE;
}

HRESULT GetConnections(HDPA* phdpa)
{
    HRESULT hr = S_OK;
    *phdpa = DPA_Create(5);
    if (*phdpa)
    {
        // Initialize the net connection enumeration
        INetConnectionManager* pmgr;

        hr = CoCreateInstance(CLSID_ConnectionManager, NULL, CLSCTX_SERVER | CLSCTX_NO_CODE_DOWNLOAD,
            IID_PPV_ARG(INetConnectionManager, &pmgr));

        if (SUCCEEDED(hr))
        {
            hr = SetProxyBlanket(pmgr);

            if (SUCCEEDED(hr))
            {
                IEnumNetConnection* penum;
                hr = pmgr->EnumConnections(NCME_DEFAULT, &penum);

                if (SUCCEEDED(hr))
                {
                    hr = SetProxyBlanket(penum);

                    if (SUCCEEDED(hr))
                    {
                        // Fill in our DPA will the connections
                        hr = penum->Reset();
                        while (S_OK == hr)
                        {
                            INetConnection* pnc;
                            ULONG ulISuck;
                            hr = penum->Next(1, &pnc, &ulISuck);

                            if (S_OK == hr)
                            {
                                hr = SetProxyBlanket(pnc);

                                if (SUCCEEDED(hr))
                                {
                                    if (-1 != DPA_AppendPtr(*phdpa, pnc))
                                    {
                                        pnc->AddRef();
                                    }
                                    else
                                    {
                                        hr = E_OUTOFMEMORY;
                                    }
                                }

                                pnc->Release();
                            }
                        }
                    }

                    penum->Release();
                }
            }
            pmgr->Release();
        }

        if (FAILED(hr))
        {
            DPA_DestroyCallback(*phdpa, FreeConnectionDPACallback, NULL);
            *phdpa = NULL;
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    return hr;
}

CHomeNetworkWizard* CHomeNetworkWizard::GetThis(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CHomeNetworkWizard* pthis = NULL;

    if (uMsg == WM_INITDIALOG)
    {
        PROPSHEETPAGE* psp = (PROPSHEETPAGE*) lParam;
        pthis = (CHomeNetworkWizard*) psp->lParam;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pthis);
    }
    else
    {
        pthis = (CHomeNetworkWizard*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }

    return pthis;
}


// Utility functions
HRESULT GetConnectionsFolder(IShellFolder** ppsfConnections)
{
    LPITEMIDLIST pidlFolder;
    HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_CONNECTIONS, &pidlFolder);

    if (SUCCEEDED(hr))
    {
        IShellFolder* pshDesktop;
        hr = SHGetDesktopFolder(&pshDesktop);

        if (SUCCEEDED(hr))
        {
            hr = pshDesktop->BindToObject(pidlFolder, NULL, IID_PPV_ARG(IShellFolder, ppsfConnections));

    

#if 0
            /*
            We need to do an IEnumIDList::Reset to set up internal data structures so that ::ParseDisplayName
            works later. Remove this once DaveA's stuff gets in the desktop build. TODO
            */
            if (SUCCEEDED(hr))
            {
                IEnumIDList* penum;
                hr = (*ppsfConnections)->EnumObjects(NULL, SHCONTF_NONFOLDERS | SHCONTF_FOLDERS, &penum);

                if (SUCCEEDED(hr))
                {
                    penum->Reset();
                    penum->Release();
                }
            }
#endif

            pshDesktop->Release();
        }

        ILFree(pidlFolder);
    }

    return hr;
}

void W9xGetNetTypeName(BYTE bNicType, WCHAR* pszBuff, UINT cchBuff)
{
    COMPILETIME_ASSERT(IDS_NETTYPE_START == IDS_NETTYPE_LAN);
    COMPILETIME_ASSERT(IDS_NETTYPE_LAN    - IDS_NETTYPE_START == NETTYPE_LAN);
    COMPILETIME_ASSERT(IDS_NETTYPE_DIALUP - IDS_NETTYPE_START == NETTYPE_DIALUP);
    COMPILETIME_ASSERT(IDS_NETTYPE_IRDA   - IDS_NETTYPE_START == NETTYPE_IRDA);
    COMPILETIME_ASSERT(IDS_NETTYPE_PPTP   - IDS_NETTYPE_START == NETTYPE_PPTP);
    COMPILETIME_ASSERT(IDS_NETTYPE_TV     - IDS_NETTYPE_START == NETTYPE_TV);
    COMPILETIME_ASSERT(IDS_NETTYPE_ISDN   - IDS_NETTYPE_START == NETTYPE_ISDN);
    COMPILETIME_ASSERT(IDS_NETTYPE_LAN    - IDS_NETTYPE_START == NETTYPE_LAN);

    if (bNicType >= NETTYPE_LAN && bNicType <= NETTYPE_ISDN)
    {
        LoadString(g_hinst, IDS_NETTYPE_START + bNicType, pszBuff, cchBuff);
    }
    else
    {
        LoadString(g_hinst, IDS_NETTYPE_UNKNOWN, pszBuff, cchBuff);
    }

    return;
}

BOOL W9xIsValidAdapter(const NETADAPTER* pNA, DWORD dwFlags)
{
    BOOL fRet = FALSE;

    if (dwFlags & CONN_EXTERNAL)
    {
        fRet = (pNA->bError      == NICERR_NONE &&
                pNA->bNetType    == NETTYPE_LAN &&
                pNA->bNetSubType != SUBTYPE_ICS &&
                pNA->bNetSubType != SUBTYPE_AOL &&
                pNA->bNicType    != NIC_1394       );
    }
    else if (dwFlags & CONN_INTERNAL)
    {
        fRet = (pNA->bError      == NICERR_NONE &&
                pNA->bNetType    == NETTYPE_LAN &&
                pNA->bNetSubType != SUBTYPE_ICS &&
                pNA->bNetSubType != SUBTYPE_AOL    );
    }
/*    else if ( dwFlags & CONN_UNPLUGGED )
    {
        if ( IsOS(OS_MILLENNIUM) )
        {
            fRet = IsAdapterDisconnected( (void*)pNA );
        }
    }
*/

    return fRet;
}

BOOL W9xIsAdapterDialUp(const NETADAPTER* pAdapter)
{
    return (pAdapter->bNetType == NETTYPE_DIALUP && pAdapter->bNetSubType == SUBTYPE_NONE);
}



HRESULT CHomeNetworkWizard::GetConnectionIconIndex(GUID& guidConnection, IShellFolder* psfConnections, int* pIndex)
{
    *pIndex = -1;
    OLECHAR szGUID[40];
    HRESULT hr = E_FAIL;
    if (StringFromGUID2(guidConnection, szGUID, ARRAYSIZE(szGUID)))
    {
        LPITEMIDLIST pidlConn = NULL;
        ULONG cchEaten = 0;
        hr = psfConnections->ParseDisplayName(NULL, NULL, szGUID, &cchEaten, &pidlConn, NULL);
        
        if (SUCCEEDED(hr))
        {
            IExtractIconW *pExtractIconW;
            LPCITEMIDLIST pcidl = pidlConn;

            hr = psfConnections->GetUIObjectOf(NULL, 1, &pcidl, IID_IExtractIconW, 0, (LPVOID *)(&pExtractIconW));
            if (SUCCEEDED(hr))
            {
                WCHAR szIconLocation[MAX_PATH];
                INT iIndex;
                UINT wFlags;

                hr = pExtractIconW->GetIconLocation(GIL_FORSHELL, szIconLocation, MAX_PATH, &iIndex, &wFlags);

                if (SUCCEEDED(hr))
                {
                    HICON hIconLarge;
                    HICON hIconSmall;

                    hr = pExtractIconW->Extract(szIconLocation, iIndex, &hIconLarge, &hIconSmall, 0x00100010);
                    if (SUCCEEDED(hr))
                    {
                        *pIndex = ImageList_AddIcon(_himlSmall, hIconSmall);
                    }
                    DestroyIcon(hIconLarge);
                    DestroyIcon(hIconSmall);
                }
            }

            if(pExtractIconW != NULL)
                pExtractIconW->Release();

            ILFree(pidlConn);
        }
    }

    return hr;
}

HRESULT GetDriveNameAndIconIndex(LPWSTR pszDrive, LPWSTR pszDisplayName, DWORD cchDisplayName, int* pIndex)
{
    SHFILEINFO fi = {0};
    
    if (SHGetFileInfoW_NT(pszDrive, 0, &fi, sizeof (fi), SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX))
    {
        *pIndex = fi.iIcon;
        lstrcpyn(pszDisplayName, fi.szDisplayName, cchDisplayName);
        return S_OK;
    }

    return E_FAIL;
}

BOOL IsEqualConnection(INetConnection* pnc1, INetConnection* pnc2)
{
    BOOL fEqual = FALSE;

    if ((pnc1) && (pnc2))
    {
        NETCON_PROPERTIES *pprops1, *pprops2;

        if (SUCCEEDED(pnc1->GetProperties(&pprops1)))
        {
            if (SUCCEEDED(pnc2->GetProperties(&pprops2)))
            {
                fEqual = (pprops1->guidId == pprops2->guidId);

                NcFreeNetconProperties(pprops2);
            }

            NcFreeNetconProperties(pprops1);
        }
    }

    return fEqual;
}

HRESULT ShareAllPrinters()
{
    PRINTER_ENUM* pPrinters;
    int nPrinters = MyEnumLocalPrinters(&pPrinters);

    if (nPrinters)
    {
        int iPrinterNumber = 1;
        for (int iPrinter = 0; iPrinter < nPrinters; iPrinter ++)
        {
            TCHAR szShare[NNLEN + 1];

            do
            {
                FormatMessageString(IDS_PRINTER, szShare, ARRAYSIZE(szShare), iPrinterNumber);
                if (1 == iPrinterNumber)
                {
                    szShare[lstrlen(szShare) - 1] = 0;
                    // Remove the "1" from the end since this is the first printer
                    // ie: "Printer1" --> "Printer"
                }

                if (!g_fRunningOnNT)
                {
                    CharUpper(szShare);
                }

                iPrinterNumber ++;
            } while (IsShareNameInUse(szShare));

            if (SharePrinter(pPrinters[iPrinter].pszPrinterName, szShare, NULL))
            {
                g_logFile.Write("Shared Printer: ");
            }
            else
            {
                g_logFile.Write("Failed to share Printer: ");
            }

            g_logFile.Write(pPrinters[iPrinter].pszPrinterName);
            g_logFile.Write("\r\n");
        }

        free(pPrinters);
    }

    return S_OK;
}

BOOL AllPlatformGetComputerName(LPWSTR pszName, DWORD cchName)
{
    if (g_fRunningOnNT)
    {
        return GetComputerNameExW_NT(ComputerNamePhysicalNetBIOS, pszName, &cchName);
    }
    else
    {
        return GetComputerName(pszName, &cchName);
    }
}

BOOL _IsTCPIPAvailable(void)
{
    BOOL fTCPIPAvailable = FALSE;
    HKEY hk;
    DWORD dwSize;

    // we check to see if the TCP/IP stack is installed and which object it is
    // bound to, this is a string, we don't check the value only that the
    // length is non-zero.

    if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                       TEXT("System\\CurrentControlSet\\Services\\Tcpip\\Linkage"),
                                       0x0,
                                       KEY_QUERY_VALUE, &hk) )
    {
        if ( ERROR_SUCCESS == RegQueryValueEx(hk, TEXT("Export"), 0x0, NULL, NULL, &dwSize) )
        {
            if ( dwSize > 2 )
            {
                fTCPIPAvailable = TRUE;
            }
        }
        RegCloseKey(hk);
    }

    return (fTCPIPAvailable);
}

BOOL AllPlatformSetComputerName(LPCWSTR pszComputerName)
{
    // NetBIOS computer name is set even on NT for completeness
    BOOL fSuccess;

    if (g_fRunningOnNT)
    {
        if (_IsTCPIPAvailable())
        {
            fSuccess = SetComputerNameExW_NT(ComputerNamePhysicalDnsHostname, pszComputerName);
        }
        else
        {
            fSuccess = SetComputerNameExW_NT(ComputerNamePhysicalNetBIOS, pszComputerName);
        }
    }
    else
    {
        // Windows 9x
        fSuccess = SetComputerName(pszComputerName);
    }

    return fSuccess;
}

BOOL SetComputerNameIfNecessary(LPCWSTR pszComputerName, BOOL* pfRebootRequired)
{
    g_logFile.Write("Attempting to set computer name\r\n");

    WCHAR szOldComputerName[LM20_CNLEN + 1];
    AllPlatformGetComputerName(szOldComputerName, ARRAYSIZE(szOldComputerName));

    if (0 != StrCmpIW(szOldComputerName, pszComputerName))
    {
        if (AllPlatformSetComputerName(pszComputerName))
        {
            g_logFile.Write("Computer name set successfully: ");
            g_logFile.Write(pszComputerName);
            g_logFile.Write("\r\n");
            *pfRebootRequired = TRUE;
        }
        else
        {
            g_logFile.Write("Computer name set failed.\r\n");
            return FALSE;
        }
    }
    else
    {
        g_logFile.Write("Old computer name is the same as new computer name - not setting.\r\n");
    }

    return TRUE;
}

BOOL SetComputerDescription(LPCWSTR pszComputerDescription)
{
    BOOL fRet;

    if (g_fRunningOnNT)
    {
        g_logFile.Write("Setting server description (comment): ");
        g_logFile.Write(pszComputerDescription);
        g_logFile.Write("\r\n");

        // Set comment (for now, NT only) win9x - TODO
        SERVER_INFO_1005_NT sv1005;
        sv1005.sv1005_comment = const_cast<LPWSTR>(pszComputerDescription);
        fRet = (NERR_Success == NetServerSetInfo_NT(NULL, 1005, (LPBYTE) &sv1005, NULL));
    }
    else
    {
        CRegistry reg;
        fRet = reg.OpenKey(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\VxD\\VNETSUP");
        if (fRet)
        {
            fRet = reg.SetStringValue(L"Comment", pszComputerDescription);
        }
    }

    return fRet;
}

HRESULT ShareWellKnownFolders(PHOMENETSETUPINFO pInfo)
{
    if (!NetConn_IsSharedDocumentsShared())
    {
        NetConn_CreateSharedDocuments(NULL, g_hinst, NULL, 0);

        if (NetConn_IsSharedDocumentsShared())
        {
            g_logFile.Write("'Shared Documents' shared.\r\n");
        }
        else
        {
            g_logFile.Write("Failed to share 'Shared Documents'\r\n");
            return E_FAIL;
        }
    }

    return S_OK;
}

HRESULT SetProxyBlanket(IUnknown * pUnk)
{
    HRESULT hr;
    hr = CoSetProxyBlanket (
            pUnk,
            RPC_C_AUTHN_WINNT,      // use NT default security
            RPC_C_AUTHZ_NONE,       // use NT default authentication
            NULL,                   // must be null if default
            RPC_C_AUTHN_LEVEL_CALL, // call
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL,                   // use process token
            EOAC_NONE);

    if(SUCCEEDED(hr))
    {
        IUnknown * pUnkSet = NULL;
        hr = pUnk->QueryInterface(IID_PPV_ARG(IUnknown, &pUnkSet));
        if(SUCCEEDED(hr))
        {
            hr = CoSetProxyBlanket (
                    pUnkSet,
                    RPC_C_AUTHN_WINNT,      // use NT default security
                    RPC_C_AUTHZ_NONE,       // use NT default authentication
                    NULL,                   // must be null if default
                    RPC_C_AUTHN_LEVEL_CALL, // call
                    RPC_C_IMP_LEVEL_IMPERSONATE,
                    NULL,                   // use process token
                    EOAC_NONE);
            pUnkSet->Release();
        }
    }
    return hr;
}

HRESULT WriteSetupInfoToRegistry(PHOMENETSETUPINFO pInfo)
{
    HKEY hkey;
    if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szAppRegKey, 0, NULL, 0, KEY_WRITE, NULL, &hkey, NULL))
    {
       // Write information telling the home network wizard to run silently on next boot, and what to share
       DWORD dwRun = 1;
       RegSetValueEx(hkey, TEXT("RunWizardFromRegistry"), NULL, REG_DWORD, (CONST BYTE*) &dwRun, sizeof (dwRun));
       RegCloseKey(hkey);
    }

    // Add a runonce entry for this wizard
    TCHAR szProcess[MAX_PATH];
    if (0 != GetModuleFileName(NULL, szProcess, ARRAYSIZE(szProcess)))
    {
        TCHAR szModule[MAX_PATH];
        if (0 != GetModuleFileName(g_hinst, szModule, ARRAYSIZE(szModule)))
        {
            const TCHAR szRunDllFormat[] = TEXT("%s %s,HomeNetWizardRunDll");
            TCHAR szRunDllLine[MAX_PATH];
            if (0 < wnsprintf(szRunDllLine, ARRAYSIZE(szRunDllLine), szRunDllFormat, szProcess, szModule))
            {
                if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, 0, NULL, 0, KEY_WRITE, NULL, &hkey, NULL))
                {
                    RegSetValueEx(hkey, TEXT("Network Setup Wizard"), 0, REG_SZ, (LPBYTE) szRunDllLine, ARRAYSIZE(szRunDllLine));
                    RegCloseKey(hkey);
                }
            }
        }
    }

   return S_OK;
}

HRESULT DeleteSetupInfoFromRegistry()
{
    RegDeleteKeyAndSubKeys(HKEY_LOCAL_MACHINE, c_szAppRegKey);
    return S_OK;
}

HRESULT ReadSetupInfoFromRegistry(PHOMENETSETUPINFO pInfo)
{
    BOOL fRunFromRegistry = FALSE;

    HKEY hkey;
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szAppRegKey, 0, KEY_READ, &hkey))
    {
        DWORD dwType;
        DWORD cbData = sizeof (fRunFromRegistry);
        if (ERROR_SUCCESS != RegQueryValueEx(hkey, TEXT("RunWizardFromRegistry"), NULL, &dwType, (LPBYTE) &fRunFromRegistry, &cbData))
        {
            fRunFromRegistry = FALSE;
        }

        RegCloseKey(hkey);
    }

    // S_FALSE indicates we're not running silently from infromation in the registry.
    return fRunFromRegistry ? S_OK : S_FALSE;
}

HRESULT MakeUniqueShareName(LPCTSTR pszBaseName, LPTSTR pszUniqueName, DWORD cchName)
{
    if (!IsShareNameInUse(pszBaseName))
    {
        lstrcpyn(pszUniqueName, pszBaseName, cchName);
        return S_OK;
    }
    else
    {
        int i = 2;
        while (TRUE)
        {
            if (0 > wnsprintf(pszUniqueName, cchName, TEXT("%s%d"), pszBaseName, i))
            {
                return E_FAIL;
            }

            if (!IsShareNameInUse(pszUniqueName))
            {
                return S_OK;
            }
        }
    }
}

// Pass NULL as TokenHandle to see if thread token is admin
HRESULT IsUserLocalAdmin(HANDLE TokenHandle, BOOL* pfIsAdmin)
{
    if (g_fRunningOnNT)
    {
        // First we must check if the current user is a local administrator; if this is
        // the case, our dialog doesn't even display

        PSID psidAdminGroup = NULL;
        SID_IDENTIFIER_AUTHORITY security_nt_authority = SECURITY_NT_AUTHORITY;

        BOOL fSuccess = ::AllocateAndInitializeSid_NT(&security_nt_authority, 2,
                                                   SECURITY_BUILTIN_DOMAIN_RID,
                                                   DOMAIN_ALIAS_RID_ADMINS,
                                                   0, 0, 0, 0, 0, 0,
                                                   &psidAdminGroup);
        if (fSuccess)
        {
            // See if the user for this process is a local admin
            fSuccess = CheckTokenMembership_NT(TokenHandle, psidAdminGroup, pfIsAdmin);
            FreeSid_NT(psidAdminGroup);
        }

        return fSuccess ? S_OK:E_FAIL;
    }
    else
    {
        // Win9x - every user is an admin
        *pfIsAdmin = TRUE;
        return S_OK;
    }
}

HRESULT GetConnectionByGUID(HDPA hdpaConnections, const GUID* pguid, INetConnection** ppnc)
{
    *ppnc = NULL;
    DWORD nItems = DPA_GetPtrCount(hdpaConnections);
    HRESULT hr = E_FAIL;

    if (nItems)
    {
        DWORD iItem = 0;
        while (iItem < nItems)
        {
            INetConnection* pnc = (INetConnection*) DPA_GetPtr(hdpaConnections, iItem);
            if (pnc)
            {
                GUID guidMatch;
                hr = GetConnectionGUID(pnc, &guidMatch);
                if (SUCCEEDED(hr))
                {
                    if (*pguid == guidMatch)
                    {
                        *ppnc = pnc;
                        (*ppnc)->AddRef();
                        break;
                    }
                }
                // Don't pnc->Release() - its coming from the DPA
            }

            iItem ++;
        }

        if (iItem == nItems)
        {
            // We searched and didn't find
            hr = E_FAIL;
        }
    }

    return hr;
}

HRESULT GUIDsToConnections(PHOMENETSETUPINFO pInfo)
{
    ASSERT(NULL == pInfo->prgncInternal);
    ASSERT(NULL == pInfo->pncExternal);

    HDPA hdpaConnections;
    HRESULT hr = GetConnections(&hdpaConnections);
    if (SUCCEEDED(hr))
    {
        // Get internal connections by GUID (allocate an extra one for null-terminated array, as elsewhere)
        pInfo->prgncInternal = (INetConnection**) LocalAlloc(LPTR, (pInfo->cguidInternal + 1) * sizeof (INetConnection*));

        if (pInfo->prgncInternal)
        {
            DWORD iConnection = 0;
            while ((iConnection < pInfo->cguidInternal) && SUCCEEDED(hr))
            {
                hr = GetConnectionByGUID(hdpaConnections, pInfo->prgguidInternal + iConnection, pInfo->prgncInternal + iConnection);
                iConnection++;
            }

            pInfo->cncInternal = iConnection;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }

        if (SUCCEEDED(hr) && (GUID_NULL != pInfo->guidExternal))
        {
            // Get external connection
            hr = GetConnectionByGUID(hdpaConnections, &(pInfo->guidExternal), &(pInfo->pncExternal));
        }

        DPA_DestroyCallback(hdpaConnections, FreeConnectionDPACallback, NULL);
        hdpaConnections = NULL;
    }

    if (FAILED(hr))
    {
        FreeExternalConnection(pInfo);
        FreeInternalConnections(pInfo);
    }

    pInfo->guidExternal = GUID_NULL;
    FreeInternalGUIDs(pInfo);

    return hr;
}

HRESULT GetConnectionGUID(INetConnection* pnc, GUID* pguid)
{
    NETCON_PROPERTIES* pncprops;
    HRESULT hr = pnc->GetProperties(&pncprops);
    if (SUCCEEDED(hr))
    {
        *pguid = pncprops->guidId;
        NcFreeNetconProperties(pncprops);
    }

    return hr;
}

HRESULT ConnectionsToGUIDs(PHOMENETSETUPINFO pInfo)
{
    HRESULT hr = S_OK;
    ASSERT(NULL == pInfo->prgguidInternal);
    ASSERT(GUID_NULL == pInfo->guidExternal);

    // Allocate the private connection guid array
    if (pInfo->cncInternal)
    {
        pInfo->prgguidInternal = (GUID*) LocalAlloc(LPTR, pInfo->cncInternal * sizeof (GUID));
        if (pInfo->prgguidInternal)
        {
            // Get each connection's GUID and fill in the array
            DWORD i = 0;
            while ((i < pInfo->cncInternal) && (SUCCEEDED(hr)))
            {
                hr = GetConnectionGUID(pInfo->prgncInternal[i], &(pInfo->prgguidInternal[i]));

                i++;
            }

            pInfo->cguidInternal = i;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        if (pInfo->pncExternal)
        {
            hr = GetConnectionGUID(pInfo->pncExternal, &(pInfo->guidExternal));
        }
    }

    if (FAILED(hr) && pInfo->prgguidInternal)
    {
        FreeInternalGUIDs(pInfo);
        pInfo->guidExternal = GUID_NULL;
    }

    FreeExternalConnection(pInfo);
    FreeInternalConnections(pInfo);

    return hr;
}


HRESULT ConfigureHomeNetwork(PHOMENETSETUPINFO pInfo)
{
    HRESULT hr = E_FAIL;

    if (pInfo->fAsync)
    {
        if (g_fRunningOnNT)
        {
            // Bundle up NT specific data for cross-thread
            hr = ConnectionsToGUIDs(pInfo);
        }
        else
        {
            // TODO: Anything necessary on win9x
            hr = S_OK;
        }

        if (SUCCEEDED(hr))
        {
            if (SHCreateThread(ConfigureHomeNetworkThread, pInfo, CTF_COINIT, NULL))
            {
                hr = S_FALSE;
            }
            else
            {
                hr = E_FAIL;
            }
        }
    }
    else
    {
        hr = ConfigureHomeNetworkSynchronous(pInfo);
    }

    return hr;
}

DWORD WINAPI ConfigureHomeNetworkThread(void* pvData)
{
    PHOMENETSETUPINFO pInfo = (PHOMENETSETUPINFO) pvData;

    // Before creating this thread, the caller MUST have freed his INetConnection*'s or
    // else the thread might touch/free them, which it must not do. Assert this.
    ASSERT(NULL == pInfo->pncExternal);
    ASSERT(NULL == pInfo->prgncInternal);

    HRESULT hr;

    if (g_fRunningOnNT)
    {
        // Unbundle data after crossing the thread boundary
        hr = GUIDsToConnections(pInfo);
    }
    else
    {
        // TODO: Anything necessary for Win9x
        hr = S_OK;
    }

    if (SUCCEEDED(hr))
    {
        hr = ConfigureHomeNetworkSynchronous(pInfo);

        FreeExternalConnection(pInfo);
        FreeInternalConnections(pInfo);

        if ((pInfo->hwnd) && (pInfo->umsgAsyncNotify))
        {
            // The HRESULT from the configuration is passed in WPARAM
            PostMessage(pInfo->hwnd, pInfo->umsgAsyncNotify, (WPARAM) hr, 0);
        }
    }

    return 0;
}

HRESULT ConfigureHomeNetworkSynchronous(PHOMENETSETUPINFO pInfo)
{
    g_logFile.Initialize("%systemroot%\\nsw.log");

    HRESULT hr = S_OK;

#ifdef NO_CONFIG
    g_logFile.Write("UI Test only - no configuration\r\n");
    g_logFile.Uninitialize();
    Sleep(2000);

#ifdef FAKE_REBOOTREQUIRED
    pInfo->fRebootRequired = TRUE;
#endif

    return S_OK;
#endif

    BOOL fInstallSharing = TRUE;
    BOOL fSharingAlreadyInstalled = IsSharingInstalled(TRUE);
    BOOL fInstalledWorkgroup = FALSE;

    // We don't need to install sharing unless we're sharing something
    if (!(pInfo->dwFlags & (HNET_SHAREFOLDERS | HNET_SHAREPRINTERS)))
    {
        g_logFile.Write("No file or printer sharing requested\r\n");
        fInstallSharing = FALSE;
    }

    // Worker function for the whole wizard

    // Computer name
    if ((pInfo->dwFlags & HNET_SETCOMPUTERNAME) && (*(pInfo->szComputer)))
    {
        SetComputerNameIfNecessary(pInfo->szComputer, &(pInfo->fRebootRequired));
        SetComputerDescription(pInfo->szComputerDescription);
    }

    // Workgroup name
    if ((pInfo->dwFlags & HNET_SETWORKGROUPNAME) && (*(pInfo->szWorkgroup)))
    {
        Install_SetWorkgroupName(pInfo->szWorkgroup, &(pInfo->fRebootRequired));

        fInstalledWorkgroup = TRUE;
    }

    // Install TCP/IP
    hr = InstallTCPIP(pInfo->hwnd, NULL, NULL);
    if (NETCONN_NEED_RESTART == hr)
    {
        pInfo->fRebootRequired = TRUE;
        hr = S_OK;
    }

    if (FAILED(hr))
    {
        g_logFile.Write("Failed to install TCP/IP\r\n");
    }

    // Install Client for Microsoft Networks
    // TODO: figure out what to do if NetWare client is installed!?!?
    hr = InstallMSClient(pInfo->hwnd, NULL, NULL);
    if (NETCONN_NEED_RESTART == hr)
    {
        pInfo->fRebootRequired = TRUE;
        hr = S_OK;
    }

    if (FAILED(hr))
    {
        g_logFile.Write("Failed to install Client for Microsoft Networks.\r\n");
    }

    // Install sharing
    if (fInstallSharing)
    {
        hr = InstallSharing(pInfo->hwnd, NULL, NULL);
        if (NETCONN_NEED_RESTART == hr)
        {
            pInfo->fRebootRequired = TRUE;
            hr = S_OK;
        }

        if (FAILED(hr))
        {
            g_logFile.Write("Failed to install File and Printer Sharing.\r\n");
        }
    }

    // TODO: What to do about share level vs. user level access control on windows 9x???

    // TODO: What to do about autodialing? Are we assuming this will already be done for us? 9x and NT? I think so!
    if ( g_fRunningOnNT )
    {
        // We only set autodial here if we are configuring an ICS Client
        // In the case that we explicitly set a public adapter then ConfigureICSBridgeFirewall
        // will set autodial if required.
        if ( pInfo->dwFlags & HNET_ICSCLIENT )
        {
            hr = HrSetAutodial( AUTODIAL_MODE_NO_NETWORK_PRESENT );
        }
    }
    else
    {
        if ( pInfo->ipaExternal != -1 )
        {
            if (W9xIsAdapterDialUp(&pInfo->pNA[LOWORD(pInfo->ipaExternal)]))  // Dialup adapter for connecting to internet
            {
                g_logFile.Write("Setting default dial-up connection to autodial.\r\n");
                SetDefaultDialupConnection((pInfo->pRas[HIWORD(pInfo->ipaExternal)]).szEntryName);
                EnableAutodial(TRUE);
            }
            else
            {
                g_logFile.Write("Disabling autodial since default connection isn't dial-up.\r\n");
                SetDefaultDialupConnection(NULL);
                EnableAutodial(FALSE);
            }
        }
    }

    // Configure ICS, the Bridge and the personal firewall
    if (g_fRunningOnNT)
    {
        hr = ConfigureICSBridgeFirewall(pInfo);
    }
    else
    {
        // ICS client or no internet connection.
        if ((pInfo->dwFlags & HNET_ICSCLIENT) || (pInfo->pNA && pInfo->ipaExternal == -1))
        {
            CICSInst* pICS = new CICSInst;
            if (pICS)
            {                    
                if (pICS->IsInstalled())
                {
                    pICS->m_option = ICS_UNINSTALL;

                    g_logFile.Write("Uninstalling ICS Client.\r\n");

                    pICS->DoInstallOption(&(pInfo->fRebootRequired), pInfo->ipaInternal);
                }

                delete pICS;
            }
            
            if ( (pInfo->dwFlags & HNET_ICSCLIENT) && (pInfo->pNA && pInfo->ipaInternal != -1))
            {
                UINT ipa;
                
                for ( ipa=0; ipa<pInfo->cNA; ipa++ )
                {
                    const NETADAPTER* pNA = &pInfo->pNA[ ipa ];
            
                    if ( W9xIsValidAdapter( pNA, CONN_INTERNAL ) && 
                        !W9xIsValidAdapter( pNA, CONN_UNPLUGGED ) )
                    {
                        HrEnableDhcp( (void*)pNA, HNW_ED_RELEASE|HNW_ED_RENEW );
                    }
                }
            }

            g_logFile.Write("Disabling autodial.\r\n");
            SetDefaultDialupConnection(NULL);
            EnableAutodial(FALSE);
        }
    }

    // NOTE: we might want to split HNET_SHAREFOLDERS out into two
    // bits: HNET_CREATESHAREDFOLDERS and HNET_SHARESHAREDFOLDERS
    //
    if (pInfo->dwFlags & (HNET_SHAREPRINTERS | HNET_SHAREFOLDERS))
    {
        // Due to domain/corporate security concerns, share things
        // iff we're setting up a workgroup, or we're already on one
        //
        BOOL fOnWorkgroup = fInstalledWorkgroup;
        if (!fOnWorkgroup)
        {
            if (g_fRunningOnNT)
            {
                LPTSTR pszDomain;
                NETSETUP_JOIN_STATUS njs;
                if (NERR_Success == NetGetJoinInformation(NULL, &pszDomain, &njs))
                {
                    NetApiBufferFree(pszDomain);

                    fOnWorkgroup = (NetSetupWorkgroupName == njs);
                }
            }
            else
            {
                fOnWorkgroup = TRUE;  // there may be some registry key we can check for this
            }
        }

        if (fOnWorkgroup)
        {
            EnableSimpleSharing();

            if (fSharingAlreadyInstalled)
            {
                if (pInfo->dwFlags & HNET_SHAREPRINTERS)
                {
                    ShareAllPrinters();
                }

                if (pInfo->dwFlags & HNET_SHAREFOLDERS)
                {
                    ShareWellKnownFolders(pInfo);
                }
            }
            else
            {
                // Write the sharing info to the registry - do required work on reboot
                g_logFile.Write("Sharing isn't installed. Will share folders and printers on reboot.\r\n");
                pInfo->fRebootRequired = TRUE;
                WriteSetupInfoToRegistry(pInfo);
            }
        }
    }

    if (pInfo->fRebootRequired)
    {
        g_logFile.Write("Reboot is required for changes to take effect.\r\n");
    }

    g_logFile.Uninitialize();

    // Kick off the netcrawler
    INetCrawler *pnc;
    if (SUCCEEDED(CoCreateInstance(CLSID_NetCrawler, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(INetCrawler, &pnc))))
    {
        pnc->Update(0x0);
        pnc->Release();
    }

#ifdef FAKE_REBOOTREQUIRED
    pInfo->fRebootRequired = TRUE;
#endif

    return hr;
}

void STDMETHODCALLTYPE ConfigurationLogCallback(LPCWSTR pszLogEntry, LPARAM lParam)
{
    g_logFile.Write(pszLogEntry);
}

typedef BOOL (APIENTRY* PFNNETSETUPICSUPGRADE)(BOOL);
HRESULT ConfigureICSBridgeFirewall(PHOMENETSETUPINFO pInfo)
{
    HRESULT hr = E_FAIL;

    // Call HNetSetShareAndBridgeSettings directly
    BOOLEAN fSharePublicConnection = (pInfo->pncExternal && (pInfo->dwFlags & HNET_SHARECONNECTION)) ? TRUE : FALSE;
    BOOLEAN fFirewallPublicConnection = (pInfo->pncExternal && (pInfo->dwFlags & HNET_FIREWALLCONNECTION)) ? TRUE : FALSE;

    if (fSharePublicConnection)
    {
        g_logFile.Write("Will attempt to share public connection.\r\n");
    }

    if (fFirewallPublicConnection)
    {
        g_logFile.Write("Will attempt to firewall public connection.\r\n");
    }

    HMODULE hHNetCfg = LoadLibrary(L"hnetcfg.dll");
    if (hHNetCfg)
    {
        INetConnection* pncPrivate = NULL;

        LPFNHNETSETSHAREANDBRIDGESETTINGS pfnHNetSetShareAndBridgeSettings

        = reinterpret_cast<LPFNHNETSETSHAREANDBRIDGESETTINGS>

            (GetProcAddress(hHNetCfg, "HNetSetShareAndBridgeSettings"));

        if (pfnHNetSetShareAndBridgeSettings)
        {
            hr = (*pfnHNetSetShareAndBridgeSettings)( pInfo->pncExternal,
                                                      pInfo->prgncInternal,
                                                      fSharePublicConnection,
                                                      fFirewallPublicConnection,
                                                      ConfigurationLogCallback,
                                                      0,
                                                      &pncPrivate );
            if (SUCCEEDED(hr))
            {
                if ( ( HNET_ICSCLIENT & pInfo->dwFlags ) &&
                    ( NULL == pInfo->prgncInternal[1] ) )
                {
                    HrEnableDhcp( pInfo->prgncInternal[0], HNW_ED_RELEASE|HNW_ED_RENEW );
                }
                
                // If we are sharing an external adapter then set WinInet settings to allow
                // for an existing connection created from ICS client traffic.
                
                if ( pInfo->pncExternal )
                {
                    hr = HrSetAutodial( AUTODIAL_MODE_NO_NETWORK_PRESENT );
                }
                
                if ( pncPrivate )
                {
                    pncPrivate->Release();
                }
            }
            else
            {
                g_logFile.Write("Adapter Configuration for Home Networking failed.\r\n");
            }
        }
        else
        {
            TraceMsg(TF_WARNING, "HNetCfg.DLL could not find HNetSetShareAndBridgeSettings");
        }

        FreeLibrary(hHNetCfg);
    }
    else
    {
        TraceMsg(TF_WARNING, "HNetCfg.DLL could not be loaded");
    }

    return hr;
}

BOOL MachineHasNetShares()
{
    SHARE_INFO* prgShares;
    int cShares = EnumLocalShares(&prgShares);
    
    // See if there are any file or print shares, which are the ones we care about
    BOOL fHasShares = FALSE;
    for (int i = 0; i < cShares; i++)
    {
        if ((STYPE_DISKTREE == prgShares[i].bShareType) ||
            (STYPE_PRINTQ   == prgShares[i].bShareType))
        {
            fHasShares = TRUE;
            break;
        }
    }
    NetApiBufferFree(prgShares);
    return fHasShares;
}


// Checks if guest access mode is enabled. If guest access mode is OFF but
// in the indeterminate state (ForceGuest is not set), and the m/c has no net shares,
// then we set ForceGuest to 1 and return TRUE.
//
// This indeterminate state occurs only on win2k->XP upgrade.

BOOL
EnsureGuestAccessMode(
    VOID
    )
{
    BOOL fIsGuestAccessMode = FALSE;

    if (IsOS(OS_PERSONAL))
    {
        // Guest mode is always on for Personal
        fIsGuestAccessMode = TRUE;
    }
    else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
    {
        LONG    ec;
        HKEY    hkey;

        // Professional, not in a domain. Check the ForceGuest value.

        ec = RegOpenKeyEx(
                    HKEY_LOCAL_MACHINE,
                    TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
                    0,
                    KEY_QUERY_VALUE | KEY_SET_VALUE,
                    &hkey
                    );

        if (ec == NO_ERROR)
        {
            DWORD dwValue;
            DWORD dwValueSize = sizeof(dwValue);

            ec = RegQueryValueEx(hkey,
                                 TEXT("ForceGuest"),
                                 NULL,
                                 NULL,
                                 (LPBYTE)&dwValue,
                                 &dwValueSize);

            if (ec == NO_ERROR)
            {
                if (1 == dwValue)
                {
                    // ForceGuest is already on
                    fIsGuestAccessMode = TRUE;
                }
            }
            else
            {
                // Value doesn't exist
                if (!MachineHasNetShares())
                {
                    // Machine has no shares
                    dwValue = 1;
                    ec = RegSetValueEx(hkey,
                                       TEXT("ForceGuest"),
                                       0,
                                       REG_DWORD,
                                       (BYTE*) &dwValue,
                                       sizeof (dwValue));

                    if (ec == NO_ERROR)
                    {
                        // Write succeeded - guest access mode is enabled
                        fIsGuestAccessMode = TRUE;
                    }
                }
            }

            RegCloseKey(hkey);
        }
    }

    return fIsGuestAccessMode;
}


// It is assumed the machine is not joined to a domain when this is called!
HRESULT EnableSimpleSharing()
{
    HRESULT hr = S_FALSE;

    if (EnsureGuestAccessMode())
    {
        ILocalMachine *pLM;
        hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILocalMachine, &pLM));

        if (SUCCEEDED(hr))
        {
            TraceMsg(TF_ALWAYS, "Enabling Guest Account");

            hr = pLM->EnableGuest(ILM_GUEST_NETWORK_LOGON);
            pLM->Release();

            SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
        }
    }

    return hr;    
}

#define INVALID_COMPUTERNAME_CHARS L" {|}~[\\]^':;<=>?@!\"#$%^`()+/*&"
#define INVALID_WORKGROUP_CHARS    L"{|}~[\\]^':;<=>?!\"#$%^`()+/*&"
#define INVALID_TRAILING_CHAR      L' '

BOOL IsValidNameSyntax(LPCWSTR pszName, NETSETUP_NAME_TYPE type)
{
    // Only support workgroup and machine - need to add new charsets if
    // required
    ASSERT(type == NetSetupWorkgroup || type == NetSetupMachine);

    LPCWSTR pszInvalid = (type == NetSetupWorkgroup) ? INVALID_WORKGROUP_CHARS : INVALID_COMPUTERNAME_CHARS;
    BOOL    fValid     = TRUE;
    WCHAR*  pch        = (LPWSTR) pszName;
    
    if ( *pch && ( NetSetupWorkgroup == type ) )
    {
        // remove trailing blanks

        WCHAR* pchLast = pch + wcslen(pch) - 1;
        
        while ( (INVALID_TRAILING_CHAR == *pchLast) && (pchLast >= pch) )
        {
            *pchLast = NULL;
            pchLast--;
        }
    }
    
    fValid = ( *pch ) ? TRUE : FALSE;

    while (*pch && fValid)
    {
        fValid = (NULL == StrChrW(pszInvalid, *pch));
        pch ++;
    }
    
    return fValid;
}

void BoldControl(HWND hwnd, int id)
{
    HWND hwndTitle = GetDlgItem(hwnd, id);

    // Get the existing font
    HFONT hfontOld = (HFONT) SendMessage(hwndTitle, WM_GETFONT, 0, 0);

    LOGFONT lf = {0};
    if (GetObject(hfontOld, sizeof(lf), &lf))
    {
        lf.lfWeight = FW_BOLD;

        HFONT hfontNew = CreateFontIndirect(&lf);
        if (hfontNew)
        {
            SendMessage(hwndTitle, WM_SETFONT, (WPARAM) hfontNew, FALSE);

            // Don't do this, its shared.
            // DeleteObject(hfontOld);
        }
    }
}

void ShowControls(HWND hwndParent, const int *prgControlIDs, DWORD nControls, int nCmdShow)
{
    for (DWORD i = 0; i < nControls; i++)
        ShowWindow(GetDlgItem(hwndParent, prgControlIDs[i]), nCmdShow);
}

void HelpCenter(HWND hwnd, LPCWSTR pszTopic)
{
    // use ShellExecuteExA for w98 compat.

    CHAR szURL[1024];
    wsprintfA(szURL, "hcp://services/layout/contentonly?topic=ms-its%%3A%%25help_location%%25\\%S", pszTopic);

    SHELLEXECUTEINFOA shexinfo = {0};
    shexinfo.cbSize = sizeof (shexinfo);
    shexinfo.fMask = SEE_MASK_FLAG_NO_UI;
    shexinfo.nShow = SW_SHOWNORMAL;
    shexinfo.lpFile = szURL;
    shexinfo.lpVerb = "open";

    // since help center doesn't properly call AllowSetForegroundWindow when it defers to an existing process we just give it to the next taker.
    
    HMODULE hUser32 = GetModuleHandleA("user32.dll");
    if(NULL != hUser32)
    {
        BOOL (WINAPI *pAllowSetForegroundWindow)(DWORD);
        
        pAllowSetForegroundWindow = reinterpret_cast<BOOL (WINAPI*)(DWORD)>(GetProcAddress(hUser32, "AllowSetForegroundWindow"));
        if(NULL != pAllowSetForegroundWindow)
        {
            pAllowSetForegroundWindow(-1);
        }
    }

    ShellExecuteExA(&shexinfo);
}

void CHomeNetworkWizard::ShowMeLink(HWND hwnd, LPCWSTR pszTopic)
{

    if (pfnShowHTMLDialog == NULL)
    {
        hinstMSHTML = LoadLibrary(TEXT("MSHTML.DLL"));

        if (hinstMSHTML)
        {
            pfnShowHTMLDialog = (SHOWHTMLDIALOGEXFN*)GetProcAddress(hinstMSHTML, "ShowHTMLDialogEx");
        }

        // can not find ShowHTMLDialog API.  Do nothing.
        if (pfnShowHTMLDialog == NULL)
            return;
    }

    WCHAR szURL[1024];
    HRESULT hr;
    VARIANT_BOOL isClosed = VARIANT_FALSE;

    // check to see if the dialog window is closed.  If so, release it so that a new one
    // will be created.
    if (showMeDlgWnd != NULL)
    {
        if (SUCCEEDED(showMeDlgWnd->get_closed(&isClosed)))
        { 
            if (isClosed == VARIANT_TRUE)
            {
                showMeDlgWnd->Release();
                showMeDlgWnd = NULL;

                if (pFrameWindow != NULL)
                {
                    pFrameWindow->Release();
                    pFrameWindow = NULL;
                }
            }
        }
        else
        {
            return;
        }
    }

    const char *helpLoc = getenv("help_location");
    
    LPWSTR lpszWinDir;     // pointer to system information string 
    WCHAR tchBuffer[MAX_PATH];  // buffer for concatenated string 

    // if unset use the default location.
    lpszWinDir = tchBuffer;
    GetWindowsDirectory(lpszWinDir, MAX_PATH);
    
    if (showMeDlgWnd == NULL)
    {    
        BSTR bstrFrameURL;        
        // need to create a new dialog window. 
        if (helpLoc != NULL)
            wnsprintfW(szURL, 1024, L"ms-its:%S\\ntart.chm::/hn_ShowMeFrame.htm", helpLoc);
        else
            wnsprintfW(szURL, 1024, L"ms-its:%s\\help\\ntart.chm::/hn_ShowMeFrame.htm", lpszWinDir);

        bstrFrameURL = SysAllocString((const LPCWSTR)szURL);

        if (bstrFrameURL == NULL)
            return;

        IMoniker * pURLMoniker = NULL;

        CreateURLMoniker(NULL, bstrFrameURL, &pURLMoniker);

        if (pURLMoniker != NULL)
        {
            VARIANT  varReturn;
            
            VariantInit(&varReturn);
            
            DWORD dwFlags = HTMLDLG_MODELESS | HTMLDLG_VERIFY;

            hr = (*pfnShowHTMLDialog)(
                    NULL, 
                    pURLMoniker,
                    dwFlags,
                    NULL, 
                    L"scroll:no;help:no;status:no;dialogHeight:394px;dialogWidth:591px;", 
                    &varReturn);

            if (SUCCEEDED(hr))
            {
                hr = V_UNKNOWN(&varReturn)->QueryInterface(__uuidof(IHTMLWindow2), (void**)&showMeDlgWnd);

            }    
            
            pURLMoniker->Release();
            VariantClear(&varReturn);
        }

        SysFreeString(bstrFrameURL);
    }

    // we don't have a dialog window to work with so quit silently.
    if (showMeDlgWnd == NULL)
    {
        return;
    }

    // we need get the frame window where the actual html page will be displayed.
    if (pFrameWindow == NULL)
    {
        VARIANT index;
        VARIANT frameOut;
        long frameLen = 0;

        VariantInit(&index);
        VariantInit(&frameOut);

        IHTMLFramesCollection2* pFramesCol = NULL;
        // we may not be able to get the frames the first time around.  So try some more.
        int i = 5;
        while (i-- > 0)
        {

            if(!SUCCEEDED(showMeDlgWnd->get_frames(&pFramesCol)))
            {
                // can not get frames. so quit.
                break;
            }
            else
                if (!SUCCEEDED(pFramesCol->get_length(&frameLen)))
                {
                    // can not determine how many frames it has.  so quit.
                    break;
                }
                else
                {
                    if (frameLen > 0)
                    {
                        V_VT(&index) = VT_I4;
                        V_I4(&index) = 0;
    
                        if (SUCCEEDED(pFramesCol->item(&index, &frameOut)))
                        {
                            if (V_VT(&frameOut) == VT_DISPATCH && V_DISPATCH(&frameOut) != NULL)
                            {
                                hr = V_DISPATCH(&frameOut)->QueryInterface(__uuidof(IHTMLWindow2), (void**)&pFrameWindow);

                            }    
                        }
                        // found at least one frame.  jump out of the loop.
                        break;
                    }
                }

            if (pFramesCol != NULL)
                pFramesCol->Release();

            Sleep(1000);
        }

        if (pFramesCol != NULL)
            pFramesCol->Release();

        VariantClear(&index);
        VariantClear(&frameOut);
    }
    
    if (pFrameWindow == NULL)
        return;

    // now to load in the actual html page    
    BSTR bstrURL;
    if (helpLoc != NULL)
        wnsprintf(szURL, 1024, L"ms-its:%S\\%s", helpLoc, pszTopic);
    else
        wnsprintf(szURL, 1024, L"ms-its:%s\\help\\%s", lpszWinDir, pszTopic);

    bstrURL = SysAllocString((const LPCWSTR)szURL);
    if (bstrURL == NULL)
        return;
    
    hr = pFrameWindow->navigate(bstrURL);
    hr = showMeDlgWnd->focus();
    
    SysFreeString(bstrURL);
}