//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       S N E T C F G . C P P
//
//  Contents:   Sample code that demonstrates how to:
//              - find out if a component is installed
//              - install a net component
//              - install an OEM net component
//              - uninstall a net component
//              - enumerate net components
//              - enumerate net adapters using Setup API
//              - enumerate binding paths of a component
//
//  Notes:
//
//----------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop
#include "snetcfg.h"


//----------------------------------------------------------------------------
// Globals
//
static const GUID* c_aguidClass[] =
{
    &GUID_DEVCLASS_NET,
    &GUID_DEVCLASS_NETTRANS,
    &GUID_DEVCLASS_NETSERVICE,
    &GUID_DEVCLASS_NETCLIENT
};

//----------------------------------------------------------------------------
// Prototypes of helper functions
//
HRESULT HrInstallNetComponent(IN INetCfg* pnc, IN PCWSTR szComponentId,
                              IN const GUID* pguidClass);
HRESULT HrUninstallNetComponent(IN INetCfg* pnc, IN PCWSTR szComponentId);
HRESULT HrGetINetCfg(IN BOOL fGetWriteLock, INetCfg** ppnc);
HRESULT HrReleaseINetCfg(BOOL fHasWriteLock, INetCfg* pnc);
void ShowMessage(IN PCWSTR szMsg, ...);
void ShowHrMessage(IN HRESULT hr);
inline ULONG ReleaseObj(IUnknown* punk)
{
    return (punk) ? punk->Release () : 0;
}


