|
|
//+---------------------------------------------------------------------------
//
// 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", 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); } }
|