windows-nt/Source/XPSP1/NT/net/wlbs/installpack/snetcfg.cpp
2020-09-26 16:20:57 +08:00

1069 lines
29 KiB
C++

//+---------------------------------------------------------------------------
//
// 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:
//
HRESULT 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);
}
return 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", szComponentId);
}
// 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;
DWORD cchRequiredSize;
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;
PCWSTR szClassName;
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);
}
}