//+---------------------------------------------------------------------------
//
// Function:  HrIsComponentInstalled
//
// Purpose:   Find out if a component is installed
//
// Arguments:
//    szComponentId [in]  id of component to search
//
// Returns:   S_OK    if installed,
//            S_FALSE if not installed,
//            otherwise an error code
//
// Author:    kumarp 11-February-99
//
// Notes:
//
HRESULT HrIsComponentInstalled(IN PCWSTR szComponentId)
{
    HRESULT hr=S_OK;
    INetCfg* pnc;
    INetCfgComponent* pncc;

    hr = HrGetINetCfg(FALSE, &pnc);
    if (S_OK == hr)
    {
        hr = pnc->FindComponent(szComponentId, &pncc);
        if (S_OK == hr)
        {
            ReleaseObj(pncc);
        }
        (void) HrReleaseINetCfg(FALSE, pnc);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  FindIfComponentInstalled
//
// Purpose:   Find out if a component is installed
//
// Arguments:
//    szComponentId [in]  id of component to locate
//
// Returns:   None
//
// Author:    kumarp 11-February-99
//
// Notes:
//
void FindIfComponentInstalled(IN PCWSTR szComponentId)
{
    HRESULT hr=S_OK;

    hr = HrIsComponentInstalled(szComponentId);
    if (S_OK == hr)
    {
        _tprintf(L"'%s' is installed\n", szComponentId);
    }
    else if (S_FALSE == hr)
    {
        _tprintf(L"'%s' is not installed\n", szComponentId);
    }
    else
    {
        _tprintf(L"Could not find if '%s' is installed. error code: 0x%x\n",
                 szComponentId, hr);
    }
}

//+---------------------------------------------------------------------------
//
// Function:  HrInstallNetComponent
//
// Purpose:   Install the specified net component
//
// Arguments:
//    szComponentId [in]  component to install
//    nc            [in]  class of the component
//    szInfFullPath [in]  full path to primary INF file
//                        required if the primary INF and other
//                        associated files are not pre-copied to
//                        the right destination dirs.
//                        Not required when installing MS components
//                        since the files are pre-copied by
//                        Windows NT Setup.
//
// Returns:   S_OK or NETCFG_S_REBOOT on success, otherwise an error code
//
// Notes:
//
HRESULT HrInstallNetComponent(IN PCWSTR szComponentId,
                              IN enum NetClass nc,
                              IN PCWSTR szInfFullPath)
{
    HRESULT hr=S_OK;
    INetCfg* pnc;

    // cannot install net adapters this way. they have to be
    // enumerated/detected and installed by PnP

    if ((nc == NC_NetProtocol) ||
        (nc == NC_NetService) ||
        (nc == NC_NetClient))
    {
        ShowMessage(L"Trying to install '%s'...", szComponentId);

        // if full path to INF has been specified, the INF
        // needs to be copied using Setup API to ensure that any other files
        // that the primary INF copies will be correctly found by Setup API
        //
        if (szInfFullPath && wcslen(szInfFullPath))
        {
            WCHAR szInfNameAfterCopy[MAX_PATH+1];
            if (SetupCopyOEMInf(
                    szInfFullPath,
                    NULL,               // other files are in the
                                        // same dir. as primary INF
                    SPOST_PATH,         // first param. contains path to INF
                    0,                  // default copy style
                    szInfNameAfterCopy, // receives the name of the INF
                                        // after it is copied to %windir%\inf
                    MAX_PATH,           // max buf. size for the above
                    NULL,               // receives required size if non-null
                    NULL))              // optionally retrieves filename
                                        // component of szInfNameAfterCopy
            {
                ShowMessage(L"...%s was copied to %s",
                            szInfFullPath,
                            szInfNameAfterCopy);
            }
            else
            {
                DWORD dwError = GetLastError();
                hr = HRESULT_FROM_WIN32(dwError);
            }
        }

        if (S_OK == hr)
        {
            // get INetCfg interface
            hr = HrGetINetCfg(TRUE, &pnc);

            if (SUCCEEDED(hr))
            {
                // install szComponentId
                hr = HrInstallNetComponent(pnc, szComponentId,
                                           c_aguidClass[nc]);
                if (SUCCEEDED(hr))
                {
                    // Apply the changes
                    hr = pnc->Apply();
                }

                // release INetCfg
                (void) HrReleaseINetCfg(TRUE, pnc);
            }
        }
        // show success/failure message
        ShowHrMessage(hr);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrInstallNetComponent
//
// Purpose:   Install the specified net component
//
// Arguments:
//    pnc           [in]  pointer to INetCfg object
//    szComponentId [in]  component to install
//    pguidClass    [in]  class guid of the component
//
// Returns:   S_OK or NETCFG_S_REBOOT on success, otherwise an error code
//
// Notes:
//
HRESULT HrInstallNetComponent(IN INetCfg* pnc,
                              IN PCWSTR szComponentId,
                              IN const GUID* pguidClass)
{
    HRESULT hr=S_OK;
    OBO_TOKEN OboToken;
    INetCfgClassSetup* pncClassSetup;
    INetCfgComponent* pncc;

    // OBO_TOKEN specifies the entity on whose behalf this
    // component is being installed

    // set it to OBO_USER so that szComponentId will be installed
    // On-Behalf-Of "user"
    ZeroMemory (&OboToken, sizeof(OboToken));
    OboToken.Type = OBO_USER;

    hr = pnc->QueryNetCfgClass (pguidClass, IID_INetCfgClassSetup,
                                (void**)&pncClassSetup);
    if (SUCCEEDED(hr))
    {
        hr = pncClassSetup->Install(szComponentId,
                                    &OboToken,
                                    NSF_POSTSYSINSTALL,
                                    0,       // <upgrade-from-build-num>
                                    NULL,    // answerfile name
                                    NULL,    // answerfile section name
                                    &pncc);
        if (S_OK == hr)
        {
            // we dont want to use pncc (INetCfgComponent), release it
            ReleaseObj(pncc);
        }

        ReleaseObj(pncClassSetup);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrUninstallNetComponent
//
// Purpose:   Initialize INetCfg and uninstall a component
//
// Arguments:
//    szComponentId [in]  InfId of component to uninstall (e.g. MS_TCPIP)
//
// Returns:   S_OK or NETCFG_S_REBOOT on success, otherwise an error code
//
// Notes:
//
HRESULT HrUninstallNetComponent(IN PCWSTR szComponentId)
{
    HRESULT hr=S_OK;
    INetCfg* pnc;

    ShowMessage(L"Trying to uninstall '%s'...", szComponentId);

    // get INetCfg interface
    hr = HrGetINetCfg(TRUE, &pnc);

    if (SUCCEEDED(hr))
    {
        // uninstall szComponentId
        hr = HrUninstallNetComponent(pnc, szComponentId);

        if (S_OK == hr)
        {
            // Apply the changes
            hr = pnc->Apply();
        }
        else if (S_FALSE == hr)
        {
            ShowMessage(L"...'%s' is not installed");
        }

        // release INetCfg
        (void) HrReleaseINetCfg(TRUE, pnc);
    }

    // show success/failure message
    ShowHrMessage(hr);

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrUninstallNetComponent
//
// Purpose:   Uninstall the specified component.
//
// Arguments:
//    pnc           [in]  pointer to INetCfg object
//    szComponentId [in]  component to uninstall
//
// Returns:   S_OK or NETCFG_S_REBOOT on success, otherwise an error code
//
// Notes:
//
HRESULT HrUninstallNetComponent(IN INetCfg* pnc, IN PCWSTR szComponentId)
{
    HRESULT hr=S_OK;
    OBO_TOKEN OboToken;
    INetCfgComponent* pncc;
    GUID guidClass;
    INetCfgClass* pncClass;
    INetCfgClassSetup* pncClassSetup;

    // OBO_TOKEN specifies the entity on whose behalf this
    // component is being uninstalld

    // set it to OBO_USER so that szComponentId will be uninstalld
    // On-Behalf-Of "user"
    ZeroMemory (&OboToken, sizeof(OboToken));
    OboToken.Type = OBO_USER;

    // see if the component is really installed
    hr = pnc->FindComponent(szComponentId, &pncc);

    if (S_OK == hr)
    {
        // yes, it is installed. obtain INetCfgClassSetup and DeInstall

        hr = pncc->GetClassGuid(&guidClass);

        if (S_OK == hr)
        {
            hr = pnc->QueryNetCfgClass(&guidClass, IID_INetCfgClass,
                                       (void**)&pncClass);
            if (SUCCEEDED(hr))
            {
                hr = pncClass->QueryInterface(IID_INetCfgClassSetup,
                                              (void**)&pncClassSetup);
                    if (SUCCEEDED(hr))
                    {
                        hr = pncClassSetup->DeInstall (pncc, &OboToken, NULL);

                        ReleaseObj (pncClassSetup);
                    }
                ReleaseObj(pncClass);
            }
        }
        ReleaseObj(pncc);
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
// Function:  HrShowNetAdapters
//
// Purpose:   Display all installed net class devices using Setup API
//
// Arguments: None
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrShowNetAdapters()
{
#define MAX_COMP_INSTID 4096
#define MAX_COMP_DESC   4096

    HRESULT hr=S_OK;
    HDEVINFO hdi;
    DWORD dwIndex=0;
    SP_DEVINFO_DATA deid;
    BOOL fSuccess=FALSE;

    WCHAR szCompInstanceId[MAX_COMP_INSTID];
    WCHAR szCompDescription[MAX_COMP_DESC];
    DWORD dwRegType;
    BOOL fFound=FALSE;

    // get a list of all devices of class 'GUID_DEVCLASS_NET'
    hdi = SetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT);

    if (INVALID_HANDLE_VALUE != hdi)
    {
        // enumerate over each device
        while (deid.cbSize = sizeof(SP_DEVINFO_DATA),
               SetupDiEnumDeviceInfo(hdi, dwIndex, &deid))
        {
            dwIndex++;

            // the right thing to do here would be to call this function
            // to get the size required to hold the instance ID and then
            // to call it second time with a buffer large enough for that size.
            // However, that would tend to obscure the control flow in
            // the sample code. Lets keep things simple by keeping the
            // buffer large enough.

            // get the device instance ID
            fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid,
                                                  szCompInstanceId,
                                                  MAX_COMP_INSTID, NULL);
            if (fSuccess)
            {
                // get the description for this instance
                fSuccess =
                    SetupDiGetDeviceRegistryProperty(hdi, &deid,
                                                     SPDRP_DEVICEDESC,
                                                     &dwRegType,
                                                     (BYTE*) szCompDescription,
                                                     MAX_COMP_DESC,
                                                     NULL);
                if (fSuccess)
                {
                    if (!fFound)
                    {
                        fFound = TRUE;
                        _tprintf(L"Instance ID\tDescription\n");
                        _tprintf(L"-----------\t-----------\n");
                    }
                    _tprintf(L"%s\t%s\n",
                             szCompInstanceId, szCompDescription);
                }
            }
        }

        // release the device info list
        SetupDiDestroyDeviceInfoList(hdi);
    }

    if (!fSuccess)
    {
        DWORD dwError = GetLastError();
        hr = HRESULT_FROM_WIN32(dwError);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrShowNetComponents
//
// Purpose:   Display the list of installed components of the
//            specified class.
//
// Arguments:
//    pnc        [in]  pointer to INetCfg object
//    pguidClass [in]  pointer to class GUID
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrShowNetComponents(IN INetCfg* pnc,
                            IN const GUID* pguidClass)
{
    HRESULT hr=S_OK;
    PWSTR szInfId;
    PWSTR szDisplayName;
    DWORD dwcc;
    INetCfgComponent* pncc;
    INetCfgClass* pncclass;
    IEnumNetCfgComponent* pencc;
    ULONG celtFetched;

    hr = pnc->QueryNetCfgClass(pguidClass, IID_INetCfgClass,
                               (void**)&pncclass);
    if (SUCCEEDED(hr))
    {
        // get IEnumNetCfgComponent so that we can enumerate
        hr = pncclass->EnumComponents(&pencc);

        ReleaseObj(pncclass);

        while (SUCCEEDED(hr) &&
               (S_OK == (hr = pencc->Next(1, &pncc, &celtFetched))))
        {
            if (pguidClass == &GUID_DEVCLASS_NET)
            {
                // we are interested only in physical netcards
                //
                hr = pncc->GetCharacteristics(&dwcc);

                if (FAILED(hr) || !(dwcc & NCF_PHYSICAL))
                {
                    hr = S_OK;
                    ReleaseObj(pncc);
                    continue;
                }
            }

            hr = pncc->GetId(&szInfId);

            if (S_OK == hr)
            {
                hr = pncc->GetDisplayName(&szDisplayName);
                if (SUCCEEDED(hr))
                {
                    _tprintf(L"%-26s %s\n", szInfId, szDisplayName);

                    CoTaskMemFree(szDisplayName);
                }
                CoTaskMemFree(szInfId);
            }
            // we dont want to stop enumeration just because 1 component
            // failed either GetId or GetDisplayName, therefore reset hr to S_OK
            hr = S_OK;

            ReleaseObj(pncc);
        }
        ReleaseObj(pencc);
    }


    if (S_FALSE == hr)
    {
        hr = S_OK;
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
// Function:  HrShowNetComponents
//
// Purpose:   Display installed net components.
//
// Arguments: None
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrShowNetComponents()
{
    HRESULT hr=S_OK;


    static const PCWSTR c_aszClassNames[] =
    {
        L"Network Adapters",
        L"Network Protocols",
        L"Network Services",
        L"Network Clients"
    };

    INetCfg* pnc;

    // get INetCfg interface
    hr = HrGetINetCfg(FALSE, &pnc);

    if (SUCCEEDED(hr))
    {
        for (int i=0; i<4; i++)
        {
            _tprintf(L"\n%s\n-----------------\n", c_aszClassNames[i]);

            (void) HrShowNetComponents(pnc, c_aguidClass[i]);
        }

        // release INetCfg
        hr = HrReleaseINetCfg(FALSE, pnc);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrGetNextBindingInterface
//
// Purpose:   Enumerate over binding interfaces that constitute
//            the given binding path
//
// Arguments:
//    pncbp  [in]  pointer to INetCfgBindingPath object
//    ppncbi [out] pointer to pointer to INetCfgBindingInterface object
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrGetNextBindingInterface(IN  INetCfgBindingPath* pncbp,
                                  OUT INetCfgBindingInterface** ppncbi)
{
    HRESULT hr=S_OK;
    INetCfgBindingInterface* pncbi=NULL;

    static IEnumNetCfgBindingInterface* pencbi=NULL;

    *ppncbi = NULL;

    // if this is the first call in the enumeration, obtain
    // the IEnumNetCfgBindingInterface interface
    //
    if (!pencbi)
    {
        hr = pncbp->EnumBindingInterfaces(&pencbi);
    }

    if (S_OK == hr)
    {
        ULONG celtFetched;

        // get next binding interface
        hr = pencbi->Next(1, &pncbi, &celtFetched);
    }

    // on the last call (hr == S_FALSE) or on error, release resources

    if (S_OK == hr)
    {
        *ppncbi = pncbi;
    }
    else
    {
        ReleaseObj(pencbi);
        pencbi = NULL;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrGetNextBindingPath
//
// Purpose:   Enumerate over binding paths that start with
//            the specified component
//
// Arguments:
//    pncc              [in]  pointer to INetCfgComponent object
//    dwBindingPathType [in]  type of binding path to retrieve
//    ppncbp            [out] pointer to INetCfgBindingPath interface
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrGetNextBindingPath(IN  INetCfgComponent* pncc,
                             IN  DWORD  dwBindingPathType,
                             OUT INetCfgBindingPath** ppncbp)
{
    HRESULT hr=S_OK;
    INetCfgBindingPath* pncbp=NULL;

    static IEnumNetCfgBindingPath* pebp=NULL;

    *ppncbp = NULL;

    // if this is the first call in the enumeration, obtain
    // the IEnumNetCfgBindingPath interface
    if (!pebp)
    {
        INetCfgComponentBindings* pnccb=NULL;

        hr = pncc->QueryInterface(IID_INetCfgComponentBindings,
                                  (void**) &pnccb);
        if (S_OK == hr)
        {
            hr = pnccb->EnumBindingPaths(dwBindingPathType, &pebp);
            ReleaseObj(pnccb);
        }
    }

    if (S_OK == hr)
    {
        ULONG celtFetched;

        // get next binding path
        hr = pebp->Next(1, &pncbp, &celtFetched);
    }

    // on the last call (hr == S_FALSE) or on error, release resources

    if (S_OK == hr)
    {
        *ppncbp = pncbp;
    }
    else
    {
        ReleaseObj(pebp);
        pebp = NULL;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrShowBindingPath
//
// Purpose:   Display components of a binding path in the format:
//            foo -> bar -> adapter
//
// Arguments:
//    pncbp [in]  pointer to INetCfgBindingPath object
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrShowBindingPath(IN INetCfgBindingPath* pncbp)
{
    HRESULT hr=S_OK;
    INetCfgBindingInterface* pncbi;
    INetCfgComponent* pncc = NULL;
    BOOL fFirstInterface=TRUE;
    PWSTR szComponentId;

    while (SUCCEEDED(hr) &&
           (S_OK == (hr = HrGetNextBindingInterface(pncbp, &pncbi))))
    {
        // for the first (top) interface we need to get the upper as well as
        // the lower component. for other interfaces we need to get
        // only the lower component.

        if (fFirstInterface)
        {
            fFirstInterface = FALSE;
            hr = pncbi->GetUpperComponent(&pncc);
            if (SUCCEEDED(hr))
            {
                // get id so that we can display it
                //
                // for readability of the output, we have used the GetId
                // function. For non net class components, this
                // does not pose a problem. In case of net class components,
                // there may be more than one net adapters of the same type
                // in which case, GetId will return the same string. This will
                // make it impossible to distinguish between two binding
                // paths that end in two distinct identical cards. In such case,
                // it may be better to use the GetInstanceGuid function because
                // it will return unique GUID for each instance of an adapter.
                //
                hr = pncc->GetId(&szComponentId);
                ReleaseObj(pncc);
                if (SUCCEEDED(hr))
                {
                    _tprintf(szComponentId);
                    CoTaskMemFree(szComponentId);
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = pncbi->GetLowerComponent(&pncc);
            if (SUCCEEDED(hr))
            {
                hr = pncc->GetId(&szComponentId);
                if (SUCCEEDED(hr))
                {
                    _tprintf(L" -> %s", szComponentId);
                    CoTaskMemFree(szComponentId);
                }
                ReleaseObj(pncc);
            }
        }
        ReleaseObj(pncbi);
    }

    _tprintf(L"\n");

    if (hr == S_FALSE)
    {
        hr = S_OK;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrShowBindingPathsBelowComponent
//
// Purpose:   Display all binding paths that start with
//            the specified component
//
// Arguments:
//    szComponentId [in]  id of given component (e.g. MS_TCPIP)
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrShowBindingPathsOfComponent(IN PCWSTR szComponentId)
{
    HRESULT hr=S_OK;
    INetCfg* pnc=NULL;
    INetCfgComponent* pncc=NULL;
    INetCfgBindingPath* pncbp=NULL;

    // get INetCfg interface
    hr = HrGetINetCfg(FALSE, &pnc);

    if (SUCCEEDED(hr))
    {
        // get INetCfgComponent for szComponentId
        hr = pnc->FindComponent(szComponentId, &pncc);
        if (S_OK == hr)
        {
            _tprintf(L"Binding paths starting with '%s'\n\n",
                     szComponentId);

            while (S_OK == (hr = HrGetNextBindingPath(pncc, EBP_BELOW,
                                                      &pncbp)))
            {
                // display the binding path
                hr = HrShowBindingPath(pncbp);
                ReleaseObj(pncbp);
            }

            _tprintf(L"Binding paths ending with '%s'\n\n",
                     szComponentId);

            while (S_OK == (hr = HrGetNextBindingPath(pncc, EBP_ABOVE,
                                                      &pncbp)))
            {
                // display the binding path
                hr = HrShowBindingPath(pncbp);
                ReleaseObj(pncbp);
            }

            ReleaseObj(pncc);
        }
        // release INetCfg
        hr = HrReleaseINetCfg(FALSE, pnc);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrGetINetCfg
//
// Purpose:   Initialize COM, create and initialize INetCfg.
//            Obtain write lock if indicated.
//
// Arguments:
//    fGetWriteLock [in]  whether to get write lock
//    ppnc          [in]  pointer to pointer to INetCfg object
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrGetINetCfg(IN BOOL fGetWriteLock,
                     INetCfg** ppnc)
{
    HRESULT hr=S_OK;

    // Initialize the output parameters.
    *ppnc = NULL;

    // initialize COM
    hr = CoInitializeEx(NULL,
                        COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED );

    if (SUCCEEDED(hr))
    {
        // Create the object implementing INetCfg.
        //
        INetCfg* pnc;
        hr = CoCreateInstance(CLSID_CNetCfg, NULL, CLSCTX_INPROC_SERVER,
                              IID_INetCfg, (void**)&pnc);
        if (SUCCEEDED(hr))
        {
            INetCfgLock * pncLock = NULL;
            if (fGetWriteLock)
            {
                // Get the locking interface
                hr = pnc->QueryInterface(IID_INetCfgLock,
                                         (LPVOID *)&pncLock);
                if (SUCCEEDED(hr))
                {
                    // Attempt to lock the INetCfg for read/write
                    static const ULONG c_cmsTimeout = 15000;
                    static const WCHAR c_szSampleNetcfgApp[] =
                        L"Sample Netcfg Application (netcfg.exe)";
                    PWSTR szLockedBy;

                    hr = pncLock->AcquireWriteLock(c_cmsTimeout,
                                                   c_szSampleNetcfgApp,
                                                   &szLockedBy);
                    if (S_FALSE == hr)
                    {
                        hr = NETCFG_E_NO_WRITE_LOCK;
                        _tprintf(L"Could not lock INetcfg, it is already locked by '%s'", szLockedBy);
                    }
                }
            }

            if (SUCCEEDED(hr))
            {
                // Initialize the INetCfg object.
                //
                hr = pnc->Initialize(NULL);
                if (SUCCEEDED(hr))
                {
                    *ppnc = pnc;
                    pnc->AddRef();
                }
                else
                {
                    // initialize failed, if obtained lock, release it
                    if (pncLock)
                    {
                        pncLock->ReleaseWriteLock();
                    }
                }
            }
            ReleaseObj(pncLock);
            ReleaseObj(pnc);
        }

        if (FAILED(hr))
        {
            CoUninitialize();
        }
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  HrReleaseINetCfg
//
// Purpose:   Uninitialize INetCfg, release write lock (if present)
//            and uninitialize COM.
//
// Arguments:
//    fHasWriteLock [in]  whether write lock needs to be released.
//    pnc           [in]  pointer to INetCfg object
//
// Returns:   S_OK on success, otherwise an error code
//
// Notes:
//
HRESULT HrReleaseINetCfg(BOOL fHasWriteLock, INetCfg* pnc)
{
    HRESULT hr = S_OK;

    // uninitialize INetCfg
    hr = pnc->Uninitialize();

    // if write lock is present, unlock it
    if (SUCCEEDED(hr) && fHasWriteLock)
    {
        INetCfgLock* pncLock;

        // Get the locking interface
        hr = pnc->QueryInterface(IID_INetCfgLock,
                                 (LPVOID *)&pncLock);
        if (SUCCEEDED(hr))
        {
            hr = pncLock->ReleaseWriteLock();
            ReleaseObj(pncLock);
        }
    }

    ReleaseObj(pnc);

    CoUninitialize();

    return hr;
}

//+---------------------------------------------------------------------------
//
// Function:  ShowMessage
//
// Purpose:   Helper function to display a message in verbose mode.
//            If not in verbose mode, do nothing.
//
// Arguments:
//    szMsg [in]  message to display
//
// Returns:   None
//
// Notes:
//
void ShowMessage(IN PCWSTR szMsg, ...)
{
    extern BOOL g_fVerbose;

    if (g_fVerbose)
    {
        va_list arglist;

        va_start(arglist, szMsg);
        _vtprintf(szMsg, arglist);
        _tprintf(L"\n");
        fflush(stdout);
        va_end(arglist);
    }
}

//+---------------------------------------------------------------------------
//
// Function:  ShowHrMessage
//
// Purpose:   Helper function to display the status of the last action
//            as indicated by the given HRESULT
//
// Arguments:
//    hr [in]  status code
//
// Returns:   None
//
// Notes:
//
void ShowHrMessage(IN HRESULT hr)
{
    if (SUCCEEDED(hr))
    {
        ShowMessage(L"...done");
        if (NETCFG_S_REBOOT == hr)
        {
            ShowMessage(L"*** You need to reboot your computer for this change to take effect ***");
        }
    }
    else
    {
        ShowMessage(L"..failed. Error code: 0x%lx", hr);
    }
}

