Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2509 lines
67 KiB

/*++
Copyright (C) Microsoft Corporation
Module Name:
machine.cpp
Abstract:
This module implements CDevInfoList, CMachine and CMachineList
Author:
William Hsieh (williamh) created
Revision History:
--*/
#include "devmgr.h"
extern "C" {
#include <initguid.h>
#include <dbt.h>
#include <devguid.h>
#include <wdmguid.h>
}
CONST TCHAR* DEVMGR_NOTIFY_CLASS_NAME = TEXT("DevMgrNotifyClass");
CONST TCHAR* DEVMGR_REFRESH_MSG = TEXT("DevMgrRefreshOn");
//
// The constant is the size we use to allocate GUID list from within
// stack when we have to build a GUID list. The aim of this is
// that buiding a guid list take time and in many case, a minimum
// buffer should retrive all of them. We do not want to get
// the size first, allocate buffer and get it again.
// 64 looks to be fair enough value because there are not
// many classes out there today (and maybe, in the future).
//
const int GUID_LIST_INIT_SIZE = 64;
//
// CDevInfoList implementation
//
BOOL
CDevInfoList::DiGetExtensionPropSheetPage(
PSP_DEVINFO_DATA DevData,
LPFNADDPROPSHEETPAGE pfnAddPropSheetPage,
DWORD PageType,
LPARAM lParam
)
{
SP_PROPSHEETPAGE_REQUEST PropPageRequest;
LPFNADDPROPSHEETPAGES AddPropPages;
PropPageRequest.cbSize = sizeof(PropPageRequest);
PropPageRequest.PageRequested = PageType;
PropPageRequest.DeviceInfoSet = m_hDevInfo;
PropPageRequest.DeviceInfoData = DevData;
if (SPPSR_SELECT_DEVICE_RESOURCES == PageType) {
HINSTANCE hModule = ::GetModuleHandle(TEXT("setupapi.dll"));
if (hModule) {
AddPropPages = (LPFNADDPROPSHEETPAGES)GetProcAddress(hModule, "ExtensionPropSheetPageProc");
if (AddPropPages) {
if (AddPropPages(&PropPageRequest, pfnAddPropSheetPage, lParam)) {
return TRUE;
}
}
}
}
return FALSE;
}
BOOL
CDevInfoList::InstallDevInst(
HWND hwndParent,
LPCTSTR DeviceId,
BOOL UpdateDriver,
DWORD* pReboot
)
{
BOOL Result = FALSE;
HINSTANCE hLib = LoadLibrary(TEXT("newdev.dll"));
LPFNINSTALLDEVINST InstallDevInst;
DWORD Status = ERROR_SUCCESS;
if (hLib) {
InstallDevInst = (LPFNINSTALLDEVINST)GetProcAddress(hLib, "InstallDevInst");
if (InstallDevInst) {
Result = (*InstallDevInst)(hwndParent, DeviceId, UpdateDriver,
pReboot);
Status = GetLastError();
}
FreeLibrary(hLib);
}
//
// We need to put back the error code that was set by newdev.dll's InstallDevInst
// API. This last error gets overwritten by FreeLibrary which we don't care about.
//
SetLastError(Status);
return Result;
}
BOOL
CDevInfoList::RollbackDriver(
HWND hwndParent,
LPCTSTR RegistryKeyName,
DWORD Flags,
DWORD* pReboot
)
{
BOOL Result = FALSE;
HINSTANCE hLib = LoadLibrary(TEXT("newdev.dll"));
LPFNROLLBACKDRIVER RollbackDriver;
if (hLib) {
RollbackDriver = (LPFNROLLBACKDRIVER)GetProcAddress(hLib, "RollbackDriver");
if (RollbackDriver) {
Result = (*RollbackDriver)(hwndParent, RegistryKeyName, Flags, pReboot);
}
FreeLibrary(hLib);
}
return Result;
}
DWORD
CDevInfoList::DiGetFlags(
PSP_DEVINFO_DATA DevData
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
return dip.Flags;
}
return 0;
}
DWORD
CDevInfoList::DiGetExFlags(
PSP_DEVINFO_DATA DevData
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
return dip.FlagsEx;
}
return 0;
}
BOOL
CDevInfoList::DiTurnOnDiFlags(
PSP_DEVINFO_DATA DevData,
DWORD FlagsMask
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
dip.Flags |= FlagsMask;
return DiSetDeviceInstallParams(DevData, &dip);
}
return FALSE;
}
BOOL
CDevInfoList::DiTurnOffDiFlags(
PSP_DEVINFO_DATA DevData,
DWORD FlagsMask
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
dip.Flags &= ~FlagsMask;
return DiSetDeviceInstallParams(DevData, &dip);
}
return FALSE;
}
BOOL
CDevInfoList::DiTurnOnDiExFlags(
PSP_DEVINFO_DATA DevData,
DWORD FlagsMask
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
dip.FlagsEx |= FlagsMask;
return DiSetDeviceInstallParams(DevData, &dip);
}
return FALSE;
}
BOOL
CDevInfoList::DiTurnOffDiExFlags(
PSP_DEVINFO_DATA DevData,
DWORD FlagsMask
)
{
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = sizeof(dip);
if (DiGetDeviceInstallParams(DevData, &dip)) {
dip.FlagsEx &= ~FlagsMask;
return DiSetDeviceInstallParams(DevData, &dip);
}
return FALSE;
}
void
CDevInfoList::DiDestroyDeviceInfoList()
{
if (INVALID_HANDLE_VALUE != m_hDevInfo) {
SetupDiDestroyDeviceInfoList(m_hDevInfo);
m_hDevInfo = INVALID_HANDLE_VALUE;
}
}
/////////////////////////////////////////////////////////////////////
//// CMachine implementation
////
//
// For every single instance of DevMgr.dll, we maintain a CMachine list
// from which different instances of IComponentData(and IComponent) will
// attach to. The objects created(CDevice, Class, HDEVINFO and etc) are
// shared by all the attached IComponentData and IComponent(the implementation
// use CFolder as the controlling identify).
// Everything changed to the CMachine or one of its object will inform
// all the attached CFolders which would pass the information down to their
// sub-objects(CResultView).
// Imaging that you have two Device Manager window in the same console
// and you do a refresh on one the window. The other window must also
// do a refresh after the first one is done. Since both windows shares
// the same machine(and will get the same notification when machine states
// changed), we can keep the two windows in sync.
/////////////////////////////////////////////////////////////////////
CMachine::CMachine(
LPCTSTR pMachineName
)
{
InitializeCriticalSection(&m_CriticalSection);
InitializeCriticalSection(&m_PropertySheetCriticalSection);
InitializeCriticalSection(&m_ChildMachineCriticalSection);
//
// Get various privilege levels, like if the user is an administrator
// or if the user is a Guest.
//
g_IsAdmin = pSetupIsUserAdmin();
m_UserIsAGuest = SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS);
m_RefreshDisableCounter = 0;
m_RefreshPending = FALSE;
m_pComputer = NULL;
m_hMachine = NULL;
m_ParentMachine = NULL;
m_Initialized = FALSE;
TCHAR LocalName[MAX_PATH + 1];
DWORD dwSize = ARRAYLEN(LocalName);
if (!GetComputerName(LocalName, &dwSize)) {
LocalName[0] = _T('\0');
}
m_strMachineFullName.Empty();
m_strMachineDisplayName.Empty();
if (pMachineName && _T('\0') != *pMachineName) {
if ((_T('\\') == pMachineName[0]) && (_T('\\') == pMachineName[1])) {
m_strMachineDisplayName = &pMachineName[2];
m_strMachineFullName = pMachineName;
} else {
m_strMachineDisplayName = pMachineName;
m_strMachineFullName = TEXT("\\\\");
m_strMachineFullName+=pMachineName;
}
m_IsLocal = (0 == m_strMachineDisplayName.CompareNoCase(LocalName));
}
else {
//
// Local machine
//
m_strMachineDisplayName = LocalName;
m_strMachineFullName = TEXT("\\\\") + m_strMachineDisplayName;
m_IsLocal = TRUE;
}
m_hwndNotify = NULL;
m_msgRefresh = 0;
m_ShowNonPresentDevices = FALSE;
m_PropertySheetShoudDestroy = FALSE;
TCHAR Buffer[MAX_PATH];
DWORD BufferLen;
//
// If the environment variable DEVMGR_SHOW_NONPRESENT_DEVICES does exist and it
// is not 0 then we will show Phantom devices.
//
if (((BufferLen = ::GetEnvironmentVariable(TEXT("DEVMGR_SHOW_NONPRESENT_DEVICES"),
Buffer,
ARRAYLEN(Buffer))) != 0) &&
((BufferLen > 1) ||
(lstrcmp(Buffer, TEXT("0"))))) {
m_ShowNonPresentDevices = TRUE;
}
}
BOOL
CMachine::Initialize(
IN HWND hwndParent,
IN LPCTSTR DeviceId, OPTIONAL
IN LPGUID ClassGuid OPTIONAL
)
{
BOOL Result = TRUE;
m_hwndParent = hwndParent;
if (DeviceId && _T('\0') == *DeviceId) {
DeviceId = NULL;
}
HCURSOR hCursorOld;
hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
if (DeviceId || ClassGuid) {
if (m_Initialized) {
return TRUE;
}
if (CreateClassesAndDevices(DeviceId, ClassGuid)) {
m_Initialized = TRUE;
}
} else {
if (m_Initialized && m_hwndNotify && IsWindow(m_hwndNotify)) {
return TRUE;
}
//
// We are ready for device change notification, create the notify window
//
Result = CreateNotifyWindow();
m_Initialized = TRUE;
ScheduleRefresh();
}
if (hCursorOld) {
SetCursor(hCursorOld);
}
return Result;
}
BOOL
CMachine::ScheduleRefresh()
{
Lock();
//
// Only queue the the request if there is no requests outstanding
// and we have a valid window handle/message to the notify window.
//
if (!m_RefreshPending && m_hwndNotify && m_msgRefresh) {
//
// Broadcast the message so that every instance runs on
// the computer get the notification
//
::PostMessage(HWND_BROADCAST, m_msgRefresh, 0, 0);
}
Unlock();
return TRUE;
}
//
// This function creates a data window to receive WM_DEVICECHANGE notification
// so that we can refresh the device tree. It also registers a private
// message so that anybody can post a refresh request.
//
BOOL
CMachine::CreateNotifyWindow()
{
WNDCLASS wndClass;
//
// Lets see if the class has been registered.
//
if (!GetClassInfo(g_hInstance, DEVMGR_NOTIFY_CLASS_NAME, &wndClass)) {
//
// Register the class
//
memset(&wndClass, 0, sizeof(wndClass));
wndClass.lpfnWndProc = dmNotifyWndProc;
wndClass.hInstance = g_hInstance;
wndClass.lpszClassName = DEVMGR_NOTIFY_CLASS_NAME;
if (!RegisterClass(&wndClass)) {
return FALSE;
}
}
//
// Register a private message for refresh. The name must contain
// the target machine name so that every machine has its own message.
//
String strMsg = DEVMGR_REFRESH_MSG;
strMsg += m_strMachineDisplayName;
m_msgRefresh = RegisterWindowMessage(strMsg);
if (m_msgRefresh) {
//
// Create a data window.
//
m_hwndNotify = CreateWindowEx(WS_EX_TOOLWINDOW, DEVMGR_NOTIFY_CLASS_NAME,
TEXT(""),
WS_DLGFRAME|WS_BORDER|WS_DISABLED,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, NULL, NULL, g_hInstance, (void*)this);
return(NULL != m_hwndNotify);
}
return FALSE;
}
BOOL
CMachine::DestroyNotifyWindow()
{
if (m_hwndNotify && IsWindow(m_hwndNotify)) {
::DestroyWindow(m_hwndNotify);
m_hwndNotify = NULL;
return TRUE;
}
//
// The notify window does not exist.
//
return FALSE;
}
//
// This is the WM_DEVICECHANGE window procedure running in the main thread
// context. It listens to two messages:
// (1). WM_DEVICECHANGE broadcasted by Configuration Manager on device
// addition/removing.
// (2). Private refresh message broadcasted by different instance
// of Device Manager targeting on the same machine.
// On WM_CREATE, we participate the WM_DEVICECHANGE notification chain
// while on WM_DESTROY, we detach oursleves from the chain.
// There are occasions that we have to detach and re-attach to the
// chain duing the window life time, for example, during device uninstallation
// or during re-enumeration. The EnableFresh function is the place
// that does the attach/detach.
//
LRESULT
dmNotifyWndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
CMachine* pThis;
pThis = (CMachine*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
//
// Special case for private refresh message
//
if (pThis && uMsg == pThis->m_msgRefresh) {
pThis->Refresh();
return FALSE;
}
switch (uMsg) {
case WM_CREATE:
pThis = (CMachine*)((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
break;
case WM_DEVICECHANGE:
if (DBT_DEVNODES_CHANGED == wParam) {
//
// While we are in WM_DEVICECHANGE context,
// no CM apis can be called because it would
// deadlock. Here, we schedule a timer so that
// we can handle the message later on.
//
SetTimer(hWnd, DM_NOTIFY_TIMERID, 1000, NULL);
}
break;
case WM_TIMER:
if (DM_NOTIFY_TIMERID == wParam) {
KillTimer(hWnd, DM_NOTIFY_TIMERID);
ASSERT(pThis);
pThis->ScheduleRefresh();
}
break;
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//
// This function attaches the given CFolder to the class.
// An attached CFolder will get notified when there are state
// changes in the class(Refresh, property changes, for example).
//
BOOL
CMachine::AttachFolder(
CFolder* pFolder
)
{
ASSERT(pFolder);
if (!IsFolderAttached(pFolder)) {
pFolder->MachinePropertyChanged(this);
m_listFolders.AddTail(pFolder);
}
return TRUE;
}
BOOL
CMachine::IsFolderAttached(
CFolder* pFolder
)
{
if (!m_listFolders.IsEmpty()) {
POSITION pos = m_listFolders.GetHeadPosition();
while (NULL != pos) {
if (pFolder == m_listFolders.GetNext(pos)) {
return TRUE;
}
}
}
return FALSE;
}
void
CMachine::DetachFolder(
CFolder* pFolder
)
{
POSITION nextpos = m_listFolders.GetHeadPosition();
while (nextpos) {
POSITION pos = nextpos;
CFolder* pFolderToTest = m_listFolders.GetNext(nextpos);
if (pFolderToTest == pFolder) {
m_listFolders.RemoveAt(pos);
break;
}
}
}
BOOL
CMachine::AttachPropertySheet(
HWND hwndPropertySheet
)
{
ASSERT(hwndPropertySheet);
EnterCriticalSection(&m_PropertySheetCriticalSection);
m_listPropertySheets.AddTail(hwndPropertySheet);
LeaveCriticalSection(&m_PropertySheetCriticalSection);
return TRUE;
}
void
CMachine::DetachPropertySheet(
HWND hwndPropertySheet
)
{
EnterCriticalSection(&m_PropertySheetCriticalSection);
POSITION nextpos = m_listPropertySheets.GetHeadPosition();
while (nextpos) {
POSITION pos = nextpos;
HWND hwndPropertySheetToTest = m_listPropertySheets.GetNext(nextpos);
if (hwndPropertySheetToTest == hwndPropertySheet) {
m_listPropertySheets.RemoveAt(pos);
break;
}
}
LeaveCriticalSection(&m_PropertySheetCriticalSection);
}
HWND
CMachine::GetDeviceWindowHandle(
LPCTSTR DeviceId
)
{
HWND hwnd = NULL;
EnterCriticalSection(&m_ChildMachineCriticalSection);
//
// Enumerate the list of Child CMachines
//
if (!m_listChildMachines.IsEmpty()) {
PVOID DeviceContext;
CDevice* pDevice;
POSITION nextpos = m_listChildMachines.GetHeadPosition();
while (nextpos) {
CMachine* pChildMachine = m_listChildMachines.GetNext(nextpos);
//
// Child machines will only have one device (if any at all)
//
if (pChildMachine->GetFirstDevice(&pDevice, DeviceContext) &&
pDevice) {
//
// If the device Ids match then get the window handle
//
if (lstrcmpi(pDevice->GetDeviceID(), DeviceId) == 0) {
hwnd = pDevice->m_psd.GetWindowHandle();
break;
}
}
}
}
LeaveCriticalSection(&m_ChildMachineCriticalSection);
return hwnd;
}
HWND
CMachine::GetClassWindowHandle(
LPGUID ClassGuid
)
{
HWND hwnd = NULL;
EnterCriticalSection(&m_ChildMachineCriticalSection);
//
// Enumerate the list of Child CMachines
//
if (!m_listChildMachines.IsEmpty()) {
PVOID ClassContext;
CClass* pClass;
POSITION nextpos = m_listChildMachines.GetHeadPosition();
while (nextpos) {
CMachine* pChildMachine = m_listChildMachines.GetNext(nextpos);
//
// Any child CMachine that has a device is a device property sheet.
// We only want to look at the ones that have 0 devices since those
// will be class property sheets.
//
if (pChildMachine->GetNumberOfDevices() == 0) {
if (pChildMachine->GetFirstClass(&pClass, ClassContext) &&
pClass) {
//
// If the ClassGuids match then get the window handle
//
if (IsEqualGUID(*ClassGuid, *pClass)) {
hwnd = pClass->m_psd.GetWindowHandle();
break;
}
}
}
}
}
LeaveCriticalSection(&m_ChildMachineCriticalSection);
return hwnd;
}
BOOL
CMachine::AttachChildMachine(
CMachine* ChildMachine
)
{
ASSERT(ChildMachine);
EnterCriticalSection(&m_ChildMachineCriticalSection);
m_listChildMachines.AddTail(ChildMachine);
LeaveCriticalSection(&m_ChildMachineCriticalSection);
return TRUE;
}
void
CMachine::DetachChildMachine(
CMachine* ChildMachine
)
{
EnterCriticalSection(&m_ChildMachineCriticalSection);
POSITION nextpos = m_listChildMachines.GetHeadPosition();
while (nextpos) {
POSITION pos = nextpos;
CMachine* CMachineToTest = m_listChildMachines.GetNext(nextpos);
if (CMachineToTest == ChildMachine) {
m_listChildMachines.RemoveAt(pos);
break;
}
}
LeaveCriticalSection(&m_ChildMachineCriticalSection);
}
CMachine::~CMachine()
{
//
// Turn off refresh. We need to do this in case there are any property
// sheets that are still active.
//
EnableRefresh(FALSE);
//
// We first need to destroy all child CMachines
//
while (!m_listChildMachines.IsEmpty()) {
//
// Remove the first child machine from the list. We need to
// do this in the critical section.
//
EnterCriticalSection(&m_ChildMachineCriticalSection);
CMachine* ChildMachine = m_listChildMachines.RemoveHead();
//
// Set the m_ParentMachine to NULL so we won't try to remove it from
// the list again.
//
ChildMachine->m_ParentMachine = NULL;
LeaveCriticalSection(&m_ChildMachineCriticalSection);
delete ChildMachine;
}
//
// We need to wait for all of the property sheets to be destroyed.
//
// We will check to see if there are any property pages still around, and
// if there is we will wait for .5 seconds and then check again. After 5
// seconds we will give up and just destroy the CMachine anyway.
//
int iSecondsCount = 0;
while (!m_listPropertySheets.IsEmpty() &&
(iSecondsCount++ < 10)) {
//
// Enumerate through all of the property sheets left and if IsWindow fails
// then pull them from the list, otherwise call DestroyWindow on them.
//
// Since property sheets each run in their own thread we need to do this
// in a critical section.
//
EnterCriticalSection(&m_PropertySheetCriticalSection);
POSITION nextpos = m_listPropertySheets.GetHeadPosition();
while (nextpos) {
POSITION pos = nextpos;
HWND hwndPropertySheetToTest = m_listPropertySheets.GetNext(nextpos);
if (IsWindow(hwndPropertySheetToTest)) {
//
// There is still a valid window for this property sheet so
// call DestroyWindow on it.
//
::DestroyWindow(hwndPropertySheetToTest);
} else {
//
// There is no window for this property sheet so just remove
// it from the list
//
m_listPropertySheets.RemoveAt(pos);
}
}
LeaveCriticalSection(&m_PropertySheetCriticalSection);
//
// Sleep for .5 seconds and then try again. This will give the property pages time to
// finish up their work.
//
Sleep(500);
}
//
// If we have created a device change data window for this machine,
// destroy it.
//
DestroyNotifyWindow();
DestroyClassesAndDevices();
if (!m_listFolders.IsEmpty()) {
m_listFolders.RemoveAll();
}
//
// If we have a parent CMachine then we need to remove oursleves from
// his list of Child CMachines.
//
if (m_ParentMachine) {
m_ParentMachine->DetachChildMachine(this);
}
DeleteCriticalSection(&m_CriticalSection);
DeleteCriticalSection(&m_PropertySheetCriticalSection);
DeleteCriticalSection(&m_ChildMachineCriticalSection);
}
//
// This function destroys all the CClass and CDevice we ever created.
// No notification is sent for the attached folder
//
//
void
CMachine::DestroyClassesAndDevices()
{
if (m_pComputer) {
delete m_pComputer;
m_pComputer = NULL;
}
if (!m_listDevice.IsEmpty()) {
POSITION pos = m_listDevice.GetHeadPosition();
while (NULL != pos) {
CDevice* pDevice = m_listDevice.GetNext(pos);
delete pDevice;
}
m_listDevice.RemoveAll();
}
if (!m_listClass.IsEmpty()) {
POSITION pos = m_listClass.GetHeadPosition();
while (NULL != pos) {
CClass* pClass = m_listClass.GetNext(pos);
delete pClass;
}
m_listClass.RemoveAll();
}
if (m_ImageListData.cbSize) {
DiDestroyClassImageList(&m_ImageListData);
}
CDevInfoList::DiDestroyDeviceInfoList();
m_hMachine = NULL;
}
BOOL
CMachine::BuildClassesFromGuidList(
LPGUID GuidList,
DWORD Guids
)
{
DWORD Index;
CClass* pClass;
//
// Build a list of CClass for each GUID.
//
for (Index = 0; Index < Guids; Index++) {
SafePtr<CClass> ClassPtr;
pClass = new CClass(this, &GuidList[Index]);
ClassPtr.Attach(pClass);
m_listClass.AddTail(ClassPtr);
ClassPtr.Detach();
}
return TRUE;
}
//
// Create CClass and CDevice for this machine.
// If DeviceId is valid, this function will create the machine
// with ONLY ONE device(and its CClass)
//
// PERFBUG Optimize this function!!!!!!!
// This function is slow because SetupDiGetClassDevs can take a long
// time (over 1 sec of a 300Mhz machine).
// The other slow part is the call to DoNotCreateDevice which needs to
// go through the service manager for all legacy devnodes to see
// if they are Win32 services (which we don't display). This takes
// around 10ms to get this information from the service manager on
// a 300Mhz machine and their are almost 100 of these legacy devices.
// This means another second of time.
//
//
BOOL
CMachine::CreateClassesAndDevices(
IN LPCTSTR DeviceId, OPTIONAL
IN LPGUID ClassGuid OPTIONAL
)
{
SC_HANDLE SCMHandle = NULL;
//
// Preventing memory leak
//
ASSERT(NULL == m_pComputer);
ASSERT(INVALID_HANDLE_VALUE == m_hDevInfo);
ASSERT(NULL == m_hMachine);
if (DeviceId || ClassGuid) {
//
// If the object is being created for a single device,
// create a empty device info list. We will add the
// device to the info list later.
//
m_hDevInfo = DiCreateDeviceInfoList(ClassGuid, m_hwndParent);
} else {
//
// We have to pull out the entire devices/classes set
// so create a device info list that contains all of them.
//
m_hDevInfo = DiGetClassDevs(NULL, NULL, m_hwndParent, DIGCF_ALLCLASSES | DIGCF_PROFILE);
}
//
// NULL != INVALID_HANDLE_VALUE. We checked both just be safe.
//
if (INVALID_HANDLE_VALUE == m_hDevInfo || NULL == m_hDevInfo) {
return FALSE;
}
SP_DEVINFO_LIST_DETAIL_DATA DevInfoDetailData;
DevInfoDetailData.cbSize = sizeof(DevInfoDetailData);
//
// Use the HMACHINE returned from Setupapi so that
// every call we make to Cfgmgr32.dll will use the
// same HMACHINE. Two call of CM_Connect_Machine will
// return different hMachines even though they refer to
// the same machine name(and thus, different set of DEVNODES!).
// The catch is that we will be able to call Setuapi and cfgmgr32
// API without worrying about which hMachine to use.
//
if (DiGetDeviceInfoListDetail(&DevInfoDetailData)) {
m_hMachine = DevInfoDetailData.RemoteMachineHandle;
} else {
//
// Unable to get the devinfo detail information.
// In this case we will just default to the local machine.
//
return FALSE;
}
//
// Get class image list data;
//
m_ImageListData.cbSize = sizeof(m_ImageListData);
if (DiGetClassImageList(&m_ImageListData)) {
//
// Add extra icons
//
HICON hIcon;
if ((hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DEVMGR))) != NULL) {
m_ComputerIndex = ImageList_AddIcon(m_ImageListData.ImageList, hIcon);
DestroyIcon(hIcon);
}
if ((hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_RESOURCES))) != NULL) {
m_ResourceIndex = ImageList_AddIcon(m_ImageListData.ImageList, hIcon);
DestroyIcon(hIcon);
}
}
//
// If the object is created for a particular device,
// do not create the entire device list because it is
// a waste of time.
//
if (DeviceId) {
SP_DEVINFO_DATA DevData;
GUID DeviceClassGuid;
DevData.cbSize = sizeof(DevData);
if (DiOpenDeviceInfo(DeviceId, m_hwndParent, 0, &DevData) &&
CmGetClassGuid(DevData.DevInst, DeviceClassGuid)) {
//
// Create a CClass for the device(without CClass, no
// device can not be created).
//
CClass* pClass;
SafePtr<CClass> ClassPtr;
pClass = new CClass(this, &DeviceClassGuid);
if (pClass) {
ClassPtr.Attach(pClass);
m_listClass.AddTail(ClassPtr);
//
// The class object has been inserted to the list
// it is safe now to detach the object from the smart pointer
// The class object will be deleted by the list
//
ClassPtr.Detach();
//
// Create the device
//
SafePtr<CDevice> DevicePtr;
CDevice* pDevice;
pDevice = new CDevice(this, pClass, &DevData);
if (pDevice) {
//
// Guard the object
//
DevicePtr.Attach(pDevice);
m_listDevice.AddTail(DevicePtr);
//
// Object added..
//
DevicePtr.Detach();
pClass->AddDevice(pDevice);
}
}
}
//
// We should have one class and one device object. no more no less.
// we are done here.
//
return(1 == m_listClass.GetCount() && 1 == m_listDevice.GetCount());
}
//
// If the object is for a specific class then just create the class and add
// it to the CMachine.
//
else if (ClassGuid) {
CClass* pClass;
SafePtr<CClass> ClassPtr;
pClass = new CClass(this, ClassGuid);
if (pClass) {
ClassPtr.Attach(pClass);
m_listClass.AddTail(ClassPtr);
//
// The class object has been inserted to the list
// it is safe now to detach the object from the smart pointer
// The class object will be deleted by the list
//
ClassPtr.Detach();
}
//
// We should have one class. no more no less.
// we are done here.
//
return(1 == m_listClass.GetCount());
}
//
// Build class guid list
//
DWORD ClassGuids, GuidsRequired;
GuidsRequired = 0;
//
// We make a guess here to save us some time.
//
GUID LocalGuid[GUID_LIST_INIT_SIZE];
ClassGuids = GUID_LIST_INIT_SIZE;
if (DiBuildClassInfoList(0, LocalGuid, ClassGuids, &GuidsRequired)) {
BuildClassesFromGuidList(LocalGuid, GuidsRequired);
} else if (ERROR_INSUFFICIENT_BUFFER == GetLastError() && GuidsRequired) {
//
// The stack based buffer is too small, allocate buffer from
// the heap.
//
BufferPtr<GUID> ClassGuidList(GuidsRequired);
if (DiBuildClassInfoList(0, ClassGuidList, GuidsRequired, &ClassGuids)) {
BuildClassesFromGuidList(ClassGuidList, ClassGuids);
}
}
//
// If we have any classes at all, create devices objects
//
if (!m_listClass.IsEmpty()) {
DWORD Index = 0;
SP_DEVINFO_DATA DevData;
//
// We need a handle to the service manager in the DoNotCreateDevice
// function. We will open it once and pass it to this function rather
// than opening and closing it for every device.
//
SCMHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
//
// Create every device in the devinfo list and
// associate each device to its class.
//
DevData.cbSize = sizeof(DevData);
while (DiEnumDeviceInfo(Index, &DevData)) {
POSITION pos = m_listClass.GetHeadPosition();
CClass* pClass;
//
// Find the class for this device
//
while (NULL != pos) {
pClass = m_listClass.GetNext(pos);
//
// Match the ClassGuid for this device.
// Note that if the device does not have a class guid (GUID_NULL)
// then we will put it in class GUID_DEVCLASS_UNKNOWN)
//
if ((IsEqualGUID(DevData.ClassGuid, *pClass)) ||
(IsEqualGUID(GUID_DEVCLASS_UNKNOWN, *pClass) &&
IsEqualGUID(DevData.ClassGuid, GUID_NULL))) {
//
// Is this one of the special DevInst that we should
// not create a CDevice for?
//
if (DoNotCreateDevice(SCMHandle, *pClass, DevData.DevInst)) {
break;
}
//
// Create the device
//
SafePtr<CDevice> DevicePtr;
CDevice* pDevice;
pDevice = new CDevice(this, pClass, &DevData);
//
// Guard the object
//
DevicePtr.Attach(pDevice);
m_listDevice.AddTail(DevicePtr);
//
// Object added.
//
DevicePtr.Detach();
//
// Put the device under the class
//
pClass->AddDevice(pDevice);
break;
}
//
// No class than, no device
//
}
//
// next device
//
Index++;
}
CloseServiceHandle(SCMHandle);
//
// Create a device tree under computer
// the tree order comes from DEVNODE structure;
//
DEVNODE dnRoot = CmGetRootDevNode();
m_pComputer = new CComputer(this, dnRoot);
DEVNODE dnStart = CmGetChild(dnRoot);
CreateDeviceTree(m_pComputer, NULL, dnStart);
}
return TRUE;
}
//
// This function builds a device tree based on the Devnode tree retreived
// from configuration manager. Note that ALL CDevice are created before
// this function is called. This function establishs each CDevice relationship.
//
void
CMachine::CreateDeviceTree(
CDevice* pParent,
CDevice* pSibling,
DEVNODE dn
)
{
CDevice* pDevice;
DEVNODE dnChild;
while (dn) {
pDevice = DevNodeToDevice(dn);
if (pDevice) {
//
// No sibling ->this is the first child
//
if (!pSibling) {
pParent->SetChild(pDevice);
}
else {
pSibling->SetSibling(pDevice);
}
pDevice->SetParent(pParent);
pSibling = pDevice;
dnChild = CmGetChild(dn);
if (dnChild) {
CreateDeviceTree(pDevice, NULL, dnChild);
}
}
dn = CmGetSibling(dn);
}
}
//
// Find CDevice from the given devnode
//
CDevice*
CMachine::DevNodeToDevice(
DEVNODE dn
)
{
POSITION pos = m_listDevice.GetHeadPosition();
while (NULL != pos) {
CDevice* pDevice = m_listDevice.GetNext(pos);
if (pDevice->GetDevNode() == dn) {
return pDevice;
}
}
return NULL;
}
//
// This function reenumerates the devnode tree from the root, rebuilds the
// device tree and notifies every attached folder about the new device tree.
//
BOOL
CMachine::Reenumerate()
{
if (m_pComputer) {
//
// Temporarily disable refresh while we are doing reenumeration
// so that we will not keep refreshing the device tree.
//
EnableRefresh(FALSE);
CDialog WaitDialog(IDD_SCAN_PNP_HARDWARES);
WaitDialog.DoModaless(m_hwndParent, (LPARAM)&WaitDialog);
if (!CmReenumerate(
m_pComputer->GetDevNode(),
CM_REENUMERATE_SYNCHRONOUS | CM_REENUMERATE_RETRY_INSTALLATION
)) {
//
// Win2K doesn't support CM_REENUMERATE_RETRY_INSTALLATION, so we
// retry without the reinstall flag.
//
CmReenumerate(m_pComputer->GetDevNode(), CM_REENUMERATE_SYNCHRONOUS);
}
DestroyWindow(WaitDialog);
//
// reenumeration is done, schedule a refresh and enable refresh now.
//
ScheduleRefresh();
EnableRefresh(TRUE);
}
return TRUE;
}
//
// This function enable/disable refresh. A disble counter is kept to
// support multiple disabling/enabling. Only when the disable counter
// is zero, a refresh is possible.
// If a refresh is pending while we are enabling, we schedule a new
// request so that we will not lose any requests.
//
BOOL
CMachine::EnableRefresh(
BOOL fEnable
)
{
BOOL Result = TRUE;
Lock();
if (fEnable) {
if (m_RefreshDisableCounter < 0) {
m_RefreshDisableCounter++;
}
}
else {
m_RefreshDisableCounter--;
}
//
// If we are enabling refresh and there is one request pending,
// schedule it again. This makes sure that we will not lose
// any requests.
// We schedule a new refresh request instead of calling Refresh
// directly because we can be called by different threads while
// we want the refresh to be done in the main thread. The data window
// we created will receive the message and execute the refresh
// in the main thread context.
//
if (fEnable && m_RefreshPending) {
m_RefreshPending = FALSE;
ScheduleRefresh();
}
Unlock();
return Result;
}
//
//
// This function rebuilds the entire list of CClass and CDevice
// All attached CFolder are notified about the new machine.
//
// There are several occasions that we need to recreate the device tree:
// (1). On WM_DEVICECHANGE.
// (2). Device properties changed.
// (3). Device removal.
// (4). Device drivers updated and
// (5). Reenumeration requested by users.
// The requests may come from different threads and we must serialize
// the requests. A critical section and a new function(EnableRefresh) are added for
// for this purpose.
// Before doing anything on a device or class, one must call EnableRefesh(FALSE)
// to suspend Device change notification. When it is done with the change,
// one must call ScheduleRefesh function(if a refresh is necessary because of
// the changes) and then EnableRefresh(TRUE) to reenable the refresh.
//
BOOL
CMachine::Refresh()
{
BOOL Result = TRUE;
POSITION pos;
Lock();
if (0 == m_RefreshDisableCounter) {
HCURSOR hCursorOld;
hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
//
// Before we destroy all the classes and devices,
// notify every attached folder so that they can dis-engaged
// from us. After we created a new set of classes and devices,
// the notification will be sent again to each folder
// so that each folder can enagage to the machine again.
//
pos = m_listFolders.GetHeadPosition();
while (NULL != pos) {
((CFolder*)m_listFolders.GetNext(pos))->MachinePropertyChanged(NULL);
}
//
// we can destroy all the "old" classes and devices now.
//
DestroyClassesAndDevices();
if (CreateClassesAndDevices()) {
//
// Notify every attach folder to recreate
//
if (!m_listFolders.IsEmpty()) {
pos = m_listFolders.GetHeadPosition();
while (NULL != pos) {
CFolder* pFolder = m_listFolders.GetNext(pos);
Result = SUCCEEDED(pFolder->MachinePropertyChanged(this));
}
}
//
// Notify every property page to refresh
//
if (!m_listChildMachines.IsEmpty()) {
EnterCriticalSection(&m_ChildMachineCriticalSection);
POSITION nextpos = m_listChildMachines.GetHeadPosition();
while (nextpos) {
pos = nextpos;
CMachine* pMachineToNotify = m_listChildMachines.GetNext(nextpos);
pMachineToNotify->DoMiniRefresh();
}
LeaveCriticalSection(&m_ChildMachineCriticalSection);
}
}
if (hCursorOld) {
SetCursor(hCursorOld);
}
m_RefreshPending = FALSE;
}
else {
//
// We need to refresh while the refresh is disabled
// Remember this so that when refresh is enabled, we
// can launch a refresh.
//
m_RefreshPending = TRUE;
}
Unlock();
return Result;
}
//
// A mini refresh is performed when the CMachine only has a single CClass or
// CDevice. This is because this will be the property sheet case. In this
// case we will see if the class or device represented by this property sheet
// still exists.
//
BOOL
CMachine::DoMiniRefresh()
{
BOOL Result = TRUE;
Lock();
//
// If the machine has one CDevice then we need to see if this device is
// still around.
//
if (1 == m_listDevice.GetCount()) {
PVOID Context;
CDevice* pDevice;
if (GetFirstDevice(&pDevice, Context)) {
//
// If the devnode did not go away we should tell it to refresh itself
// in case something changed.
//
::PostMessage(pDevice->m_psd.GetWindowHandle(), PSM_QUERYSIBLINGS, QSC_PROPERTY_CHANGED, 0L);
}
}
//
// Note that currently we don't do anything in the class property sheet case.
// The reason for this is that classes can't really go away unless someone deletes
// them from the registry, which probably wills mess up lots of other things. Also
// all current class property sheets don't do anything anyway and so even if someone
// does delete the class key nothing bad will happen to the property sheet.
//
Unlock();
return Result;
}
BOOL
CMachine::GetFirstDevice(
CDevice** ppDevice,
PVOID& Context
)
{
ASSERT(ppDevice);
if (!m_listDevice.IsEmpty()) {
POSITION pos = m_listDevice.GetHeadPosition();
*ppDevice = m_listDevice.GetNext(pos);
Context = pos;
return TRUE;
}
*ppDevice = NULL;
Context = NULL;
return FALSE;
}
BOOL
CMachine::GetNextDevice(
CDevice** ppDevice,
PVOID& Context
)
{
ASSERT(ppDevice);
POSITION pos = (POSITION)Context;
if (NULL != pos) {
*ppDevice = m_listDevice.GetNext(pos);
Context = pos;
return TRUE;
}
*ppDevice = NULL;
return FALSE;
}
BOOL
CMachine::GetFirstClass(
CClass** ppClass,
PVOID& Context
)
{
ASSERT(ppClass);
if (!m_listClass.IsEmpty()) {
POSITION pos = m_listClass.GetHeadPosition();
*ppClass = m_listClass.GetNext(pos);
Context = pos;
return TRUE;
}
*ppClass = NULL;
Context = NULL;
return FALSE;
}
BOOL
CMachine::GetNextClass(
CClass** ppClass,
PVOID& Context
)
{
ASSERT(ppClass);
POSITION pos = (POSITION)Context;
if (NULL != pos) {
*ppClass = m_listClass.GetNext(pos);
Context = pos;
return TRUE;
}
*ppClass = NULL;
return FALSE;
}
BOOL
CMachine::GetInfDigitalSigner(
LPCTSTR FullInfPath,
String& DigitalSigner
)
/*++
This function returns whether the specified INF file is digitally signed
or not and returns the digital signer.
NOTE that this function can return TRUE, meaning the file is digitally
signed, but not fill in the DigitalSigner parameter if the catalog
file associated with this INF didn't specify one.
--*/
{
SP_INF_SIGNER_INFO InfSignerInfo;
BOOL bRet = FALSE;
if (m_UserIsAGuest || !IsLocal()) {
//
// If the user is loged in as a guest, or we are not on the local
// machine, then make the digital signer string be 'Not available'
//
DigitalSigner.LoadString(g_hInstance, IDS_NOT_AVAILABLE);
} else {
InfSignerInfo.cbSize = sizeof(InfSignerInfo);
bRet = SetupVerifyInfFile(FullInfPath,
NULL,
&InfSignerInfo
);
//
// If SetupVerifyInfFile sets one of the following errors then the
// INF file is Authenticode signed, and we will treat this as
// a success case.
//
if((GetLastError() == ERROR_AUTHENTICODE_TRUSTED_PUBLISHER) ||
(GetLastError() == ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED)) {
bRet = TRUE;
}
if (bRet && (InfSignerInfo.DigitalSigner[0] != TEXT('\0'))) {
DigitalSigner = (LPTSTR)InfSignerInfo.DigitalSigner;
}
}
return bRet;
}
BOOL
CMachine::DoNotCreateDevice(
SC_HANDLE SCMHandle,
LPGUID ClassGuid,
DEVINST DevInst
)
/*++
This function returns whether a CDevice should be created for this DevInst
or not. If a CDevice is not created for DevInst then it will never show up
in the device manager UI, even when "Show hidden devices" is turned on.
We don't create a CDevice in the following cases:
- DevInst is HTREE\ROOT\0
- DevInst is a Win32 Service.
--*/
{
SC_HANDLE ServiceHandle;
TCHAR ServiceName[MAX_PATH];
LPQUERY_SERVICE_CONFIG ServiceConfig = NULL;
DWORD ServiceConfigSize;
ULONG Size;
String strDeviceID;
BOOL Return = FALSE;
//
// If the DEVMGR_SHOW_NONPRESENT_DEVICES environment variable is not set then
// we do not want to show any Phantom devices.
//
if (!m_ShowNonPresentDevices) {
ULONG Status, Problem;
if ((!CmGetStatus(DevInst, &Status, &Problem)) &&
((m_LastCR == CR_NO_SUCH_VALUE) ||
(m_LastCR == CR_NO_SUCH_DEVINST))) {
return TRUE;
}
}
//
// Check to see if this device is a Win32 service. Only
// legacy devices could be Win32 services.
//
if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_LEGACYDRIVER)) {
Size = sizeof(ServiceName);
if (CmGetRegistryProperty(DevInst,
CM_DRP_SERVICE,
(PVOID)ServiceName,
&Size
) == CR_SUCCESS) {
//
// Open this particular service
//
if ((ServiceHandle = OpenService(SCMHandle, ServiceName, GENERIC_READ)) != NULL) {
//
// Get the service config
//
if ((!QueryServiceConfig(ServiceHandle, NULL, 0, &ServiceConfigSize)) &&
(ERROR_INSUFFICIENT_BUFFER == GetLastError())) {
if ((ServiceConfig = (LPQUERY_SERVICE_CONFIG)malloc(ServiceConfigSize)) != NULL) {
if (QueryServiceConfig(ServiceHandle, ServiceConfig, ServiceConfigSize, &ServiceConfigSize)) {
if (ServiceConfig->dwServiceType & (SERVICE_WIN32 | SERVICE_FILE_SYSTEM_DRIVER)) {
Return = TRUE;
}
}
free(ServiceConfig);
}
}
CloseServiceHandle(ServiceHandle);
}
}
}
//
// Check to see if this is the HTREE\ROOT\0 device. We don't
// want to create a CDevice for this phantom devnode.
//
// This is an else statement because the HTREE\ROOT\0 device is
// not in the legacy device class.
//
else {
CmGetDeviceIDString(DevInst, strDeviceID);
if (!_wcsicmp((LPTSTR)strDeviceID, TEXT("HTREE\\ROOT\\0"))) {
Return = TRUE;
}
}
return Return;
}
BOOL
CMachine::DiGetClassFriendlyNameString(
LPGUID Guid,
String& strClass
)
{
TCHAR DisplayName[LINE_LEN + 1];
//
// Try friendly name first. If it failed, try the class name
//
if (SetupDiGetClassDescriptionEx(Guid, DisplayName, ARRAYLEN(DisplayName),
NULL, GetRemoteMachineFullName(), NULL) ||
SetupDiClassNameFromGuidEx(Guid, DisplayName, ARRAYLEN(DisplayName),
NULL, GetRemoteMachineFullName(), NULL)) {
strClass = DisplayName;
return TRUE;
}
return FALSE;
}
DEVNODE
CMachine::CmGetParent(
DEVNODE dn
)
{
DEVNODE dnParent;
m_LastCR = CM_Get_Parent_Ex(&dnParent, dn, 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return dnParent;
}
return NULL;
}
DEVNODE
CMachine::CmGetChild(
DEVNODE dn
)
{
DEVNODE dnChild;
m_LastCR = CM_Get_Child_Ex(&dnChild, dn, 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return dnChild;
}
return NULL;
}
DEVNODE
CMachine::CmGetSibling(
DEVNODE dn
)
{
DEVNODE dnSibling;
m_LastCR = CM_Get_Sibling_Ex(&dnSibling, dn, 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return dnSibling;
}
return NULL;
}
DEVNODE
CMachine::CmGetRootDevNode()
{
DEVNODE dnRoot;
m_LastCR = CM_Locate_DevNode_Ex(&dnRoot, NULL, 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return dnRoot;
}
return NULL;
}
BOOL
CMachine::CmGetDeviceIDString(
DEVNODE dn,
String& str
)
{
TCHAR DeviceID[MAX_DEVICE_ID_LEN + 1];
m_LastCR = CM_Get_Device_ID_Ex(dn, DeviceID, ARRAYLEN(DeviceID), 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
str = DeviceID;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetConfigFlags(
DEVNODE dn,
DWORD* pFlags
)
{
DWORD Size;
Size = sizeof(DWORD);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_CONFIGFLAGS, pFlags, &Size);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmGetCapabilities(
DEVNODE dn,
DWORD* pCapabilities
)
{
DWORD Size;
Size = sizeof(DWORD);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_CAPABILITIES, pCapabilities, &Size);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmGetDescriptionString(
DEVNODE dn,
String& str
)
{
TCHAR Description[LINE_LEN + 1];
ULONG Size = sizeof(Description);
Description[0] = TEXT('\0');
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_FRIENDLYNAME, Description, &Size);
if ((CR_NO_SUCH_VALUE == m_LastCR) || (Description[0] == TEXT('\0'))) {
Size = sizeof(Description);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_DEVICEDESC, Description,
&Size);
}
if ((CR_SUCCESS == m_LastCR) && (Description[0] != TEXT('\0'))) {
str = Description;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetMFGString(
DEVNODE dn,
String& str
)
{
TCHAR MFG[LINE_LEN + 1];
ULONG Size = sizeof(MFG);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_MFG, MFG, &Size);
if (CR_SUCCESS == m_LastCR) {
str = MFG;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetProviderString(
DEVNODE dn,
String& str
)
{
TCHAR Provider[LINE_LEN + 1];
ULONG Size = sizeof(Provider);
m_LastCR = CmGetRegistrySoftwareProperty(dn, TEXT("ProviderName"),
Provider, &Size);
if (CR_SUCCESS == m_LastCR) {
str = Provider;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetDriverDateString(
DEVNODE dn,
String& str
)
{
TCHAR DriverDate[LINE_LEN + 1];
ULONG Size = sizeof(DriverDate);
m_LastCR = CmGetRegistrySoftwareProperty(dn, TEXT("DriverDate"),
DriverDate, &Size);
if (CR_SUCCESS == m_LastCR) {
str = DriverDate;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetDriverDateData(
DEVNODE dn,
FILETIME *ft
)
{
ULONG Size = sizeof(*ft);
m_LastCR = CmGetRegistrySoftwareProperty(dn, TEXT("DriverDateData"),
ft, &Size);
return(m_LastCR == CR_SUCCESS);
}
BOOL
CMachine::CmGetDriverVersionString(
DEVNODE dn,
String& str
)
{
TCHAR DriverVersion[LINE_LEN + 1];
ULONG Size = sizeof(DriverVersion);
m_LastCR = CmGetRegistrySoftwareProperty(dn, TEXT("DriverVersion"),
DriverVersion, &Size);
if (CR_SUCCESS == m_LastCR) {
str = DriverVersion;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetBusGuid(
DEVNODE dn,
LPGUID Guid
)
{
ULONG Size = sizeof(*Guid);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_BUSTYPEGUID, (LPVOID)Guid, &Size);
if (CR_SUCCESS == m_LastCR) {
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetBusGuidString(
DEVNODE dn,
String& str
)
{
GUID BusGuid;
TCHAR BusGuidString[MAX_GUID_STRING_LEN];
ULONG Size;
while (dn) {
//
// We have to set the size on each loop
//
Size = sizeof(BusGuid);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_BUSTYPEGUID, &BusGuid, &Size);
if (CR_SUCCESS == m_LastCR && GuidToString(&BusGuid, BusGuidString,
ARRAYLEN(BusGuidString))) {
str = BusGuidString;
return TRUE;
}
dn = CmGetParent(dn);
}
return FALSE;
}
BOOL
CMachine::CmGetClassGuid(
DEVNODE dn,
GUID& Guid
)
{
TCHAR szGuidString[MAX_GUID_STRING_LEN + 1];
ULONG Size = sizeof(szGuidString);
m_LastCR = CmGetRegistryProperty(dn, CM_DRP_CLASSGUID, szGuidString, &Size);
if (CR_SUCCESS == m_LastCR && GuidFromString(szGuidString, &Guid)) {
return TRUE;
}
//
// If we can't get the class GUID from the registry then most likely the device
// does not have a class GUID. If this is the case then we will return
// GUID_DEVCLASS_UNKNOWN
//
else {
memcpy(&Guid, &GUID_DEVCLASS_UNKNOWN, sizeof(GUID));
return TRUE;
}
}
BOOL
CMachine::CmGetStatus(
DEVNODE dn,
DWORD* pProblem,
DWORD* pStatus
)
{
ASSERT(pProblem && pStatus);
m_LastCR = CM_Get_DevNode_Status_Ex(pStatus, pProblem, dn, 0, m_hMachine);
return(CR_SUCCESS == m_LastCR);
}
BOOL
CMachine::CmGetKnownLogConf(
DEVNODE dn,
LOG_CONF* plc,
DWORD* plcType
)
{
ASSERT(plc);
*plc = 0;
if (plcType) {
*plcType = LOG_CONF_BITS + 1;
}
ULONG lcTypeFirst = ALLOC_LOG_CONF;
ULONG lcTypeLast = FORCED_LOG_CONF;
ASSERT(ALLOC_LOG_CONF + 1 == BOOT_LOG_CONF &&
BOOT_LOG_CONF + 1 == FORCED_LOG_CONF);
for (ULONG lcType = lcTypeFirst; lcType <= lcTypeLast; lcType++) {
m_LastCR = CM_Get_First_Log_Conf_Ex(plc, dn, lcType, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
if (plcType) {
*plcType = lcType;
}
break;
}
}
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmHasResources(
DEVNODE dn
)
{
for (ULONG lcType = 0; lcType < NUM_LOG_CONF; lcType++) {
m_LastCR = CM_Get_First_Log_Conf_Ex(NULL, dn, lcType, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
break;
}
}
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmReenumerate(
DEVNODE dn,
ULONG Flags
)
{
m_LastCR = CM_Reenumerate_DevNode_Ex(dn, Flags, m_hMachine);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmGetHwProfileFlags(
DEVNODE dn,
ULONG Profile,
ULONG* pFlags
)
{
TCHAR DeviceID[MAX_DEVICE_ID_LEN + 1];
m_LastCR = CM_Get_Device_ID_Ex(dn, DeviceID, ARRAYLEN(DeviceID),
0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return CmGetHwProfileFlags(DeviceID, Profile, pFlags);
}
return FALSE;
}
BOOL
CMachine::CmGetHwProfileFlags(
LPCTSTR DeviceID,
ULONG Profile,
ULONG* pFlags
)
{
m_LastCR = CM_Get_HW_Prof_Flags_Ex((LPTSTR)DeviceID, Profile, pFlags, 0,
m_hMachine);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmSetHwProfileFlags(
DEVNODE dn,
ULONG Profile,
ULONG Flags
)
{
TCHAR DeviceID[MAX_DEVICE_ID_LEN + 1];
m_LastCR = CM_Get_Device_ID_Ex(dn, DeviceID, ARRAYLEN(DeviceID),
0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return CmSetHwProfileFlags(DeviceID, Profile, Flags);
}
return FALSE;
}
BOOL
CMachine::CmSetHwProfileFlags(
LPCTSTR DeviceID,
ULONG Profile,
ULONG Flags
)
{
m_LastCR = CM_Set_HW_Prof_Flags_Ex((LPTSTR)DeviceID, Profile, Flags, 0,
m_hMachine);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmGetCurrentHwProfile(
ULONG* phwpf
)
{
HWPROFILEINFO hwpfInfo;
ASSERT(phwpf);
if (CmGetHwProfileInfo(0xFFFFFFFF, &hwpfInfo)) {
*phwpf = hwpfInfo.HWPI_ulHWProfile;
return TRUE;
}
return FALSE;
}
BOOL
CMachine::CmGetHwProfileInfo(
int Index,
PHWPROFILEINFO pHwProfileInfo
)
{
m_LastCR = CM_Get_Hardware_Profile_Info_Ex(Index, pHwProfileInfo, 0, m_hMachine);
return(CR_SUCCESS == m_LastCR);
}
ULONG
CMachine::CmGetResDesDataSize(
RES_DES rd
)
{
ULONG Size;
m_LastCR = CM_Get_Res_Des_Data_Size_Ex(&Size, rd, 0, m_hMachine);
if (CR_SUCCESS == m_LastCR) {
return Size;
}
return 0;
}
BOOL
CMachine::CmGetResDesData(
RES_DES rd,
PVOID Buffer,
ULONG BufferSize
)
{
m_LastCR = CM_Get_Res_Des_Data_Ex(rd, Buffer, BufferSize, 0, m_hMachine);
return CR_SUCCESS == m_LastCR;
}
BOOL
CMachine::CmGetNextResDes(
PRES_DES prdNext,
RES_DES rd,
RESOURCEID ForResource,
PRESOURCEID pTheResource
)
{
m_LastCR = CM_Get_Next_Res_Des_Ex(prdNext, rd, ForResource, pTheResource,
0, m_hMachine);
return(CR_SUCCESS == m_LastCR);
}
void
CMachine::CmFreeResDesHandle(
RES_DES rd
)
{
m_LastCR = CM_Free_Res_Des_Handle(rd);
}
void
CMachine::CmFreeResDes(
PRES_DES prdPrev,
RES_DES rd
)
{
m_LastCR = CM_Free_Res_Des_Ex(prdPrev, rd, 0, m_hMachine);
}
void
CMachine::CmFreeLogConfHandle(
LOG_CONF lc
)
{
m_LastCR = CM_Free_Log_Conf_Handle(lc);
}
BOOL
CMachine::CmGetFirstLogConf(
DEVNODE dn,
LOG_CONF* plc,
ULONG Type
)
{
m_LastCR = CM_Get_First_Log_Conf_Ex(plc, dn, Type, m_hMachine);
return CR_SUCCESS == m_LastCR;
}
CONFIGRET
CMachine::CmGetRegistryProperty(
DEVNODE dn,
ULONG Property,
PVOID pBuffer,
ULONG* pBufferSize
)
{
return CM_Get_DevNode_Registry_Property_Ex(dn, Property, NULL,
pBuffer, pBufferSize,
0, m_hMachine
);
}
CONFIGRET
CMachine::CmGetRegistrySoftwareProperty(
DEVNODE dn,
LPCTSTR ValueName,
PVOID pBuffer,
ULONG* pBufferSize
)
{
HKEY hKey;
DWORD Type = REG_SZ;
CONFIGRET CR;
if (CR_SUCCESS == (CR = CM_Open_DevNode_Key_Ex(dn, KEY_READ, 0, RegDisposition_OpenExisting,
&hKey, CM_REGISTRY_SOFTWARE, m_hMachine))) {
if (ERROR_SUCCESS != RegQueryValueEx(hKey, ValueName, NULL, &Type, (const PBYTE)pBuffer,
pBufferSize)) {
CR = CR_REGISTRY_ERROR;
}
RegCloseKey(hKey);
}
return CR;
}
CMachineList::~CMachineList()
{
if (!m_listMachines.IsEmpty()) {
POSITION pos = m_listMachines.GetHeadPosition();
CMachine* pMachine;
while (NULL != pos) {
pMachine = m_listMachines.GetNext(pos);
delete pMachine;
}
m_listMachines.RemoveAll();
}
}
//
// This function creates a machine object on the given machine name
// INPUT:
// MachineName -- the machine name. Must be in full qualified format
// NULL means the local machine
// ppMachine -- buffer to receive the newly create machine.
//
// OUTPUT:
// TRUE if the machine is created successfully. ppMachine
// is filled with the newly created Machine.
// FALSE if the function failed.
// NOTE:
// The caller should NOT free any machine object retruned
// from this function.
//
BOOL
CMachineList::CreateMachine(
LPCTSTR MachineName,
CMachine** ppMachine
)
{
ASSERT(ppMachine);
*ppMachine = NULL;
CMachine* pMachine = NULL;
if (!MachineName || _T('\0') == MachineName[0]) {
//
// Local machine.
//
String strMachineName;
strMachineName.GetComputerName();
pMachine = FindMachine(strMachineName);
}
else {
pMachine = FindMachine(MachineName);
}
if (NULL == pMachine) {
pMachine = new CMachine(MachineName);
m_listMachines.AddTail(pMachine);
}
*ppMachine = pMachine;
return NULL != pMachine;
}
CMachine*
CMachineList::FindMachine(
LPCTSTR MachineName
)
{
if (!m_listMachines.IsEmpty()) {
POSITION pos = m_listMachines.GetHeadPosition();
while (NULL != pos) {
CMachine* pMachine;
pMachine = m_listMachines.GetNext(pos);
if (!lstrcmpi(MachineName, pMachine->GetMachineFullName())) {
return pMachine;
}
}
}
return NULL;
}