2025-11-28 00:35:46 +09:00

957 lines
27 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
////////////////////////////////////////////////////////////////////////////////
// Public Headers
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
#include <propkey.h>
#include <functiondiscoverykeys.h>
#include <setupapi.h>
#include <CommonControls.h>
#include <new>
// Needed before including devpkey.h as there's no lib with the DEVPKEY objects
#include <initguid.h>
#include <devpkey.h>
// Sample Headers
#include "CDevicePropertyPage.h"
#include "common.h"
#include "resource.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
//
// Struct used to pass data from AddPages -> Window Proc function
//
struct PROPSINPUT
{
IShellItem2* pShellItem;
HICON hIcon;
// Add extra data needed here.
};
//------------------------------------------------------------------------------
// CDevicePropertyPage::CDevicePropertyPage (Constructor)
//------------------------------------------------------------------------------
CDevicePropertyPage::CDevicePropertyPage():
m_cRef(1),
m_pShellItem(NULL)
{
DllIncLockCount();
}
//------------------------------------------------------------------------------
// CDevicePropertyPage::~CDevicePropertyPage (Destructor)
//------------------------------------------------------------------------------
CDevicePropertyPage::~CDevicePropertyPage()
{
if( NULL != m_pShellItem )
{
m_pShellItem->Release();
}
DllDecLockCount();
}
//
// IShellPropSheetExt
//
//------------------------------------------------------------------------------
// CDevicePropertyPage::AddPages
//
// This method is called by the Windows Shell to give oportunity to
// register one or more property pages with the property sheet. Minimal
// work should be done here. Essentially only enough work to figure out
// whether the property page should be added or not (i.e. no critical data
// is missing). All other work should be done in the DlgProc function
// when the WM_INITDIALOG message is sent.
//------------------------------------------------------------------------------
IFACEMETHODIMP CDevicePropertyPage::AddPages(
__in LPFNADDPROPSHEETPAGE pfnAddPage,
__in LPARAM lParam
)
{
HPROPSHEETPAGE hPage;
HRESULT hr = S_OK;
PROPSHEETPAGE psp = {0};
PROPSINPUT* pPropsInput = NULL;
if( NULL == pfnAddPage )
{
return E_INVALIDARG;
}
//
// Allocate the PROPSINPUT struct
//
pPropsInput = new (std::nothrow) PROPSINPUT;
if( NULL == pPropsInput )
{
hr = E_OUTOFMEMORY;
}
if( S_OK == hr )
{
//
// Fill out the PROPSHEETPAGE structure
//
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_USECALLBACK;
psp.hInstance = g_hInstance;
psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_DEVICE);
psp.pfnDlgProc = PropPageDlgProc;
psp.lParam = reinterpret_cast<LPARAM>( pPropsInput );
psp.pfnCallback = PropPageDlgCleanup;
//
// Give a reference to the shell item to the callback data
//
hr = m_pShellItem->QueryInterface( &pPropsInput->pShellItem );
}
//
// Grab the icon from the shell item
//
if( S_OK == hr )
{
hr = GetIconFromItem( m_pShellItem, SHIL_LARGE, &pPropsInput->hIcon );
}
//
// Create the page
//
hPage = CreatePropertySheetPage( &psp );
if( NULL == hPage )
{
hr = E_OUTOFMEMORY;
}
//
// Give the shell the page
//
if( S_OK == hr &&
!pfnAddPage( hPage, lParam ) )
{
hr = E_FAIL;
}
//
// As the documentation for AddPages describes, you can "request" that your
// page is the one visibile when the property sheet is loaded. You can do
// this by giving the index of the page you added (1 based). In this sample
// we just added one page, so we can return 1 instead of the HR (assuming
// we didn't fail) to request our page to be the one visible on launch.
// Keep in mind it is a "request" and is not guaranteed.
//
if( S_OK == hr )
{
hr = static_cast<HRESULT>(1);
}
else
{
if( NULL != hPage )
{
DestroyPropertySheetPage( hPage );
}
if( NULL != pPropsInput->pShellItem )
{
pPropsInput->pShellItem->Release();
}
if( NULL != pPropsInput->hIcon )
{
DestroyIcon( pPropsInput->hIcon );
}
}
return hr;
}// CDevicePropertyPage::AddPages
//------------------------------------------------------------------------------
// CDevicePropertyPage::ReplacePage
// Not supported.
//------------------------------------------------------------------------------
IFACEMETHODIMP CDevicePropertyPage::ReplacePage(
__in UINT uPageID,
__in LPFNADDPROPSHEETPAGE pfnReplacePage,
__in LPARAM lParam
)
{
UNREFERENCED_PARAMETER( uPageID );
UNREFERENCED_PARAMETER( pfnReplacePage );
UNREFERENCED_PARAMETER( lParam );
return E_NOTIMPL;
}// CDevicePropertyPage::ReplacePage
//
// IShellExtInit
//
//------------------------------------------------------------------------------
// CDevicePropertyPage::Initialize
//
// This is the first method that the Shell calls after it creates an
// instance of a property sheet extension.
//------------------------------------------------------------------------------
IFACEMETHODIMP CDevicePropertyPage::Initialize(
__in PCIDLIST_ABSOLUTE pidlFolder,
__in IDataObject* pdtobj,
__in HKEY hkeyProgID
)
{
UNREFERENCED_PARAMETER( hkeyProgID );
UNREFERENCED_PARAMETER( pidlFolder );
if( NULL != m_pShellItem )
{
m_pShellItem->Release();
m_pShellItem = NULL;
}
//
// Grab the interface to the shell item
//
return SHGetItemFromObject(
pdtobj,
__uuidof(IShellItem2),
reinterpret_cast<void**>(&m_pShellItem)
);
}// CDevicePropertyPage::Initialize
//
// IUnknown
//
//------------------------------------------------------------------------------
// CDevicePropertyPage::QueryInterface
//------------------------------------------------------------------------------
IFACEMETHODIMP CDevicePropertyPage::QueryInterface(
__in REFIID riid,
__deref_out void** ppvObject
)
{
HRESULT hr = S_OK;
if( NULL == ppvObject )
{
return E_INVALIDARG;
}
*ppvObject = NULL;
if( __uuidof(IShellExtInit) == riid )
{
*ppvObject = static_cast<IShellExtInit*>(this);
AddRef();
}
else if( __uuidof(IShellPropSheetExt) == riid )
{
*ppvObject = static_cast<IShellPropSheetExt*>(this);
AddRef();
}
else if( __uuidof(IUnknown) == riid )
{
*ppvObject = static_cast<IUnknown*>(static_cast<IShellExtInit*>(this));
AddRef();
}
else
{
hr = E_NOINTERFACE;
}
return hr;
}// CDevicePropertyPage::QueryInterface
//------------------------------------------------------------------------------
// CDevicePropertyPage::AddRef
//------------------------------------------------------------------------------
IFACEMETHODIMP_(ULONG) CDevicePropertyPage::AddRef()
{
return InterlockedIncrement( &m_cRef );
}// CDevicePropertyPage::AddRef
//------------------------------------------------------------------------------
// CDevicePropertyPage::Release
//------------------------------------------------------------------------------
IFACEMETHODIMP_(ULONG) CDevicePropertyPage::Release()
{
LONG cRef = InterlockedDecrement( &m_cRef );
if( 0 == cRef )
{
delete this;
}
return cRef;
}// CDevicePropertyPage::Release
//
// Private class methods
//
//------------------------------------------------------------------------------
// CDevicePropertyPage::GetIconFromItem [STATIC FUNC]
//
// Gets a handle to the icon of the shell item. phIcon needs to be cleaned
// up with DestroyIcon() when done.
//------------------------------------------------------------------------------
HRESULT CDevicePropertyPage::GetIconFromItem(
__in IShellItem* pShellItem,
__in int iImageList,
__out HICON* phIcon
)
{
HRESULT hr = S_OK;
int iIcon = 0;
PITEMID_CHILD pidl = NULL;
IImageList* pImageList = NULL;
IParentAndItem* pParentAndItem = NULL;
IShellFolder* pShellFolder = NULL;
*phIcon = NULL;
hr = pShellItem->QueryInterface( &pParentAndItem );
if( S_OK == hr )
{
hr = pParentAndItem->GetParentAndItem( NULL, &pShellFolder, &pidl );
}
if( S_OK == hr )
{
hr = SHGetImageList(
iImageList,
__uuidof(IImageList),
reinterpret_cast<void**>(&pImageList)
);
}
if( S_OK == hr )
{
iIcon = SHMapPIDLToSystemImageListIndex( pShellFolder, pidl, NULL );
hr = pImageList->GetIcon( iIcon, 0, phIcon );
}
//
// Cleanup
//
if( NULL != pImageList )
{
pImageList->Release();
}
if( NULL != pidl )
{
ILFree( pidl );
}
if( NULL != pShellFolder )
{
pShellFolder->Release();
}
if( NULL != pParentAndItem )
{
pParentAndItem->Release();
}
return hr;
}// CDevicePropertyPage::GetIconFromItem
//------------------------------------------------------------------------------
// CDevicePropertyPage::PropPageDlgProc [STATIC FUNC]
//
// This function is used to process windows messages that are sent to the
// property page.
//------------------------------------------------------------------------------
INT_PTR CALLBACK CDevicePropertyPage::PropPageDlgProc(
__in HWND hWndDlg,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
UNREFERENCED_PARAMETER( wParam );
HRESULT hr = S_OK;
PROPSINPUT* pPropsInput = NULL;
LPPROPSHEETPAGE ppsp = NULL;
if( WM_INITDIALOG == uMsg )
{
ppsp = reinterpret_cast<LPPROPSHEETPAGE>(lParam);
pPropsInput = reinterpret_cast<PROPSINPUT*>(ppsp->lParam);
//
// Set the Icon if available
//
if( NULL != pPropsInput->hIcon )
{
SendMessage(
GetDlgItem( hWndDlg, IDC_DEVICE_ICON ),
STM_SETICON,
reinterpret_cast<WPARAM>(pPropsInput->hIcon),
0
);
}
//
// Populate the page with properties available on the shell object
//
hr = PopulateShellProperties( hWndDlg, pPropsInput->pShellItem );
//
// Populate the page with properties available directly from the
// devnode(s)
//
if( S_OK == hr )
{
hr = PopulateDevnodeProperties( hWndDlg, pPropsInput->pShellItem );
}
}
return hr;
}// CDevicePropertyPage::PropPageDlgProc
//------------------------------------------------------------------------------
// CDevicePropertyPage::PropPageDlgCleanup [STATIC FUNC]
//
// This function is called just before the page is created and when it's
// about to be destroyed. In this sample, it will just be used to free the
// pidl.
//------------------------------------------------------------------------------
UINT CALLBACK CDevicePropertyPage::PropPageDlgCleanup(
__in HWND hwnd,
__in UINT uMsg,
__in LPPROPSHEETPAGE ppsp
)
{
UNREFERENCED_PARAMETER( hwnd );
PROPSINPUT* pPropsInput = reinterpret_cast<PROPSINPUT*>(ppsp->lParam);
if( PSPCB_RELEASE == uMsg &&
NULL != pPropsInput )
{
if( NULL != pPropsInput->pShellItem )
{
pPropsInput->pShellItem->Release();
}
if( NULL != pPropsInput->hIcon )
{
DestroyIcon( pPropsInput->hIcon );
}
delete pPropsInput;
}
return 1;
}// CDevicePropertyPage::PropPageDlgCleanup
//------------------------------------------------------------------------------
// CDevicePropertyPage::PopulateShellProperties [STATIC FUNC]
//
// Populates the property page with the properties available directly from
// the shell item object. Other properties have to be farmed from the
// devnodes that make up the device. This process is shown in
// PopulateDevnodeProperties.
//------------------------------------------------------------------------------
HRESULT CDevicePropertyPage::PopulateShellProperties(
__in HWND hWndDlg,
__in IShellItem2* pShellItem
)
{
HRESULT hr = S_OK;
PWSTR pszValue = {0};
GUID guidContainerID = {0};
//
// Start filling out the properties on the property page.
// If any failure occurs, the rest of the properties will be skipped.
//
// If this pattern isn't ideal for your code--for instance you don't
// need all properties available all the time--then be sure to adjust
// the error checking pattern.
//
//
// Device Name
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_ItemNameDisplay, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_NAME_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Container Id
//
if( S_OK == hr &&
S_OK == pShellItem->GetCLSID( PKEY_Devices_ContainerId, &guidContainerID ) &&
S_OK == StringFromCLSID( guidContainerID, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_CONTAINERID_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Manufacturer
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_Devices_Manufacturer, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_MANUFACTURER_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Model Name
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_Devices_ModelName, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_MODELNAME_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Model Number
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_Devices_ModelNumber, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_MODELNUMBER_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Device Description 1
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_Devices_DeviceDescription1, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_DESCRIPTION1_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
//
// Device Description 2
//
if( S_OK == hr &&
S_OK == pShellItem->GetString( PKEY_Devices_DeviceDescription2, &pszValue ))
{
SetDlgItemText( hWndDlg, IDC_DESCRIPTION2_FIELD, pszValue );
CoTaskMemFree( pszValue );
}
return hr;
}// CDevicePropertyPage::PopulateShellProperties
//------------------------------------------------------------------------------
// CDevicePropertyPage::PopulateDevnodeProperties [STATIC FUNC]
//
// Populates the property page with some properties that come from the
// devnode(s) of the device directly. When implementing the property page
// for your device, this method is needed for all properties that cannot
// be obtained directly from the shell item object.
//
// Note there are two pieces of contextual information from the shell
// object you can use to find the devnodes for your device and farm extra
// properties and/or communicate with your device.
//
// 1) Function Paths (PKEY_Devices_FunctionPaths)
// This property is a vector of strings of the Device Instance Paths
// for your device's devnodes. This function below will show how to
// use this property to access properties off the devnodes.
//
// 2) Interface Paths (PKEY_Devices_InterfacePaths)
// This property is a vectory of strings of the Device Interface Paths
// of all your device's devnodes. Use of this property use not shown in
// this sample, but if you find the property helpful for your scenario
// you can use it.
//------------------------------------------------------------------------------
HRESULT CDevicePropertyPage::PopulateDevnodeProperties(
__in HWND hWndDlg,
__in IShellItem2* pShellItem
)
{
DWORD cbBuffer = 0;
HDEVINFO devInfo = {0};
SP_DEVINFO_DATA devInfoData = {0};
DEVPROPTYPE devPropType = 0;
HRESULT hr = S_OK;
SYSTEMTIME installTime = {0};
PBYTE pBuffer = NULL;
PROPVARIANT pv = {0};
PCWSTR pszHwid = NULL;
WCHAR szInstallTime[1024] = {0};
PropVariantInit( &pv );
//
// First get the Function Paths (Device Instance Paths) needed to grab the
// devnode info directly from PnP.
//
if( S_OK == hr )
{
hr = pShellItem->GetProperty( PKEY_Devices_FunctionPaths, &pv );
}
if( S_OK == hr &&
((VT_VECTOR | VT_LPWSTR) != pv.vt ||
( 0 == pv.calpwstr.cElems ) ) )
{
// Function Paths doesn't exist or is the wrong type or empty.
// This should never happen, but its good practice to check anyway.
hr = HRESULT_FROM_WIN32( ERROR_NOT_FOUND );
}
//
// For simplicity in the sample, we'll just work with the first path in the
// list. i.e. the first devnode in the list.
//
// IMPORTANT: In a real scenario, you need to keep in mind that your device
// shown in the Devices and Printers folder is likely made up of one or more
// Device Functions (devnodes). In this case, you may not be able to get all
// properties from just any devnode. You'll need to look at all devnodes and
// figure out which one contains the properties you're after. In this sample
// we're just attempting to get a set of properties from the devnode who's
// Device Instance Path is in the FunctionPaths list retreived from the shell
// object.
//
//
// Create an empty HDEVINFO set to use for the first devnode's info
//
if( S_OK == hr )
{
devInfo = SetupDiCreateDeviceInfoList( NULL, NULL );
if( INVALID_HANDLE_VALUE == devInfo )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
//
// Open the devnode's info
//
if( S_OK == hr )
{
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if( FALSE == SetupDiOpenDeviceInfo(
devInfo,
pv.calpwstr.pElems[0],
NULL,
0,
&devInfoData
) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
//
// Now populate the property page with some properties from the devnode
//
// You can ask for properties defined in devpkey.h. Some keys in
// FunctionDiscoveryKey.h are also available on devnodes; For example,
// devnodes from PnP-X (Network Devices) will have PKEY_PNPX properties
// set on them. These can be retreived with SetupDi* calls as well.
// PROPERTYKEY can be safely cast to DEVPROPKEY. However, keep in mind
// the types are defined differently. Variant types are essentially a
// superset of DEVPROPTYPEs. The mapping on many property types is
// straight forward. DEVPROP_TYPE_STRING and VT_LPWSTR
// are the same, for example. Below we'll get a PnP-X property so it's
// clear how this works.
//
// One case where the mapping isn't exact, is VT_VECTOR | VT_LPWSTR
// (vector of strings), which in the devnode is stored as a
// DEVPROP_TYPE_STRING_LIST (REG_MULTI_SZ style string list). Keep this
// in mind when asking for PKEY types from a devnode vs. DEVPKEY types.
//
//
// Get the hardware ids
//
if( S_OK == hr )
{
// Get the required buffer size
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
&DEVPKEY_Device_HardwareIds,
&devPropType,
NULL,
0,
&cbBuffer,
0
) &&
ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr )
{
pBuffer = new (std::nothrow) BYTE[cbBuffer];
if( NULL == pBuffer )
{
hr = E_OUTOFMEMORY;
}
ZeroMemory( pBuffer, cbBuffer );
}
if( S_OK == hr )
{
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
&DEVPKEY_Device_HardwareIds,
&devPropType,
pBuffer,
cbBuffer,
NULL,
0
) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr &&
DEVPROP_TYPE_STRING_LIST == devPropType )
{
//
// The UI only has slots for up to three hardware ids, so we'll
// just set up to the first three.
//
pszHwid = reinterpret_cast<PWSTR>(pBuffer);
if( NULL != *pszHwid )
{
SetDlgItemText(
hWndDlg,
IDC_HARDWAREID_FIELD1,
pszHwid
);
pszHwid = pszHwid + wcslen(pszHwid) + 1;
}
if( NULL != *pszHwid )
{
SetDlgItemText(
hWndDlg,
IDC_HARDWAREID_FIELD2,
pszHwid
);
pszHwid = pszHwid + wcslen(pszHwid) + 1;
}
if( NULL != *pszHwid )
{
SetDlgItemText(
hWndDlg,
IDC_HARDWAREID_FIELD3,
pszHwid
);
}
}
if( NULL != pBuffer )
{
delete[] pBuffer;
pBuffer = NULL;
}
//
// Get the install date
//
if( S_OK == hr )
{
// Get the required buffer size
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
&DEVPKEY_Device_InstallDate,
&devPropType,
NULL,
0,
&cbBuffer,
0
) &&
ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr )
{
pBuffer = new (std::nothrow) BYTE[cbBuffer];
if( NULL == pBuffer )
{
hr = E_OUTOFMEMORY;
}
}
if( S_OK == hr )
{
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
&DEVPKEY_Device_InstallDate,
&devPropType,
pBuffer,
cbBuffer,
NULL,
0
) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
else if( DEVPROP_TYPE_FILETIME != devPropType )
{
hr = E_INVALIDARG;
}
}
if( S_OK == hr )
{
if( FALSE == FileTimeToSystemTime(
reinterpret_cast<FILETIME*>(pBuffer),
&installTime ))
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr )
{
if( 0 == GetDateFormatEx(
LOCALE_NAME_USER_DEFAULT,
DATE_AUTOLAYOUT | DATE_LONGDATE,
&installTime,
NULL,
szInstallTime,
ARRAY_SIZE(szInstallTime),
NULL
))
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr )
{
SetDlgItemText( hWndDlg, IDC_INSTALLDATE_FIELD, szInstallTime );
}
if( NULL != pBuffer )
{
delete[] pBuffer;
pBuffer = NULL;
}
//
// Now get PKEY_** property instead of a DEVPKEY property
// to show how that's done. This would typically just be done
// for PNPX properties, but other properties in FunctionDisocovery.h
// might be available on the devnode.
//
// Let's attempt to get the IP Address, though it won't be available on
// Mice (which is what this sample property page binds to). It at least
// shows how the process would work. Also, as stated above, only *some*
// of the devnodes that make up your PnP-X device may contain the
// IP Address, so you might have to look through all the devnodes by
// iterating the FunctionPaths list.
//
// This property won't be set on the property page itself since a mouse
// will never have an IP Address.
//
if( S_OK == hr )
{
// Get the required buffer size
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
(const DEVPROPKEY*)&PKEY_PNPX_IpAddress,
&devPropType,
NULL,
0,
&cbBuffer,
0
) &&
ERROR_INSUFFICIENT_BUFFER != GetLastError() )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr )
{
pBuffer = new (std::nothrow) BYTE[cbBuffer];
if( NULL == pBuffer )
{
hr = E_OUTOFMEMORY;
}
}
if( S_OK == hr )
{
if( FALSE == SetupDiGetDeviceProperty(
devInfo,
&devInfoData,
(const DEVPROPKEY*)&PKEY_PNPX_IpAddress,
&devPropType,
pBuffer,
cbBuffer,
NULL,
0
) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if( S_OK == hr &&
DEVPROP_TYPE_STRING == devPropType
)
{
// pBuffer now contains the IP Address if this is a PnP-X device and
// this was one of the devnodes that had the property set.
}
//
// Cleanup
//
if( NULL != pBuffer )
{
delete[] pBuffer;
}
if( INVALID_HANDLE_VALUE != devInfo )
{
(void) SetupDiDestroyDeviceInfoList( devInfo );
}
PropVariantClear( &pv );
return hr;
}// CDevicePropertyPage::PopulateDevnodeProperties