#include "pch.h"
#pragma  hdrstop
#include "kkcwinf.h"
#include "ncstring.h"
#include "netcfgp.h"
#include "nsbase.h"
#include "kkutils.h" 
#include "ncnetcfg.h"
#include "lancmn.h"
#include "ncreg.h"
#include "ncsysprp.h"
#include "resource.h"

extern WCHAR c_szInfId_MS_TCPIP[]              = L"ms_tcpip";
extern WCHAR c_szInfId_MS_MSClient[]           = L"ms_msclient";
extern WCHAR c_szInfId_MS_NWClient[]           = L"ms_nwclient";

extern WCHAR c_szRegKeyAnswerFileMap[]         = L"SYSTEM\\Setup\\AnswerFileMap";

const WCHAR c_szParams_MS_TCPIP_Adapter01[]    = L"params.ms_tcpip_Adapter01";

static const WCHAR c_szNetConfigSysPrepAnswerFile[]   = L"\\$ncsp$.inf";

struct
{
    PCWSTR pwszId;
    PCWSTR pwszIdAdapterParamsSection;
} g_IdMap[] = {{c_szInfId_MS_TCPIP, c_szParams_MS_TCPIP_Adapter01},

                };

HRESULT HrSaveNetworkComponentsForSysPrep(INetCfg* pNetCfg);
HRESULT HrRestoreNetworkComponentsForSysPrep(INetCfg* pNetCfg);
HRESULT HrGetFirstAdapterInstanceGuid(INetCfg * pnc, BOOL fDuringSetup, GUID * pGuidAdapter);
BOOL    FSectionHasAtLeastOneKey(IN CWInfFile* pwifAnswerFile, IN PCWSTR pszSection);

inline HRESULT HrSetupSetFirstDword(IN HWIF   hwif,
                                    IN PCWSTR pwszSection,
                                    IN PCWSTR pwszKey,
                                    IN DWORD  dwValue)
{
    Assert (hwif && pwszSection && pwszKey);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);
    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddKey(pwszKey, dwValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

inline HRESULT HrSetupSetFirstString(IN HWIF   hwif,
                                     IN PCWSTR pwszSection,
                                     IN PCWSTR pwszKey,
                                     IN PCWSTR pwszValue)
{
    Assert (hwif && pwszSection && pwszKey && pwszValue);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);

    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddKey(pwszKey, pwszValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

inline HRESULT HrSetupSetFirstStringAsBool(IN HWIF   hwif,
                                           IN PCWSTR pwszSection,
                                           IN PCWSTR pwszKey,
                                           IN BOOL   fValue)
{
    Assert (hwif && pwszSection && pwszKey);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);

    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddBoolKey(pwszKey, fValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

HRESULT HrSetupSetFirstMultiSzField(IN HWIF   hwif,
                                    IN PCWSTR pwszSection,
                                    IN PCWSTR pwszKey, 
                                    IN PCWSTR pmszValue)
{
    HRESULT  hr = S_OK;

    Assert (hwif && pwszSection && pwszKey && pmszValue);

    TStringList slValues;
    MultiSzToColString(pmszValue, &slValues);
    if (slValues.empty())
    {

        return HrSetupSetFirstString(hwif, pwszSection, pwszKey, pmszValue);
    }
    else
    {
        CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);
        PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
        if (pwifSection)
        {
            pwifAnswerFile->GotoEndOfSection(pwifSection);
            pwifAnswerFile->AddKey(pwszKey, slValues);
        }
        else
            hr = S_FALSE;
    }
    EraseAndDeleteAll(&slValues);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstDword(
                                                    IN PCWSTR pwszSection, 
                                                    IN PCWSTR pwszKey, 
                                                    IN DWORD  dwValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstDword(
						m_hwif,
                        pwszSection,
						pwszKey,
						dwValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstString(
                                                     IN PCWSTR pwszSection, 
                                                     IN PCWSTR pwszKey, 
                                                     IN PCWSTR pwszValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstString(
						m_hwif,
                        pwszSection,
						pwszKey,
						pwszValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstStringAsBool(
                                                           IN PCWSTR pwszSection, 
                                                           IN PCWSTR pwszKey, 
                                                           IN BOOL   fValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstStringAsBool(
						m_hwif,
                        pwszSection,
						pwszKey,
						fValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstMultiSzField(
                                                           IN PCWSTR pwszSection, 
                                                           IN PCWSTR pwszKey, 
                                                           IN PCWSTR pmszValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstMultiSzField(
						m_hwif,
                        pwszSection,
						pwszKey,
						pmszValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

BOOL FNetSetupPrepareSysPrep()
{
    INetCfg* pNetCfg        = NULL;
    BOOL     fInitCom       = TRUE;
    HRESULT  hr;    

    DefineFunctionName("FNetSetupPrepareSysPrep");
    TraceFunctionEntry(ttidNetSetup);

    hr = HrCreateAndInitializeINetCfg(&fInitCom, &pNetCfg, 
                                       FALSE, 
                                       0,     
                                       L"Save Configuration for SysPrep",
                                       NULL);
    if (SUCCEEDED(hr))
    {

        if (! fInitCom)
        {
            TraceTag(ttidNetSetup, "%s: Failed to init COM", __FUNCNAME__);
            return FALSE;
        }

        hr = HrSaveNetworkComponentsForSysPrep(pNetCfg);
        if (hr == S_OK)
        {

            HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE, c_szRegKeyAnswerFileMap);
        }

        HrUninitializeAndReleaseINetCfg (fInitCom, pNetCfg, FALSE);
    }

    TraceError("FNetSetupPrepareSysPrep", hr);
    return (hr == S_OK)? TRUE : FALSE;   
}

HRESULT HrSaveNetworkComponentsForSysPrep(INetCfg* pNetCfg)
{
    HRESULT hr = S_OK;
    GUID    guidAdapter;         
    BOOL    fApplyWrite = FALSE; 
    BOOL    fRet = TRUE;

    DefineFunctionName("HrSaveNetworkComponentsForSysPrep");

    hr = HrGetFirstAdapterInstanceGuid(pNetCfg, FALSE, &guidAdapter); 
    if (hr != S_OK)
    {
        TraceTag(ttidNetSetup, "%s: HrGetFirstAdapterInstanceGuid failed 0x%08x", __FUNCNAME__, hr);
        return S_FALSE;
    }

    CWInfFile* pwifAnswerFile = new CWInfFile();

    if ((pwifAnswerFile == NULL) || (pwifAnswerFile->Init() == FALSE))
	{
	    AssertSz(FALSE,"HrSaveNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        if (pwifAnswerFile)
            delete pwifAnswerFile;
		return(E_OUTOFMEMORY);
	}

    CComObject<CNetCfgSysPrep>* pncsp = new CComObject<CNetCfgSysPrep>;

    if ((pncsp == NULL) || (pncsp->HrInit((HWIF) pwifAnswerFile) != S_OK))
	{
	    AssertSz(FALSE,"HrSaveNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        delete pwifAnswerFile;
        if (pncsp)
            delete pncsp;
		return(E_OUTOFMEMORY);
	}
    pncsp->AddRef(); 

    for (UINT nIdx = 0; nIdx < celems(g_IdMap); nIdx++)
    {
        INetCfgComponent* pINetCfgComponent;
        PCWSTR pwszInfId;
        PCWSTR pwszAdapterParamsSections;

        pwszInfId = g_IdMap[nIdx].pwszId;
        pwszAdapterParamsSections = g_IdMap[nIdx].pwszIdAdapterParamsSection;

        hr = pNetCfg->FindComponent(pwszInfId, &pINetCfgComponent);

        if (hr == S_OK)
        {
            Assert (pINetCfgComponent);

            INetCfgComponentPrivate* pComponentPrivate;
            hr = pINetCfgComponent->QueryInterface(
                        IID_INetCfgComponentPrivate,
                        reinterpret_cast<void**>(&pComponentPrivate));

            if (hr == S_OK)
            {
                INetCfgComponentSysPrep* pINetCfgComponentSysPrep;

                hr = pComponentPrivate->QueryNotifyObject(
                                IID_INetCfgComponentSysPrep, 
                                (void**) &pINetCfgComponentSysPrep);
                if (S_OK == hr)
                {

                    pwifAnswerFile->AddSection(pwszAdapterParamsSections);

                    hr = pINetCfgComponentSysPrep->SaveAdapterParameters(
                                reinterpret_cast<INetCfgSysPrep*>(pncsp), pwszAdapterParamsSections, &guidAdapter);
                    if (hr == S_OK)
                        fApplyWrite = TRUE;
                    ReleaseObj(pINetCfgComponentSysPrep);
                }
                else if (hr == E_NOINTERFACE)
                {
                    TraceTag(ttidNetSetup, "%s: %S component doesn't support IID_INetCfgComponentSysPrep", __FUNCNAME__, pwszInfId);
                }
                else
                    fRet = FALSE; 
                ReleaseObj(pComponentPrivate);
            }
            else
            {
                TraceTag(ttidNetSetup, "%s: can't find IID_INetCfgComponentPrivate for component %S", __FUNCNAME__, pwszInfId);
                fRet = FALSE;
            }

            ReleaseObj (pINetCfgComponent);
        }
        else
        {

            TraceTag(ttidNetSetup, "%s: Can't find %S component", __FUNCNAME__, pwszInfId);
        }
    } 

    pncsp->SetHWif(NULL); 
    ReleaseObj(pncsp);    

    if (fApplyWrite)
    {
        WCHAR wszSystemDir[MAX_PATH]; 

        if (GetSystemDirectory(wszSystemDir, MAX_PATH) != 0)
        {
            tstring strAnswerFile;
            strAnswerFile = wszSystemDir;
            strAnswerFile += c_szNetConfigSysPrepAnswerFile;

            if (! pwifAnswerFile->SaveAsEx(strAnswerFile.c_str()))
                fRet = FALSE;
        }
        else
        {
            TraceTag(ttidNetSetup, "%s: GetSystemDirectory failed 0x%8x", __FUNCNAME__, GetLastError());
            fRet = FALSE;
        }
    }    

    delete pwifAnswerFile;

    return fRet? S_OK : S_FALSE;
}

BOOL FNetSetupApplySysPrep()
{
    INetCfg* pNetCfg        = NULL;
    BOOL     fInitCom       = TRUE;
    HRESULT  hr;    

    DefineFunctionName("FNetSetupApplySysPrep");
    TraceFunctionEntry(ttidNetSetup);

    hr = HrCreateAndInitializeINetCfg(&fInitCom, &pNetCfg, 
                                       FALSE, 
                                       0,     
                                       L"Restore Configuration for SysPrep",
                                       NULL);
    if (SUCCEEDED(hr))
    {

        if (! fInitCom)
        {
            TraceTag(ttidNetSetup, "%s: Failed to init COM", __FUNCNAME__);
            return FALSE;
        }

        hr = HrRestoreNetworkComponentsForSysPrep(pNetCfg);

        HrUninitializeAndReleaseINetCfg (fInitCom, pNetCfg, FALSE);
    }

    TraceError("FNetSetupApplySysPrep", hr);
    return (hr == S_OK)? TRUE : FALSE;
}

HRESULT HrRestoreNetworkComponentsForSysPrep(INetCfg* pNetCfg)
{
    HRESULT hr = S_OK;
    GUID    guidAdapter; 
    WCHAR   wszSystemDir[MAX_PATH]; 
    tstring strAnswerFile; 
    BOOL    fRet = TRUE; 

    DefineFunctionName("HrRestoreNetworkComponentsForSysPrep");

    if (GetSystemDirectory(wszSystemDir, MAX_PATH) != 0)
    {
        strAnswerFile = wszSystemDir;
        strAnswerFile += c_szNetConfigSysPrepAnswerFile;
    }
    else
    {
        TraceTag(ttidNetSetup, "%s: GetSystemDirectory failed 0x%8x", __FUNCNAME__, GetLastError());
        return S_FALSE;
    }

    hr = HrGetFirstAdapterInstanceGuid(pNetCfg, TRUE, &guidAdapter); 
    if (hr != S_OK)
    {
        TraceTag(ttidNetSetup, "%s: HrGetFirstAdapterInstanceGuid failed 0x%08x", __FUNCNAME__, hr);
        return S_FALSE;
    }

    CWInfFile* pwifAnswerFile = new CWInfFile();

    if ((pwifAnswerFile == NULL) || (pwifAnswerFile->Init() == FALSE))
	{
	    AssertSz(FALSE,"HrRestoreNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        if (pwifAnswerFile)
            delete pwifAnswerFile;
		return(E_OUTOFMEMORY);
	}

    if (pwifAnswerFile->Open(strAnswerFile.c_str()) == FALSE)
    {
        TraceTag(ttidNetSetup, "%s: pwifAnswerFile->Open failed 0x%08x", __FUNCNAME__);
        delete pwifAnswerFile;
        return S_FALSE;
    }

    for (UINT nIdx = 0; nIdx < celems(g_IdMap); nIdx++)
    {
        INetCfgComponent* pINetCfgComponent;
        PCWSTR pwszInfId;  
        PCWSTR pwszAdapterParamsSections; 

        pwszInfId = g_IdMap[nIdx].pwszId;
        pwszAdapterParamsSections = g_IdMap[nIdx].pwszIdAdapterParamsSection;

        pwifAnswerFile->FindSection(pwszAdapterParamsSections);
        if (FSectionHasAtLeastOneKey(pwifAnswerFile, pwszAdapterParamsSections))
        {
            hr = pNetCfg->FindComponent(pwszInfId, &pINetCfgComponent);
            if (hr == S_OK)
            {
                Assert (pINetCfgComponent);

                INetCfgComponentPrivate* pComponentPrivate;
                hr = pINetCfgComponent->QueryInterface(
                            IID_INetCfgComponentPrivate,
                            reinterpret_cast<void**>(&pComponentPrivate));

                if (hr == S_OK)
                {
                    INetCfgComponentSysPrep* pINetCfgComponentSysPrep;

                    hr = pComponentPrivate->QueryNotifyObject(
                                    IID_INetCfgComponentSysPrep,
                                    (void**) &pINetCfgComponentSysPrep);
                    if (S_OK == hr)
                    {                    
                        hr = pINetCfgComponentSysPrep->RestoreAdapterParameters(
                                    strAnswerFile.c_str(), pwszAdapterParamsSections, &guidAdapter);
                        if (hr != S_OK)
                            fRet = FALSE; 
                        ReleaseObj(pINetCfgComponentSysPrep);
                    }
                    else if (hr == E_NOINTERFACE)
                    {
                        TraceTag(ttidNetSetup, "%s: %S component doesn't support IID_INetCfgComponentSysPrep", __FUNCNAME__, pwszInfId);

                    }
                    else
                        fRet = FALSE; 
                    ReleaseObj(pComponentPrivate);
                }
                else
                {
                    TraceTag(ttidNetSetup, "%s: can't find IID_INetCfgComponentPrivate for component %S", __FUNCNAME__, pwszInfId);
                    fRet = FALSE;
                }
                ReleaseObj (pINetCfgComponent);
            }
            else
            {

                TraceTag(ttidNetSetup, "%s: Can't find %S component", __FUNCNAME__, pwszInfId);
            }
        } 
    } 

#ifndef DBG
    DeleteFile(strAnswerFile.c_str());
#endif
    delete pwifAnswerFile;
    return fRet? S_OK : S_FALSE;
}

BOOL FSectionHasAtLeastOneKey(IN CWInfFile* pwifAnswerFile, IN PCWSTR pwszSection)
{
    Assert(pwifAnswerFile && pwszSection);
    PCWInfSection pwifs = pwifAnswerFile->FindSection(pwszSection);
    if (pwifs == NULL)
        return FALSE;
    PCWInfKey pwifk = pwifs->FirstKey();
    if (pwifs == NULL)
        return FALSE;
    return TRUE;
}

HRESULT HrGetFirstAdapterInstanceGuid(INetCfg * pnc, BOOL fDuringSetup, GUID * pGuidAdapter)
{
    HRESULT      hr = S_OK;

    DefineFunctionName("HrGetFirstAdapterInstanceGuid");
    TraceTag(ttidNetSetup, "HrGetFirstAdapterInstanceGuid - Enter Find first available adapter");

    Assert(pnc && pGuidAdapter);
    CIterNetCfgComponent nccIter(pnc, &GUID_DEVCLASS_NET);
    INetCfgComponent*    pncc;
    while (SUCCEEDED(hr) &&  (S_OK == (hr = nccIter.HrNext(&pncc))))
    {
        hr = HrIsLanCapableAdapter(pncc);
        TraceError("HrIsLanCapableAdapter", hr);
        if (S_OK == hr)
        {
            DWORD        dw;
            ULONG        ul;

            TraceTag(ttidNetSetup, "%s: Found HrIsLanCapableAdapter", __FUNCNAME__);

            if (! fDuringSetup)
            {

                hr = HrIsConnection(pncc);
                if (hr != S_OK)
                {
                    TraceError("HrGetFirstAdapterInstanceGuid: HrIsConnection", hr);
                    goto NextAdapter;
                }
            }

            hr = pncc->GetCharacteristics(&dw);
            if (hr != S_OK)
            {
                TraceError("GetCharacteristics", hr);
                goto NextAdapter;
            }
            if (! (dw & NCF_PHYSICAL))
            {
                TraceTag(ttidNetSetup, "%s: It is not a PHYSICAL adapter", __FUNCNAME__);
                goto NextAdapter;
            }

            hr = pncc->GetDeviceStatus(&ul);
            if ((hr != S_OK) || (ul != 0))
            {
                TraceTag(ttidNetSetup, "%s: device is not active.", __FUNCNAME__);
                goto NextAdapter;
            }

            hr = pncc->GetInstanceGuid(pGuidAdapter);
            if (hr != S_OK)
            {
                TraceError("GetInstanceGuid", hr); 
                goto NextAdapter;
            }

            ReleaseObj(pncc);
            return S_OK;
        }

NextAdapter:
        ReleaseObj(pncc);
        hr = S_OK;
    }
    return hr;
}
//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 2000.
//
//  File:       N C S Y S P R P . C P P
//
//  Contents:   Functions that related to SysPrep.exe requiremnts.
//
//  Notes: 1. 
//          1.a sysprep calls NetSetupPrepareSysPrep
//          1.b NetSetup saves adapter specific settings into $ncsp$.inf file.
//              NetSetup does this by calling INetCfgComponent::SaveAdapterParameters
//              implemented by notify object.
//
//         2.
//          2.a Mini-Setup calls NetSetupRequestWizardPages passing the 
//              "SETUPOPER_MINISETUP" flag right after PnP device installation.
//          2.b NetSetup's InstallUpgradeWorkThrd thread checks the "SETUPOPER_MINISETUP" flag, 
//              if set, it calls FNetSetupApplySysPrep which reads Answer-File "$ncsp$.inf".
//              It then calls notify object's INetCfgComponent::RestoreAdapterParameters
//              to restore adapter specific parameter settings from "$ncsp$.inf" Answer-File.
//
//         3. Only ONE network adapter card is supported on this version.
//
//  Author:     FrankLi    22-April-2000
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma  hdrstop
#include "kkcwinf.h"
#include "ncstring.h"
#include "netcfgp.h"
#include "nsbase.h"
#include "kkutils.h" 
#include "ncnetcfg.h"
#include "lancmn.h"
#include "ncreg.h"
#include "ncsysprp.h"
#include "resource.h"


// constants from ncnetcfg\netinfid.cpp
extern WCHAR c_szInfId_MS_TCPIP[]              = L"ms_tcpip";
extern WCHAR c_szInfId_MS_MSClient[]           = L"ms_msclient";
extern WCHAR c_szInfId_MS_NWClient[]           = L"ms_nwclient";

// constants from ncbase\afilestr.cpp
extern WCHAR c_szRegKeyAnswerFileMap[]         = L"SYSTEM\\Setup\\AnswerFileMap";

// constants for adapter specific sections
const WCHAR c_szParams_MS_TCPIP_Adapter01[]    = L"params.ms_tcpip_Adapter01";
//const WCHAR c_szParams_MS_MSClient_Adapter01[] = L"params.ms_msclient_Adapter01";
//const WCHAR c_szParams_MS_NWClient_Adapter01[] = L"params.ms_nwclient_Adapter01";

// The Answer-File name to save registry settings
static const WCHAR c_szNetConfigSysPrepAnswerFile[]   = L"\\$ncsp$.inf";

// struct to map component Id to its corresponding adapter specific parameter 
// Answer-File section
struct
{
    PCWSTR pwszId;
    PCWSTR pwszIdAdapterParamsSection;
} g_IdMap[] = {{c_szInfId_MS_TCPIP, c_szParams_MS_TCPIP_Adapter01},
                   // no adapter specific parameters for MSClient and NWClient
                   //{c_szInfId_MS_MSClient, c_szParams_MS_MSClient_Adapter01},
                   //{c_szInfId_MS_NWClient, c_szParams_MS_NWClient_Adapter01}
                };

// forward declaration
HRESULT HrSaveNetworkComponentsForSysPrep(INetCfg* pNetCfg);
HRESULT HrRestoreNetworkComponentsForSysPrep(INetCfg* pNetCfg);
HRESULT HrGetFirstAdapterInstanceGuid(INetCfg * pnc, BOOL fDuringSetup, GUID * pGuidAdapter);
BOOL    FSectionHasAtLeastOneKey(IN CWInfFile* pwifAnswerFile, IN PCWSTR pszSection);



//-------------------------------------------------------------------------
// internal helper APIs used by CNetCfgSysPrep implementation 
// to save settings from notify object:

// Purpose: save REG_DWORD to our internal CWinfFile object
inline HRESULT HrSetupSetFirstDword(IN HWIF   hwif,
                                    IN PCWSTR pwszSection,
                                    IN PCWSTR pwszKey,
                                    IN DWORD  dwValue)
{
    Assert (hwif && pwszSection && pwszKey);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);
    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddKey(pwszKey, dwValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

// Purpose: save REG_SZ to our internal CWinfFile object
inline HRESULT HrSetupSetFirstString(IN HWIF   hwif,
                                     IN PCWSTR pwszSection,
                                     IN PCWSTR pwszKey,
                                     IN PCWSTR pwszValue)
{
    Assert (hwif && pwszSection && pwszKey && pwszValue);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);

    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddKey(pwszKey, pwszValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

// Purpose: save BOOL data to our internal CWinfFile object
inline HRESULT HrSetupSetFirstStringAsBool(IN HWIF   hwif,
                                           IN PCWSTR pwszSection,
                                           IN PCWSTR pwszKey,
                                           IN BOOL   fValue)
{
    Assert (hwif && pwszSection && pwszKey);
    CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);

    PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
    if (pwifSection)
    {
        pwifAnswerFile->GotoEndOfSection(pwifSection);
        pwifAnswerFile->AddBoolKey(pwszKey, fValue);
        return S_OK;
    }
    else
        return S_FALSE;
}

// Purpose: save MULTI_SZ to our internal CWinfFile object
HRESULT HrSetupSetFirstMultiSzField(IN HWIF   hwif,
                                    IN PCWSTR pwszSection,
                                    IN PCWSTR pwszKey, 
                                    IN PCWSTR pmszValue)
{
    HRESULT  hr = S_OK;
    
    Assert (hwif && pwszSection && pwszKey && pmszValue);

    TStringList slValues;
    MultiSzToColString(pmszValue, &slValues);
    if (slValues.empty())
    {
        // empty pmszValue
        return HrSetupSetFirstString(hwif, pwszSection, pwszKey, pmszValue);
    }
    else
    {
        CWInfFile* pwifAnswerFile = reinterpret_cast<PCWInfFile>(hwif);
        PCWInfSection pwifSection = pwifAnswerFile->FindSection(pwszSection);
        if (pwifSection)
        {
            pwifAnswerFile->GotoEndOfSection(pwifSection);
            pwifAnswerFile->AddKey(pwszKey, slValues);
        }
        else
            hr = S_FALSE;
    }
    EraseAndDeleteAll(&slValues);
    return hr;
}

//--------------------------------------------------------------------------
// Implementation of INetCfgSysPrep component
inline HRESULT CNetCfgSysPrep::HrSetupSetFirstDword(
                                                    IN PCWSTR pwszSection, 
                                                    IN PCWSTR pwszKey, 
                                                    IN DWORD  dwValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstDword(
						m_hwif,
                        pwszSection,
						pwszKey,
						dwValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstString(
                                                     IN PCWSTR pwszSection, 
                                                     IN PCWSTR pwszKey, 
                                                     IN PCWSTR pwszValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstString(
						m_hwif,
                        pwszSection,
						pwszKey,
						pwszValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstStringAsBool(
                                                           IN PCWSTR pwszSection, 
                                                           IN PCWSTR pwszKey, 
                                                           IN BOOL   fValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstStringAsBool(
						m_hwif,
                        pwszSection,
						pwszKey,
						fValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

inline HRESULT CNetCfgSysPrep::HrSetupSetFirstMultiSzField(
                                                           IN PCWSTR pwszSection, 
                                                           IN PCWSTR pwszKey, 
                                                           IN PCWSTR pmszValue)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&m_csWrite);
    if (m_hwif)
	{
        hr = ::HrSetupSetFirstMultiSzField(
						m_hwif,
                        pwszSection,
						pwszKey,
						pmszValue);
	}
	else
	{
		hr = E_FAIL;
	}
    LeaveCriticalSection(&m_csWrite);
    return hr;
}

//
// Function:    FNetSetupPrepareSysPrep
//
// Purpose:     wrapper for HrSaveNetworkComponentsForSysPrep
//
// Parameters:  
//
// Returns:     TRUE on success, otherwise, FALSE
//
// 
BOOL FNetSetupPrepareSysPrep()
{
    INetCfg* pNetCfg        = NULL;
    BOOL     fInitCom       = TRUE;
    HRESULT  hr;    

    DefineFunctionName("FNetSetupPrepareSysPrep");
    TraceFunctionEntry(ttidNetSetup);

    hr = HrCreateAndInitializeINetCfg(&fInitCom, &pNetCfg, 
                                       FALSE, // no write lock
                                       0,     // don't wait for it
                                       L"Save Configuration for SysPrep",
                                       NULL);
    if (SUCCEEDED(hr))
    {
        // Retain our success in initializing COM only if we asked to
        // initialize COM in the first place.
        if (! fInitCom)
        {
            TraceTag(ttidNetSetup, "%s: Failed to init COM", __FUNCNAME__);
            return FALSE;
        }
        // Save network component per adapter registry settings
        hr = HrSaveNetworkComponentsForSysPrep(pNetCfg);
        if (hr == S_OK)
        {
            // delete the HKLM\SYSTEM\Setup\AnswerFileMap registry key if it exits
            HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE, c_szRegKeyAnswerFileMap);
        }

        HrUninitializeAndReleaseINetCfg (fInitCom, pNetCfg, FALSE);
    }

    TraceError("FNetSetupPrepareSysPrep", hr);
    return (hr == S_OK)? TRUE : FALSE;   
}


//
// Function:    HrSaveNetworkComponentsForSysPrep
//
// Purpose:     Ask notify object to save adapter specific settings
//
// Parameters:  pNetCfg [IN]     - An INetCfg interface
//
// Returns:     HRESULT, S_OK on success
//
// 
// Note: only 1 network adapter card is supported. If there are more than 1,
//       we'll pick the first working one.
HRESULT HrSaveNetworkComponentsForSysPrep(INetCfg* pNetCfg)
{
    HRESULT hr = S_OK;
    GUID    guidAdapter;         // network adapter's Instance GUID
    BOOL    fApplyWrite = FALSE; // flag to save internal registry settings to Answer-File
    BOOL    fRet = TRUE;

    DefineFunctionName("HrSaveNetworkComponentsForSysPrep");

    hr = HrGetFirstAdapterInstanceGuid(pNetCfg, FALSE, &guidAdapter); // FALSE ==> not in setup    
    if (hr != S_OK)
    {
        TraceTag(ttidNetSetup, "%s: HrGetFirstAdapterInstanceGuid failed 0x%08x", __FUNCNAME__, hr);
        return S_FALSE;
    }

    CWInfFile* pwifAnswerFile = new CWInfFile();
    // initialize answer file class
    if ((pwifAnswerFile == NULL) || (pwifAnswerFile->Init() == FALSE))
	{
	    AssertSz(FALSE,"HrSaveNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        if (pwifAnswerFile)
            delete pwifAnswerFile;
		return(E_OUTOFMEMORY);
	}

    // access INetCfgSysPrep ATL component using C++ instead of CoCreateInstance
    CComObject<CNetCfgSysPrep>* pncsp = new CComObject<CNetCfgSysPrep>;
    // initialize CNetCfgSysPrep class
    if ((pncsp == NULL) || (pncsp->HrInit((HWIF) pwifAnswerFile) != S_OK))
	{
	    AssertSz(FALSE,"HrSaveNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        delete pwifAnswerFile;
        if (pncsp)
            delete pncsp;
		return(E_OUTOFMEMORY);
	}
    pncsp->AddRef(); // keep a reference to our component

    for (UINT nIdx = 0; nIdx < celems(g_IdMap); nIdx++)
    {
        INetCfgComponent* pINetCfgComponent;
        PCWSTR pwszInfId;
        PCWSTR pwszAdapterParamsSections;

        
        pwszInfId = g_IdMap[nIdx].pwszId;
        pwszAdapterParamsSections = g_IdMap[nIdx].pwszIdAdapterParamsSection;

        hr = pNetCfg->FindComponent(pwszInfId, &pINetCfgComponent);
       
        if (hr == S_OK)
        {
            Assert (pINetCfgComponent);
            // Component has already installed, we'll call notify object's 
            // INetCfgComponentSysPrep::SaveAdapterParameters
            
            // Need to query for the private component interface which
            // gives us access to the notify object.
            //
            INetCfgComponentPrivate* pComponentPrivate;
            hr = pINetCfgComponent->QueryInterface(
                        IID_INetCfgComponentPrivate,
                        reinterpret_cast<void**>(&pComponentPrivate));

            if (hr == S_OK)
            {
                INetCfgComponentSysPrep* pINetCfgComponentSysPrep;

                // Query the notify object for its INetCfgComponentSysPrep interface.
                // If it doesn't support it, that's okay, we can continue.
                //

                hr = pComponentPrivate->QueryNotifyObject(
                                IID_INetCfgComponentSysPrep, 
                                (void**) &pINetCfgComponentSysPrep);
                if (S_OK == hr)
                {
                    // add a section first
                    pwifAnswerFile->AddSection(pwszAdapterParamsSections);
                    // trigger Notify object to save registry settings
                    hr = pINetCfgComponentSysPrep->SaveAdapterParameters(
                                reinterpret_cast<INetCfgSysPrep*>(pncsp), pwszAdapterParamsSections, &guidAdapter);
                    if (hr == S_OK)
                        fApplyWrite = TRUE;
                    ReleaseObj(pINetCfgComponentSysPrep);
                }
                else if (hr == E_NOINTERFACE)
                {
                    TraceTag(ttidNetSetup, "%s: %S component doesn't support IID_INetCfgComponentSysPrep", __FUNCNAME__, pwszInfId);
                }
                else
                    fRet = FALSE; // unexpected error
                ReleaseObj(pComponentPrivate);
            }
            else
            {
                TraceTag(ttidNetSetup, "%s: can't find IID_INetCfgComponentPrivate for component %S", __FUNCNAME__, pwszInfId);
                fRet = FALSE;
            }

            ReleaseObj (pINetCfgComponent);
        }
        else
        {
            // it is okay that this component has not been installed
            TraceTag(ttidNetSetup, "%s: Can't find %S component", __FUNCNAME__, pwszInfId);
        }
    } // end for


    pncsp->SetHWif(NULL); // no more writes for those components holding our INetCfgSysPrep interface
    ReleaseObj(pncsp);    // done with the usage of INetCfgSysPrep component

    if (fApplyWrite)
    {
        WCHAR wszSystemDir[MAX_PATH]; 
        
        // get the path to $ncsp$.inf (NetConfig SysPrep Answer-File)
        if (GetSystemDirectory(wszSystemDir, MAX_PATH) != 0)
        {
            tstring strAnswerFile;
            strAnswerFile = wszSystemDir;
            strAnswerFile += c_szNetConfigSysPrepAnswerFile;
            // save the parameters filled by notify object to Answer-File
            if (! pwifAnswerFile->SaveAsEx(strAnswerFile.c_str()))
                fRet = FALSE;
        }
        else
        {
            TraceTag(ttidNetSetup, "%s: GetSystemDirectory failed 0x%8x", __FUNCNAME__, GetLastError());
            fRet = FALSE;
        }
    }    

    delete pwifAnswerFile;

    return fRet? S_OK : S_FALSE;
}

//
// Function:    FNetSetupApplySysPrep
//
// Purpose:     wrapper for HrRestoreNetworkComponentsForSysPrep
//
// Parameters:  
//
// Returns:     TRUE on success, otherwise, FALSE
//
// Notes:       This causes NetSetup to load the content of 
//              %systemroot%\system32\$ncsp$.inf into a CWInfFile object.
//              Then, NetSetup will instruct notify object to restore their
//              per adatper settings from the corresponding sections of the
//              $ncsp$.inf file. 

BOOL FNetSetupApplySysPrep()
{
    INetCfg* pNetCfg        = NULL;
    BOOL     fInitCom       = TRUE;
    HRESULT  hr;    
    

    DefineFunctionName("FNetSetupApplySysPrep");
    TraceFunctionEntry(ttidNetSetup);

    hr = HrCreateAndInitializeINetCfg(&fInitCom, &pNetCfg, 
                                       FALSE, // no write lock
                                       0,     // don't wait for it
                                       L"Restore Configuration for SysPrep",
                                       NULL);
    if (SUCCEEDED(hr))
    {
        // Retain our success in initializing COM only if we asked to
        // initialize COM in the first place.
        if (! fInitCom)
        {
            TraceTag(ttidNetSetup, "%s: Failed to init COM", __FUNCNAME__);
            return FALSE;
        }
        // Restore network component per adapter registry settings
        hr = HrRestoreNetworkComponentsForSysPrep(pNetCfg);

        HrUninitializeAndReleaseINetCfg (fInitCom, pNetCfg, FALSE);
    }
    
    TraceError("FNetSetupApplySysPrep", hr);
    return (hr == S_OK)? TRUE : FALSE;
}

//
// Function:    HrRestoreNetworkComponentsForSysPrep
//
// Purpose:     read $ncsp$.inf file. If this file has
//              adapter specific sections, trigger the corresponding
//              notify object to restore the settings to registry.
//
// Parameters:  pNetCfg [IN]     - An INetCfg interface
//
// Returns:     HRESULT, S_OK on success
//
// 
HRESULT HrRestoreNetworkComponentsForSysPrep(INetCfg* pNetCfg)
{
    HRESULT hr = S_OK;
    GUID    guidAdapter; // network adapter's Instance GUID
    WCHAR   wszSystemDir[MAX_PATH]; // system32 directory
    tstring strAnswerFile; // Answer-File which was saved by SysPrep
    BOOL    fRet = TRUE; // notify object's status in restore settings

    DefineFunctionName("HrRestoreNetworkComponentsForSysPrep");


    // get the path to $ncsp$.inf (NetConfig SysPrep Answer-File)
    if (GetSystemDirectory(wszSystemDir, MAX_PATH) != 0)
    {
        strAnswerFile = wszSystemDir;
        strAnswerFile += c_szNetConfigSysPrepAnswerFile;
    }
    else
    {
        TraceTag(ttidNetSetup, "%s: GetSystemDirectory failed 0x%8x", __FUNCNAME__, GetLastError());
        return S_FALSE;
    }

    hr = HrGetFirstAdapterInstanceGuid(pNetCfg, TRUE, &guidAdapter); // TRUE ==> during setup
    if (hr != S_OK)
    {
        TraceTag(ttidNetSetup, "%s: HrGetFirstAdapterInstanceGuid failed 0x%08x", __FUNCNAME__, hr);
        return S_FALSE;
    }

    CWInfFile* pwifAnswerFile = new CWInfFile();
    // initialize answer file class
    if ((pwifAnswerFile == NULL) || (pwifAnswerFile->Init() == FALSE))
	{
	    AssertSz(FALSE,"HrRestoreNetworkComponentsForSysPrep - Failed to initialize CWInfFile");
        if (pwifAnswerFile)
            delete pwifAnswerFile;
		return(E_OUTOFMEMORY);
	}
    // read $ncsp$.inf Answer-File into pwifAnswerFile object
    if (pwifAnswerFile->Open(strAnswerFile.c_str()) == FALSE)
    {
        TraceTag(ttidNetSetup, "%s: pwifAnswerFile->Open failed 0x%08x", __FUNCNAME__);
        delete pwifAnswerFile;
        return S_FALSE;
    }

    for (UINT nIdx = 0; nIdx < celems(g_IdMap); nIdx++)
    {
        INetCfgComponent* pINetCfgComponent;
        PCWSTR pwszInfId;  // component ID
        PCWSTR pwszAdapterParamsSections; // adapter specific parameter section 

        
        pwszInfId = g_IdMap[nIdx].pwszId;
        pwszAdapterParamsSections = g_IdMap[nIdx].pwszIdAdapterParamsSection;

        // trigger Notify object to restore registry settings if
        // the section has at least one line of parameter
        pwifAnswerFile->FindSection(pwszAdapterParamsSections);
        if (FSectionHasAtLeastOneKey(pwifAnswerFile, pwszAdapterParamsSections))
        {
            hr = pNetCfg->FindComponent(pwszInfId, &pINetCfgComponent);
            if (hr == S_OK)
            {
                Assert (pINetCfgComponent);
                // Component has already installed, just call notify object's
                // INetCfgComponentSysPrep::RestoreAdapterParameters
            
                // Need to query for the private component interface which
                // gives us access to the notify object.
                //
                INetCfgComponentPrivate* pComponentPrivate;
                hr = pINetCfgComponent->QueryInterface(
                            IID_INetCfgComponentPrivate,
                            reinterpret_cast<void**>(&pComponentPrivate));

                if (hr == S_OK)
                {
                    INetCfgComponentSysPrep* pINetCfgComponentSysPrep;

                    // Query the notify object for its INetCfgComponentSysPrep interface.
                    // If it doesn't support it, that's okay, we can continue.
                    //

                    hr = pComponentPrivate->QueryNotifyObject(
                                    IID_INetCfgComponentSysPrep,
                                    (void**) &pINetCfgComponentSysPrep);
                    if (S_OK == hr)
                    {                    
                        hr = pINetCfgComponentSysPrep->RestoreAdapterParameters(
                                    strAnswerFile.c_str(), pwszAdapterParamsSections, &guidAdapter);
                        if (hr != S_OK)
                            fRet = FALSE; // notify object can't restore settings
                        ReleaseObj(pINetCfgComponentSysPrep);
                    }
                    else if (hr == E_NOINTERFACE)
                    {
                        TraceTag(ttidNetSetup, "%s: %S component doesn't support IID_INetCfgComponentSysPrep", __FUNCNAME__, pwszInfId);

                    }
                    else
                        fRet = FALSE; // unexpected error
                    ReleaseObj(pComponentPrivate);
                }
                else
                {
                    TraceTag(ttidNetSetup, "%s: can't find IID_INetCfgComponentPrivate for component %S", __FUNCNAME__, pwszInfId);
                    fRet = FALSE;
                }
                ReleaseObj (pINetCfgComponent);
            }
            else
            {
                // this component wasn't installed before SysPrep
                TraceTag(ttidNetSetup, "%s: Can't find %S component", __FUNCNAME__, pwszInfId);
            }
        } // end if section has at least one key to restore setting
    } // end for

    // delete the Answer-File in free build.
#ifndef DBG
    DeleteFile(strAnswerFile.c_str());
#endif
    delete pwifAnswerFile;
    return fRet? S_OK : S_FALSE;
}

//
// Function:    FSectionHasAtLeastOneKey
//
// Purpose:     Check if an Answer-File section has at least one key 
//
// Parameters:  pwifAnswerFile [IN] - pointer to a CWInfFile object
//              pszSection [IN]     - the section to check
//
// Returns:     TRUE if found else FALSE
//
BOOL FSectionHasAtLeastOneKey(IN CWInfFile* pwifAnswerFile, IN PCWSTR pwszSection)
{
    Assert(pwifAnswerFile && pwszSection);
    PCWInfSection pwifs = pwifAnswerFile->FindSection(pwszSection);
    if (pwifs == NULL)
        return FALSE;
    PCWInfKey pwifk = pwifs->FirstKey();
    if (pwifs == NULL)
        return FALSE;
    return TRUE;
}

//
// Function:    HrGetFirstAdapterInstanceGuid
//
// Purpose:     Get the first installed adapter instance guid 
//
// Parameters:  pnc [IN]     - An INetCfg interface
//              fDuringSetup [IN] - TRUE when this is being called during the setup time
//              pGuidAdapter [IN,OUT] - Receives an instance GUID of an adapter
//
// Returns:     HRESULT, S_OK on success
//
HRESULT HrGetFirstAdapterInstanceGuid(INetCfg * pnc, BOOL fDuringSetup, GUID * pGuidAdapter)
{
    HRESULT      hr = S_OK;

    DefineFunctionName("HrGetFirstAdapterInstanceGuid");
    TraceTag(ttidNetSetup, "HrGetFirstAdapterInstanceGuid - Enter Find first available adapter");

    // Enumerate the available adapters
    Assert(pnc && pGuidAdapter);
    CIterNetCfgComponent nccIter(pnc, &GUID_DEVCLASS_NET);
    INetCfgComponent*    pncc;
    while (SUCCEEDED(hr) &&  (S_OK == (hr = nccIter.HrNext(&pncc))))
    {
        hr = HrIsLanCapableAdapter(pncc);
        TraceError("HrIsLanCapableAdapter", hr);
        if (S_OK == hr)
        {
            DWORD        dw;
            ULONG        ul;
            
            TraceTag(ttidNetSetup, "%s: Found HrIsLanCapableAdapter", __FUNCNAME__);

            if (! fDuringSetup)
            {
                // Is it in used in a connection?
                hr = HrIsConnection(pncc);
                if (hr != S_OK)
                {
                    TraceError("HrGetFirstAdapterInstanceGuid: HrIsConnection", hr);
                    goto NextAdapter;
                }
            }

            // Is this a virtual adapter?
            hr = pncc->GetCharacteristics(&dw);
            if (hr != S_OK)
            {
                TraceError("GetCharacteristics", hr);
                goto NextAdapter;
            }
            if (! (dw & NCF_PHYSICAL))
            {
                TraceTag(ttidNetSetup, "%s: It is not a PHYSICAL adapter", __FUNCNAME__);
                goto NextAdapter;
            }

            // Check device, if not present skip it
            //
            hr = pncc->GetDeviceStatus(&ul);
            if ((hr != S_OK) || (ul != 0))
            {
                TraceTag(ttidNetSetup, "%s: device is not active.", __FUNCNAME__);
                goto NextAdapter;
            }

            // Get the adapter instance guid
            hr = pncc->GetInstanceGuid(pGuidAdapter);
            if (hr != S_OK)
            {
                TraceError("GetInstanceGuid", hr); //
                goto NextAdapter;
            }

            ReleaseObj(pncc);
            return S_OK;
        }

NextAdapter:
        ReleaseObj(pncc);
        hr = S_OK;
    }
    return hr;
}
