1882 lines
42 KiB
C++
1882 lines
42 KiB
C++
/*++
|
|
|
|
Copyright (C) 1997-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cnode.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements CDevice, CClass, CResource and CComputer classes.
|
|
|
|
Author:
|
|
|
|
William Hsieh (williamh) created
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
#include "devmgr.h"
|
|
#include "cdriver.h"
|
|
#include "hwprof.h"
|
|
#include "sysinfo.h"
|
|
#include <initguid.h>
|
|
#include <mountmgr.h>
|
|
#include <devguid.h>
|
|
#include <wdmguid.h>
|
|
|
|
|
|
//
|
|
// CClass implementation
|
|
//
|
|
CClass::CClass(
|
|
CMachine* pMachine,
|
|
GUID* pGuid
|
|
)
|
|
{
|
|
m_Guid = *pGuid;
|
|
ASSERT(pMachine);
|
|
ASSERT(pGuid);
|
|
|
|
HKEY hKey = NULL;
|
|
|
|
m_NoDisplay = FALSE;
|
|
m_pMachine = pMachine;
|
|
m_TotalDevices = 0;
|
|
m_TotalHiddenDevices = 0;
|
|
m_pDevInfoList = NULL;
|
|
m_pos = NULL;
|
|
|
|
if (!m_pMachine->DiGetClassFriendlyNameString(pGuid, m_strDisplayName))
|
|
{
|
|
m_strDisplayName.LoadString(g_hInstance, IDS_UNKNOWN);
|
|
}
|
|
|
|
m_pMachine->DiGetClassImageIndex(pGuid, &m_iImage);
|
|
|
|
hKey = m_pMachine->DiOpenClassRegKey(pGuid, KEY_READ, DIOCR_INSTALLER);
|
|
|
|
if (INVALID_HANDLE_VALUE != hKey)
|
|
{
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_NODISPLAYCLASS, NULL, NULL, NULL, NULL))
|
|
{
|
|
m_NoDisplay = TRUE;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
CDevInfoList*
|
|
CClass::GetDevInfoList(
|
|
HWND hwndParent
|
|
)
|
|
{
|
|
if (!m_pDevInfoList)
|
|
{
|
|
HDEVINFO hDevInfo = m_pMachine->DiCreateDeviceInfoList(&m_Guid, hwndParent);
|
|
|
|
if (hDevInfo && INVALID_HANDLE_VALUE != hDevInfo)
|
|
{
|
|
m_pDevInfoList = new CDevInfoList(hDevInfo, hwndParent);
|
|
}
|
|
}
|
|
|
|
return m_pDevInfoList;
|
|
}
|
|
|
|
inline
|
|
CItemIdentifier*
|
|
CClass::CreateIdentifier()
|
|
{
|
|
return new CClassIdentifier(*this);
|
|
}
|
|
|
|
CClass::~CClass()
|
|
{
|
|
m_listDevice.RemoveAll();
|
|
|
|
if (m_pDevInfoList)
|
|
{
|
|
delete m_pDevInfoList;
|
|
}
|
|
}
|
|
|
|
HICON
|
|
CClass::LoadIcon()
|
|
{
|
|
HICON hClassIcon;
|
|
|
|
if (!m_pMachine->DiLoadClassIcon(&m_Guid, &hClassIcon, NULL))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return hClassIcon;
|
|
}
|
|
|
|
void
|
|
CClass::AddDevice(CDevice* pDevice)
|
|
{
|
|
ASSERT(pDevice);
|
|
|
|
m_listDevice.AddTail(pDevice);
|
|
m_TotalDevices++;
|
|
|
|
// Every device under a NoDisplay class is a hidden device
|
|
if (m_NoDisplay || pDevice->IsHidden()) {
|
|
|
|
m_TotalHiddenDevices++;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CClass::GetFirstDevice(
|
|
CDevice** ppDevice,
|
|
PVOID& Context
|
|
)
|
|
{
|
|
ASSERT(ppDevice);
|
|
|
|
if (!m_listDevice.IsEmpty())
|
|
{
|
|
POSITION pos;
|
|
|
|
pos = m_listDevice.GetHeadPosition();
|
|
*ppDevice = m_listDevice.GetNext(pos);
|
|
Context = pos;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
*ppDevice = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CClass::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;
|
|
}
|
|
|
|
void
|
|
CClass::PropertyChanged()
|
|
{
|
|
HKEY hKey = NULL;
|
|
|
|
if (!m_pMachine->DiGetClassFriendlyNameString(&m_Guid, m_strDisplayName))
|
|
{
|
|
m_strDisplayName.LoadString(g_hInstance, IDS_UNKNOWN);
|
|
}
|
|
|
|
m_pMachine->DiGetClassImageIndex(&m_Guid, &m_iImage);
|
|
|
|
if (m_pDevInfoList)
|
|
{
|
|
delete m_pDevInfoList;
|
|
m_pDevInfoList = NULL;
|
|
}
|
|
|
|
hKey = m_pMachine->DiOpenClassRegKey(&m_Guid, KEY_READ, DIOCR_INSTALLER);
|
|
|
|
if (INVALID_HANDLE_VALUE != hKey)
|
|
{
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_NODISPLAYCLASS, NULL, NULL, NULL, NULL))
|
|
{
|
|
m_NoDisplay = TRUE;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
// CDevice implementation
|
|
//
|
|
CDevice::CDevice(
|
|
CMachine* pMachine,
|
|
CClass* pClass,
|
|
PSP_DEVINFO_DATA pDevData
|
|
)
|
|
{
|
|
ASSERT(pMachine && pDevData && pClass);
|
|
|
|
m_DevData = *pDevData;
|
|
m_pClass = pClass;
|
|
m_pMachine = pMachine;
|
|
m_pSibling = NULL;
|
|
m_pParent = NULL;
|
|
m_pChild = NULL;
|
|
|
|
if (!m_pMachine->CmGetDescriptionString(m_DevData.DevInst, m_strDisplayName))
|
|
{
|
|
m_strDisplayName.LoadString(g_hInstance, IDS_UNKNOWN_DEVICE);
|
|
}
|
|
|
|
m_pMachine->CmGetDeviceIDString(m_DevData.DevInst, m_strDeviceID);
|
|
m_iImage = m_pClass->GetImageIndex();
|
|
}
|
|
|
|
inline
|
|
CItemIdentifier*
|
|
CDevice::CreateIdentifier()
|
|
{
|
|
return new CDeviceIdentifier(*this);
|
|
}
|
|
|
|
void
|
|
CDevice::PropertyChanged()
|
|
{
|
|
if (!m_pMachine->CmGetDescriptionString(m_DevData.DevInst, m_strDisplayName))
|
|
{
|
|
m_strDisplayName.LoadString(g_hInstance, IDS_UNKNOWN_DEVICE);
|
|
}
|
|
|
|
m_pMachine->CmGetDeviceIDString(m_DevData.DevInst, m_strDeviceID);
|
|
m_iImage = m_pClass->GetImageIndex();
|
|
}
|
|
|
|
HICON
|
|
CDevice::LoadClassIcon()
|
|
{
|
|
HICON hClassIcon;
|
|
hClassIcon = NULL;
|
|
|
|
if (m_pMachine->DiLoadClassIcon(&m_DevData.ClassGuid, &hClassIcon, NULL))
|
|
{
|
|
return hClassIcon;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetStatus(
|
|
DWORD* pStatus,
|
|
DWORD* pProblem
|
|
)
|
|
{
|
|
return m_pMachine->CmGetStatus(m_DevData.DevInst, pProblem, pStatus);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetCapabilities(
|
|
DWORD* pCapabilities
|
|
)
|
|
{
|
|
return m_pMachine->CmGetCapabilities(m_DevData.DevInst, pCapabilities);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetPowerCapabilities(
|
|
DWORD* pCapabilities
|
|
)
|
|
{
|
|
CM_POWER_DATA CmPowerData;
|
|
ULONG Size;
|
|
|
|
Size = sizeof(CmPowerData);
|
|
if (m_pMachine->CmGetRegistryProperty(m_DevData.DevInst,
|
|
CM_DRP_DEVICE_POWER_DATA,
|
|
&CmPowerData,
|
|
&Size
|
|
) == CR_SUCCESS) {
|
|
|
|
*pCapabilities = CmPowerData.PD_Capabilities;
|
|
return TRUE;
|
|
}
|
|
|
|
*pCapabilities = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsRAW()
|
|
{
|
|
DWORD Capabilities;
|
|
|
|
return (m_pMachine->CmGetCapabilities(m_DevData.DevInst, &Capabilities) &&
|
|
(CM_DEVCAP_RAWDEVICEOK & Capabilities));
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsHidden()
|
|
{
|
|
CClass *pClass = GetClass();
|
|
|
|
//
|
|
// A device is hidden if one of the following are TRUE:
|
|
//
|
|
// - It's class is a NoDisplayClass
|
|
// - It has the DN_NO_SHOW_IN_DM Status flag set
|
|
// - It is a Phantom devnode
|
|
//
|
|
return (NoShowInDM() || IsPhantom() || pClass->NoDisplay());
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsPhantom()
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
return !m_pMachine->CmGetStatus(m_DevData.DevInst, &Problem, &Status) &&
|
|
(CR_NO_SUCH_VALUE == m_pMachine->GetLastCR() ||
|
|
CR_NO_SUCH_DEVINST == m_pMachine->GetLastCR());
|
|
}
|
|
|
|
BOOL
|
|
CDevice::NoShowInDM()
|
|
{
|
|
DWORD Status, Problem;
|
|
Status = 0;
|
|
GetStatus(&Status, &Problem);
|
|
return (Status & DN_NO_SHOW_IN_DM);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsUninstallable(
|
|
)
|
|
/*++
|
|
|
|
This function determins whether a device can be uninstalled. A device
|
|
cannot be uninstalled if it is a ROOT device and it does not have
|
|
the DN_DISABLEABLE DevNode status bit set.
|
|
|
|
Return Value:
|
|
TRUE if the device can be uninstalled.
|
|
FALSE if the device cannot be uninstalled.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
if (GetStatus(&Status, &Problem) &&
|
|
!(Status & DN_DISABLEABLE) &&
|
|
(Status & DN_ROOT_ENUMERATED)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsDisableable(
|
|
)
|
|
/*++
|
|
|
|
This function determins whether a device can be disabled or not by
|
|
checking the DN_DISABLEABLE DevNode status bit.
|
|
|
|
A device that is currently Hardware Disabled cannot be software disabled.
|
|
|
|
Return Value:
|
|
TRUE if the device can be disabled.
|
|
FALSE if the device cannot be disabled.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
if (GetStatus(&Status, &Problem) &&
|
|
(Status & DN_DISABLEABLE) &&
|
|
(CM_PROB_HARDWARE_DISABLED != Problem)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsDisabled(
|
|
)
|
|
/*++
|
|
|
|
A device is disabled if it has the problem CM_PROB_DISABLED.
|
|
|
|
Return Value:
|
|
TRUE if device is disabled.
|
|
FALSE if device is NOT disabled.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
if (GetStatus(&Status, &Problem))
|
|
{
|
|
return ((Status & DN_HAS_PROBLEM) && (CM_PROB_DISABLED == Problem));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsStateDisabled(
|
|
)
|
|
/*++
|
|
|
|
A device state is disabled if it has the CONFIGFLAG_DISABLED ConfigFlag
|
|
set or the CSCONFIGFLAG_DISABLED Config Specific ConfigFlag disabled in
|
|
the current profile.
|
|
|
|
Note that a device disabled State has nothing to do with whether the device
|
|
is currently physically disabled or not. The disabled state is just a registry
|
|
flag that tells Plug and Play what to do with the device the next time it is
|
|
started.
|
|
|
|
Return Value:
|
|
TRUE if device's state is disabled.
|
|
FALSE if device's state is NOT disabled.
|
|
|
|
--*/
|
|
{
|
|
ULONG hwpfCurrent;
|
|
DWORD Flags;
|
|
|
|
//
|
|
// Check if the device state is globally disabled by checking it's ConfigFlags
|
|
//
|
|
GetConfigFlags(&Flags);
|
|
if (Flags & CONFIGFLAG_DISABLED) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if the device state is disabled in the current hardware profile by
|
|
// checking it's Config Specific ConfigFlags.
|
|
//
|
|
if (m_pMachine->CmGetCurrentHwProfile(&hwpfCurrent) &&
|
|
m_pMachine->CmGetHwProfileFlags(m_DevData.DevInst, hwpfCurrent, &Flags) &&
|
|
(Flags & CSCONFIGFLAG_DISABLED)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsStarted()
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
//
|
|
// Check to see if the DN_STARTED devnode status flag is set.
|
|
//
|
|
if (GetStatus(&Status, &Problem) &&
|
|
(Status & DN_STARTED))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::HasProblem(
|
|
)
|
|
/*++
|
|
|
|
This function returns whether a device has a problem or not.
|
|
|
|
Return Value:
|
|
TRUE if device has a problem.
|
|
FALSE if device does not have a problem.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
if (GetStatus(&Status, &Problem))
|
|
{
|
|
//
|
|
// If the DN_HAS_PROBLEM or DN_PRIVATE_PROBLEM status bits are set
|
|
// then this device has a problem, unless the problem is CM_PROB_MOVED.
|
|
//
|
|
if ((Status & DN_PRIVATE_PROBLEM) ||
|
|
((Status & DN_HAS_PROBLEM) && (Problem != CM_PROB_MOVED)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If the device is not started and RAW capable then it also has a problem
|
|
//
|
|
if (!(Status & DN_STARTED) && IsRAW())
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::NeedsRestart(
|
|
)
|
|
/*++
|
|
|
|
This function returns whether a device needs a restart or not. It checks the
|
|
DN_NEED_RESTART Status flag.
|
|
|
|
Return Value:
|
|
TRUE if device needs the computer to be restarted for it to work properly.
|
|
FALSE if device does not need the computer to be restarted.
|
|
|
|
--*/
|
|
{
|
|
DWORD Status, Problem;
|
|
|
|
if (GetStatus(&Status, &Problem))
|
|
{
|
|
return (Status & DN_NEED_RESTART);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
CDevice*
|
|
CDevice::FindMFParent()
|
|
{
|
|
if (!IsMFChild())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(m_pParent);
|
|
|
|
CDevice* pDevice = m_pParent;
|
|
|
|
while (pDevice->IsMFChild())
|
|
{
|
|
pDevice = pDevice->GetParent();
|
|
}
|
|
|
|
return pDevice;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsMFChild(
|
|
)
|
|
{
|
|
DWORD Status, Problem;
|
|
Status = 0;
|
|
GetStatus(&Status, &Problem);
|
|
return (Status & DN_MF_CHILD);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsSpecialMFChild(
|
|
)
|
|
{
|
|
if (IsMFChild())
|
|
{
|
|
DWORD Flags;
|
|
|
|
if (GetConfigFlags(&Flags))
|
|
{
|
|
return (Flags & CONFIGFLAG_CANTSTOPACHILD);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// This function determines if the device is a pnp device
|
|
// INPUT:
|
|
// None
|
|
// OUTPUT:
|
|
// TRUE if the device is a pnp device
|
|
// FLASE if the device is not a pnp device or undetermined
|
|
BOOL
|
|
CDevice::IsPnpDevice()
|
|
{
|
|
// A device is pnp device if the device id is not led
|
|
// by "root" and DN_ROOT_ENUMERATED is not set
|
|
// in its status.
|
|
if (!m_strDeviceID.IsEmpty())
|
|
{
|
|
// unfortunately, we do not have lstrcmpin library function
|
|
TCHAR DeviceId[MAX_DEVICE_ID_LEN + 1];
|
|
int len = lstrlen(REGSTR_KEY_ROOTENUM);
|
|
|
|
lstrcpyn(DeviceId, (LPCTSTR)m_strDeviceID, len);
|
|
DeviceId[len] = _T('\0');
|
|
|
|
if (lstrcmpi(DeviceId, REGSTR_KEY_ROOTENUM))
|
|
{
|
|
// the device id does not start with "root"
|
|
// check the status
|
|
DWORD Problem, Status;
|
|
|
|
if (GetStatus(&Status, &Problem))
|
|
{
|
|
return !(Status & DN_ROOT_ENUMERATED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsBiosDevice()
|
|
{
|
|
// A device is bios enumerated if its device id
|
|
// is led by "root" and its status does not
|
|
// have DN_ROOT_ENUMERATED
|
|
if (!m_strDeviceID.IsEmpty())
|
|
{
|
|
TCHAR DeviceId[MAX_DEVICE_ID_LEN + 1];
|
|
int len = lstrlen(REGSTR_KEY_ROOTENUM);
|
|
|
|
lstrcpyn(DeviceId, (LPCTSTR)m_strDeviceID, len);
|
|
DeviceId[len] = _T('\0');
|
|
|
|
if (!lstrcmpi(DeviceId, REGSTR_KEY_ROOTENUM))
|
|
{
|
|
// the device id starts with "root"
|
|
// check the status
|
|
DWORD Problem, Status;
|
|
|
|
if (GetStatus(&Status, &Problem))
|
|
{
|
|
return !(Status & DN_ROOT_ENUMERATED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsPCMCIA()
|
|
{
|
|
ASSERT(m_pClass);
|
|
|
|
// if the device's class is PCMCIA, it is a PCMCIA device
|
|
if (IsEqualGUID(*m_pClass, GUID_DEVCLASS_PCMCIA))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// if the device has ancestor(s), it is a PCMCIA if one of
|
|
// its ancestor is PCMCIA
|
|
if (m_pParent)
|
|
{
|
|
return m_pParent->IsPCMCIA();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::IsPCIDevice()
|
|
{
|
|
GUID BusGuid;
|
|
CONFIGRET ConfigRet;
|
|
|
|
if (m_pMachine->CmGetBusGuid(GetDevNode(), &BusGuid) &&
|
|
IsEqualGUID(BusGuid, GUID_BUS_TYPE_PCI)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetConfigFlags(
|
|
DWORD* pFlags
|
|
)
|
|
{
|
|
return m_pMachine->CmGetConfigFlags(m_DevData.DevInst, pFlags);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetConfigSpecificConfigFlags(
|
|
DWORD* pCSStatus
|
|
)
|
|
{
|
|
ULONG hwpfCurrent;
|
|
|
|
if (m_pMachine->CmGetCurrentHwProfile(&hwpfCurrent) &&
|
|
m_pMachine->CmGetHwProfileFlags(m_DevData.DevInst, hwpfCurrent, pCSStatus)) {
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::GetKnownLogConf(LOG_CONF* plc, DWORD* plcType)
|
|
{
|
|
return m_pMachine->CmGetKnownLogConf(m_DevData.DevInst, plc, plcType);
|
|
}
|
|
|
|
BOOL
|
|
CDevice::HasResources()
|
|
{
|
|
return m_pMachine->CmHasResources(m_DevData.DevInst);
|
|
}
|
|
|
|
void
|
|
CDevice::GetMFGString(
|
|
String& strMFG
|
|
)
|
|
{
|
|
m_pMachine->CmGetMFGString(m_DevData.DevInst, strMFG);
|
|
|
|
if (strMFG.IsEmpty())
|
|
{
|
|
strMFG.LoadString(g_hInstance, IDS_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
void
|
|
CDevice::GetProviderString(
|
|
String& strProvider
|
|
)
|
|
{
|
|
m_pMachine->CmGetProviderString(m_DevData.DevInst, strProvider);
|
|
|
|
if (strProvider.IsEmpty()) {
|
|
|
|
strProvider.LoadString(g_hInstance, IDS_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
void
|
|
CDevice::GetDriverDateString(
|
|
String& strDriverDate
|
|
)
|
|
{
|
|
FILETIME ft;
|
|
|
|
strDriverDate.Empty();
|
|
|
|
//
|
|
// First try to get the driver date FileTime data from the registry,
|
|
// this way we can localize the date.
|
|
//
|
|
if (m_pMachine->CmGetDriverDateData(m_DevData.DevInst, &ft)) {
|
|
|
|
SYSTEMTIME SystemTime;
|
|
TCHAR DriverDate[MAX_PATH];
|
|
|
|
DriverDate[0] = TEXT('\0');
|
|
|
|
if (FileTimeToSystemTime(&ft, &SystemTime)) {
|
|
|
|
if (GetDateFormat(LOCALE_USER_DEFAULT,
|
|
DATE_SHORTDATE,
|
|
&SystemTime,
|
|
NULL,
|
|
DriverDate,
|
|
sizeof(DriverDate)/sizeof(TCHAR)
|
|
) != 0) {
|
|
|
|
strDriverDate = DriverDate;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We couldn't get the FileTime data so just get the DriverDate string
|
|
// from the registry.
|
|
//
|
|
m_pMachine->CmGetDriverDateString(m_DevData.DevInst, strDriverDate);
|
|
}
|
|
|
|
if (strDriverDate.IsEmpty()) {
|
|
|
|
strDriverDate.LoadString(g_hInstance, IDS_NOT_AVAILABLE);
|
|
}
|
|
}
|
|
|
|
void
|
|
CDevice::GetDriverVersionString(
|
|
String& strDriverVersion
|
|
)
|
|
{
|
|
m_pMachine->CmGetDriverVersionString(m_DevData.DevInst, strDriverVersion);
|
|
|
|
if (strDriverVersion.IsEmpty()) {
|
|
|
|
strDriverVersion.LoadString(g_hInstance, IDS_NOT_AVAILABLE);
|
|
}
|
|
}
|
|
|
|
LPCTSTR
|
|
CDevice::GetClassDisplayName()
|
|
{
|
|
if (m_pClass)
|
|
{
|
|
return m_pClass->GetDisplayName();
|
|
}
|
|
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CDevice::NoChangeUsage()
|
|
{
|
|
SP_DEVINSTALL_PARAMS dip;
|
|
dip.cbSize = sizeof(dip);
|
|
|
|
if (m_pMachine->DiGetDeviceInstallParams(&m_DevData, &dip))
|
|
{
|
|
return (dip.Flags & DI_PROPS_NOCHANGEUSAGE);
|
|
}
|
|
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
CDriver*
|
|
CDevice::CreateDriver()
|
|
{
|
|
SP_DRVINFO_DATA DrvInfoData;
|
|
PSP_DRVINFO_DATA pDrvInfoData;
|
|
CDriver* pDriver = NULL;
|
|
|
|
DrvInfoData.cbSize = sizeof(DrvInfoData);
|
|
|
|
// If the device has a selected driver, use it. Otherwise,
|
|
// we pass a NULL drvinfodata so that CDriver will search
|
|
// for the device driver key or service to create
|
|
// a list of driver files.
|
|
if (m_pMachine->DiGetSelectedDriver(&m_DevData, &DrvInfoData))
|
|
{
|
|
pDrvInfoData = &DrvInfoData;
|
|
}
|
|
|
|
else
|
|
{
|
|
pDrvInfoData = NULL;
|
|
}
|
|
|
|
pDriver = new CDriver();
|
|
|
|
if (!pDriver) {
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
// if we have a selected driver and we failed to
|
|
// to create the driver, retry it without the selected driver
|
|
//
|
|
if (pDrvInfoData && pDriver->Create(this, pDrvInfoData)) {
|
|
|
|
return pDriver;
|
|
}
|
|
|
|
pDriver->Create(this, NULL);
|
|
|
|
return pDriver;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::HasDrivers()
|
|
{
|
|
HKEY hKey;
|
|
BOOL HasDriverKey, HasServiceKey;
|
|
CDriver DeviceDriver;
|
|
BOOL Result;
|
|
|
|
HasDriverKey = FALSE;
|
|
|
|
if (m_pMachine->IsLocal())
|
|
{
|
|
// open drvice's driver registry key
|
|
hKey = m_pMachine->DiOpenDevRegKey(&m_DevData, DICS_FLAG_GLOBAL,
|
|
0, DIREG_DRV, KEY_ALL_ACCESS);
|
|
|
|
HasDriverKey = INVALID_HANDLE_VALUE != hKey;
|
|
|
|
if (INVALID_HANDLE_VALUE != hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
DWORD Size = 0;
|
|
|
|
m_pMachine->DiGetDeviceRegistryProperty(&m_DevData, SPDRP_SERVICE,
|
|
NULL, NULL, 0, &Size);
|
|
|
|
HasServiceKey = (0 != Size);
|
|
|
|
if (HasServiceKey || HasDriverKey)
|
|
{
|
|
// either we have a driver or a service key.
|
|
// try to find out if we can find any valid driver files.
|
|
Result = DeviceDriver.Create(this);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (m_pMachine->IsLocal() && g_HasLoadDriverNamePrivilege)
|
|
{
|
|
// If the target machine is local and the user has the
|
|
// administrator privilege, we need the driver page
|
|
// for users to update the drivers.
|
|
//
|
|
Result = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
DWORD
|
|
CDevice::EnableDisableDevice(
|
|
HWND hDlg,
|
|
BOOL Enabling
|
|
)
|
|
{
|
|
BOOL Disabling = !Enabling;
|
|
BOOL Canceled;
|
|
Canceled = FALSE;
|
|
DWORD RestartFlags = 0;
|
|
DWORD ConfigFlags;
|
|
HCURSOR hCursorOld = NULL;
|
|
BOOL Refresh = FALSE;
|
|
|
|
//
|
|
// Disable refreshing the TREE while we are enabling/disabling this device
|
|
//
|
|
m_pMachine->EnableRefresh(FALSE);
|
|
|
|
if (!GetConfigFlags(&ConfigFlags)) {
|
|
ConfigFlags = 0;
|
|
}
|
|
|
|
//
|
|
// Only want the disabled bit
|
|
//
|
|
ConfigFlags &= CONFIGFLAG_DISABLED;
|
|
|
|
CHwProfileList* pHwProfileList = new CHwProfileList();
|
|
|
|
if (!pHwProfileList) {
|
|
goto clean0;
|
|
}
|
|
|
|
pHwProfileList->Create(this, ConfigFlags);
|
|
|
|
//
|
|
// Get the current profile
|
|
//
|
|
CHwProfile* phwpf;
|
|
|
|
if (!(pHwProfileList->GetCurrentHwProfile(&phwpf))) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Can only enable a device that is currently disabled
|
|
//
|
|
if (IsStateDisabled() && Enabling) {
|
|
phwpf->SetEnablePending();
|
|
}
|
|
|
|
//
|
|
// Can only disable a device that is currently enabled
|
|
//
|
|
else if (!IsStateDisabled() && Disabling) {
|
|
phwpf->SetDisablePending();
|
|
}
|
|
|
|
//
|
|
// If we don't have a valid enable or disable then exit
|
|
//
|
|
if (!(phwpf->IsEnablePending()) && !(phwpf->IsDisablePending())) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// This device is not a boot device so just display the normal disable
|
|
// warning to the user.
|
|
//
|
|
if (Disabling) {
|
|
int MsgBoxResult;
|
|
TCHAR szText[MAX_PATH];
|
|
DWORD Size;
|
|
|
|
Size = LoadResourceString(IDS_WARN_NORMAL_DISABLE, szText, ARRAYLEN(szText));
|
|
MsgBoxResult = MessageBox(hDlg,
|
|
szText,
|
|
GetDisplayName(),
|
|
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2
|
|
);
|
|
|
|
if (IDYES != MsgBoxResult) {
|
|
goto clean0;
|
|
}
|
|
}
|
|
|
|
hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
//
|
|
// If this isn't a live devnode then we need to do a manual refresh if we
|
|
// are diabling the device.
|
|
//
|
|
Refresh = (!Enabling &&
|
|
(IsPhantom() ||
|
|
HasProblem() ||
|
|
!IsStarted()));
|
|
|
|
m_pMachine->DiTurnOnDiFlags(*this, DI_NODI_DEFAULTACTION);
|
|
|
|
SP_PROPCHANGE_PARAMS pcp;
|
|
pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
|
|
pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
|
|
|
|
//
|
|
// Now ask the class installer if the device can be specifically enabled/disabled
|
|
//
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.StateChange = DICS_DISABLE;
|
|
|
|
if (phwpf->IsEnablePending()) {
|
|
pcp.StateChange = DICS_ENABLE;
|
|
}
|
|
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
|
|
m_pMachine->DiSetClassInstallParams(*this,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pMachine->DiCallClassInstaller(DIF_PROPERTYCHANGE, *this);
|
|
Canceled = (ERROR_CANCELLED == GetLastError());
|
|
|
|
//
|
|
// class installer has not objection of our enabling/disabling,
|
|
// do real enabling/disabling.
|
|
//
|
|
if (!Canceled) {
|
|
if (phwpf->IsDisablePending()) {
|
|
pcp.StateChange = DICS_DISABLE;
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
m_pMachine->DiSetClassInstallParams(*this,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pMachine->DiChangeState(*this);
|
|
}
|
|
|
|
else {
|
|
//
|
|
// We are enabling the device,
|
|
// do a specific enabling then a globally enabling.
|
|
// the globally enabling will start the device
|
|
// The implementation here is different from
|
|
// Win9x which does a global enabling, a config
|
|
// specific enabling and then a start.
|
|
//
|
|
pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
|
|
pcp.HwProfile = phwpf->GetHwProfile();
|
|
m_pMachine->DiSetClassInstallParams(*this,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pMachine->DiChangeState(*this);
|
|
|
|
//
|
|
// This call will start the device is it not started.
|
|
//
|
|
pcp.Scope = DICS_FLAG_GLOBAL;
|
|
m_pMachine->DiSetClassInstallParams(*this,
|
|
&pcp.ClassInstallHeader,
|
|
sizeof(pcp)
|
|
);
|
|
|
|
m_pMachine->DiChangeState(*this);
|
|
}
|
|
|
|
if (phwpf->IsEnablePending()) {
|
|
phwpf->ResetEnablePending();
|
|
}
|
|
|
|
else if (phwpf->IsDisablePending()) {
|
|
phwpf->ResetDisablePending();
|
|
}
|
|
|
|
//
|
|
// signal that the property of the device is changed.
|
|
//
|
|
m_pMachine->DiTurnOnDiFlags(*this, DI_PROPERTIES_CHANGE);
|
|
|
|
//
|
|
// See if we need a restart.
|
|
//
|
|
RestartFlags |= (m_pMachine->DiGetFlags(*this)) & (DI_NEEDRESTART | DI_NEEDREBOOT);
|
|
|
|
if (NeedsRestart()) {
|
|
RestartFlags |= DI_NEEDRESTART;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove class install parameters, this also reset
|
|
// DI_CLASSINATLLPARAMS
|
|
//
|
|
m_pMachine->DiSetClassInstallParams(*this, NULL, 0);
|
|
|
|
m_pMachine->DiTurnOffDiFlags(*this, DI_NODI_DEFAULTACTION);
|
|
|
|
clean0:
|
|
if (pHwProfileList) {
|
|
delete pHwProfileList;
|
|
}
|
|
|
|
//
|
|
// Enable the tree for refreshing.
|
|
// We will only schedule a refresh ourselves if the device was not started
|
|
// before we tried to disable it, and we are not going to prompt for a reboot.
|
|
// In all other cases we should get a WM_DEVICECHANGE which will cause us
|
|
// to refresh our tree.
|
|
//
|
|
if (Refresh && !NeedsRestart()) {
|
|
m_pMachine->ScheduleRefresh();
|
|
}
|
|
|
|
m_pMachine->EnableRefresh(TRUE);
|
|
|
|
if (hCursorOld != NULL) {
|
|
SetCursor(hCursorOld);
|
|
}
|
|
|
|
return RestartFlags;
|
|
}
|
|
|
|
BOOL
|
|
CDevice::operator ==(
|
|
CDevice& OtherDevice
|
|
)
|
|
{
|
|
return \
|
|
(*m_pClass == *(OtherDevice.GetClass())) &&
|
|
!lstrcmpi(m_strDeviceID, OtherDevice.GetDeviceID()) &&
|
|
!lstrcmpi(m_strDisplayName, OtherDevice.GetDisplayName());
|
|
}
|
|
|
|
//
|
|
// CComputer implementation
|
|
//
|
|
|
|
CComputer::CComputer(
|
|
CMachine* pMachine,
|
|
DEVNODE dnRoot
|
|
)
|
|
{
|
|
ASSERT(pMachine);
|
|
ASSERT(!GetChild() && !GetParent() && !GetSibling());
|
|
|
|
m_pMachine = pMachine;
|
|
m_strDisplayName.Empty();
|
|
m_strDisplayName = pMachine->GetMachineDisplayName();
|
|
m_iImage = pMachine->GetComputerIconIndex();
|
|
m_dnRoot = dnRoot;
|
|
}
|
|
|
|
inline
|
|
CItemIdentifier*
|
|
CComputer::CreateIdentifier()
|
|
{
|
|
return new CComputerIdentifier(*this);
|
|
}
|
|
|
|
|
|
CResource::CResource(
|
|
CDevice* pDevice,
|
|
RESOURCEID ResType,
|
|
DWORDLONG dlBase,
|
|
DWORDLONG dlLen,
|
|
BOOL Forced,
|
|
BOOL Free
|
|
)
|
|
{
|
|
m_pChild = NULL;
|
|
m_pSibling = NULL;
|
|
m_pParent = NULL;
|
|
m_ResType = ResType;
|
|
m_dlBase = dlBase;
|
|
m_dlLen = dlLen;
|
|
m_Forced = Forced;
|
|
m_dlEnd = m_dlBase + m_dlLen - 1;
|
|
m_Allocated = !Free;
|
|
ASSERT(pDevice);
|
|
m_pDevice = pDevice;
|
|
m_iImage = pDevice->GetImageIndex();
|
|
ASSERT(ResType >= ResType_Mem && ResType <= ResType_IRQ);
|
|
|
|
m_strDisplayName.Empty();
|
|
m_strDisplayName = pDevice->GetDisplayName();
|
|
|
|
if (ResType_IRQ == m_ResType)
|
|
{
|
|
String strBus;
|
|
|
|
strBus.LoadString(g_hInstance,
|
|
pDevice->IsPCIDevice() ? IDS_PCI : IDS_ISA
|
|
);
|
|
|
|
m_strViewName.Format(TEXT("%2d "), m_dlBase);
|
|
m_strViewName = strBus + m_strViewName;
|
|
}
|
|
|
|
else if (ResType_DMA == m_ResType)
|
|
{
|
|
m_strViewName.Format(TEXT("%2d " ), m_dlBase);
|
|
}
|
|
|
|
else
|
|
{
|
|
#ifdef _WIN64
|
|
m_strViewName.Format(TEXT("[%016I64X - %016I64X] "), m_dlBase, m_dlEnd);
|
|
#else
|
|
m_strViewName.Format(TEXT("[%08lX - %08lX] "), (ULONG)m_dlBase, (ULONG)m_dlEnd);
|
|
#endif
|
|
}
|
|
|
|
if (m_Allocated)
|
|
{
|
|
m_strViewName += pDevice->GetDisplayName();
|
|
}
|
|
}
|
|
|
|
void
|
|
CResource::GetRangeString(
|
|
String& strRange
|
|
)
|
|
{
|
|
if (ResType_IRQ == m_ResType ||
|
|
ResType_DMA == m_ResType)
|
|
{
|
|
strRange.Format(TEXT("%2d"), m_dlBase);
|
|
}
|
|
|
|
else
|
|
{
|
|
strRange.Format(TEXT("[%08lX - %08lX]"), (ULONG)m_dlBase, (ULONG)m_dlEnd);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CResource::operator <=(
|
|
const CResource& resSrc
|
|
)
|
|
{
|
|
DWORDLONG dlBase, dlLen;
|
|
|
|
resSrc.GetValue(&dlBase, &dlLen);
|
|
|
|
if (m_dlBase < dlBase)
|
|
return TRUE;
|
|
|
|
// if this resource contain the given resource,
|
|
// we are smaller!
|
|
if (m_dlBase == dlBase)
|
|
return (m_dlBase + m_dlLen > dlBase + dlLen);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CResource::EnclosedBy(
|
|
const CResource& resSrc
|
|
)
|
|
{
|
|
DWORDLONG dlBase, dlLen;
|
|
resSrc.GetValue(&dlBase, &dlLen);
|
|
return m_dlBase >= dlBase && m_dlBase + m_dlLen <= dlBase + dlLen;
|
|
}
|
|
|
|
|
|
CResourceType::CResourceType(
|
|
CMachine* pMachine,
|
|
RESOURCEID ResType
|
|
)
|
|
{
|
|
int iStringID;
|
|
|
|
m_ResType = ResType;
|
|
m_pChild = NULL;
|
|
m_pSibling = NULL;
|
|
m_pParent = NULL;
|
|
m_pMachine = pMachine;
|
|
ASSERT(ResType >= ResType_Mem && ResType <= ResType_IRQ);
|
|
|
|
switch (ResType)
|
|
{
|
|
case ResType_IRQ:
|
|
iStringID = IDS_VIEW_RESOURCE_IRQ;
|
|
break;
|
|
|
|
case ResType_IO:
|
|
iStringID = IDS_VIEW_RESOURCE_IO;
|
|
break;
|
|
|
|
case ResType_DMA:
|
|
iStringID = IDS_VIEW_RESOURCE_DMA;
|
|
break;
|
|
|
|
case ResType_Mem:
|
|
iStringID = IDS_VIEW_RESOURCE_MEM;
|
|
break;
|
|
|
|
default:
|
|
iStringID = IDS_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
m_strDisplayName.Empty();
|
|
m_strDisplayName.LoadString(g_hInstance, iStringID);
|
|
m_iImage = pMachine->GetResourceIconIndex();
|
|
}
|
|
|
|
inline
|
|
CItemIdentifier*
|
|
CResourceType::CreateIdentifier()
|
|
{
|
|
return new CResourceTypeIdentifier(*this);
|
|
}
|
|
|
|
|
|
// This function creates CResourceList object to contain the designated
|
|
// resources for the given device.
|
|
// INPUT:
|
|
// pDevice -- the device
|
|
// ResType -- what type of resource
|
|
// LogConfType -- what type of logconf
|
|
// OUTPUT:
|
|
// NONE.
|
|
//
|
|
// This function may throw CMemoryException
|
|
//
|
|
CResourceList::CResourceList(
|
|
CDevice* pDevice,
|
|
RESOURCEID ResType,
|
|
ULONG LogConfType,
|
|
ULONG AltLogConfType
|
|
)
|
|
{
|
|
ASSERT(ResType_All != ResType);
|
|
ASSERT(BOOT_LOG_CONF == LogConfType ||
|
|
FORCED_LOG_CONF == LogConfType ||
|
|
ALLOC_LOG_CONF == LogConfType);
|
|
ASSERT(pDevice);
|
|
|
|
LOG_CONF lc;
|
|
RES_DES rd, rdPrev;
|
|
rdPrev;
|
|
RESOURCEID ResId;
|
|
BOOL Forced;
|
|
CMachine* pMachine = pDevice->m_pMachine;
|
|
ASSERT(pMachine);
|
|
|
|
rdPrev = 0;
|
|
|
|
// even though we have a valid logconf, it does not mean
|
|
// GetNextResDes would succeed because the ResType is not
|
|
// ResType_All.
|
|
if (pMachine->CmGetFirstLogConf(pDevice->GetDevNode(), &lc, LogConfType))
|
|
{
|
|
if (pMachine->CmGetNextResDes(&rd, lc, ResType, &ResId))
|
|
{
|
|
ULONG DataSize;
|
|
DWORDLONG dlBase, dlLen;
|
|
|
|
do
|
|
{
|
|
DataSize = pMachine->CmGetResDesDataSize(rd);
|
|
|
|
if (DataSize)
|
|
{
|
|
BufferPtr<BYTE> DataPtr(DataSize);
|
|
|
|
if (pMachine->CmGetResDesData(rd, DataPtr, DataSize))
|
|
{
|
|
// need this to use a different image overlay for
|
|
// forced allocated resource
|
|
Forced = pMachine->CmGetFirstLogConf(pDevice->GetDevNode(),
|
|
NULL, FORCED_LOG_CONF);
|
|
|
|
if (ExtractResourceValue(ResType, DataPtr, &dlBase, &dlLen))
|
|
{
|
|
SafePtr<CResource> ResPtr;
|
|
CResource* pRes;
|
|
|
|
pRes = new CResource(pDevice, ResType, dlBase,
|
|
dlLen, Forced, FALSE);
|
|
|
|
if (pRes)
|
|
{
|
|
ResPtr.Attach(pRes);
|
|
InsertResourceToList(pRes);
|
|
ResPtr.Detach();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rdPrev)
|
|
{
|
|
pMachine->CmFreeResDesHandle(rdPrev);
|
|
}
|
|
|
|
rdPrev = rd;
|
|
|
|
} while (pMachine->CmGetNextResDes(&rd, rdPrev, ResType, &ResId));
|
|
|
|
//free the last resource descriptor handle
|
|
pMachine->CmFreeResDesHandle(rd);
|
|
}
|
|
|
|
pMachine->CmFreeLogConfHandle(lc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This function creates CResourceList object to contain the designated
|
|
// resources for the given machine.
|
|
// INPUT:
|
|
// pMachine -- the machine
|
|
// ResType -- what type of resource
|
|
// LogConfType -- what type of logconf
|
|
// OUTPUT:
|
|
// NONE.
|
|
//
|
|
// This function may throw CMemoryException
|
|
//
|
|
CResourceList::CResourceList(
|
|
CMachine* pMachine,
|
|
RESOURCEID ResType,
|
|
ULONG LogConfType,
|
|
ULONG AltLogConfType
|
|
)
|
|
{
|
|
ASSERT(ResType_All != ResType);
|
|
ASSERT(BOOT_LOG_CONF == LogConfType ||
|
|
FORCED_LOG_CONF == LogConfType ||
|
|
ALLOC_LOG_CONF == LogConfType ||
|
|
ALL_LOG_CONF == LogConfType);
|
|
|
|
ASSERT(pMachine);
|
|
|
|
if (pMachine->GetNumberOfDevices())
|
|
{
|
|
ASSERT(pMachine->m_pComputer && pMachine->m_pComputer->GetChild());
|
|
|
|
CDevice* pFirstDevice;
|
|
|
|
pFirstDevice = pMachine->m_pComputer->GetChild();
|
|
CreateSubtreeResourceList(pFirstDevice, ResType, LogConfType, AltLogConfType);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This function extracts resource value from the provided buffer
|
|
//
|
|
// INPUT:
|
|
// ResType -- resource type the data contain
|
|
// pData -- the raw data
|
|
// pdlBase -- buffer to hold the base of the value
|
|
// pdlLen -- buffer to hold the length of the value
|
|
//
|
|
// OUTPUT:
|
|
// TRUE if this is a valid resource descriptor or FALSE if we should ignore it.
|
|
//
|
|
// NOTE:
|
|
// If the return value is FALSE then pdlBase and pdlLen are not filled in.
|
|
//
|
|
BOOL
|
|
CResourceList::ExtractResourceValue(
|
|
RESOURCEID ResType,
|
|
PVOID pData,
|
|
DWORDLONG* pdlBase,
|
|
DWORDLONG* pdlLen
|
|
)
|
|
{
|
|
BOOL bValidResDes = TRUE;
|
|
|
|
ASSERT(pData && pdlBase && pdlLen);
|
|
|
|
switch (ResType)
|
|
{
|
|
case ResType_Mem:
|
|
if (pMemResData(pData)->MEM_Header.MD_Alloc_Base <= pMemResData(pData)->MEM_Header.MD_Alloc_End) {
|
|
|
|
*pdlBase = pMemResData(pData)->MEM_Header.MD_Alloc_Base;
|
|
*pdlLen = pMemResData(pData)->MEM_Header.MD_Alloc_End -
|
|
*pdlBase + 1;
|
|
} else {
|
|
//
|
|
// If base > end then ignore this resource descriptor
|
|
//
|
|
*pdlBase = 0;
|
|
*pdlLen = 0;
|
|
bValidResDes = FALSE;
|
|
}
|
|
break;
|
|
|
|
case ResType_IRQ:
|
|
*pdlBase = pIRQResData(pData)->IRQ_Header.IRQD_Alloc_Num;
|
|
// IRQ len is always 1
|
|
*pdlLen = 1;
|
|
break;
|
|
|
|
case ResType_DMA:
|
|
*pdlBase = pDMAResData(pData)->DMA_Header.DD_Alloc_Chan;
|
|
// DMA len is always 1
|
|
*pdlLen = 1;
|
|
break;
|
|
|
|
case ResType_IO:
|
|
if (pIOResData(pData)->IO_Header.IOD_Alloc_Base <= pIOResData(pData)->IO_Header.IOD_Alloc_End) {
|
|
|
|
*pdlBase = pIOResData(pData)->IO_Header.IOD_Alloc_Base;
|
|
*pdlLen = pIOResData(pData)->IO_Header.IOD_Alloc_End -
|
|
*pdlBase + 1;
|
|
} else {
|
|
//
|
|
// If base > end then ignore this resource descriptor
|
|
//
|
|
*pdlBase = 0;
|
|
*pdlLen = 0;
|
|
bValidResDes = FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
*pdlBase = 0;
|
|
*pdlLen = 0;
|
|
break;
|
|
}
|
|
|
|
return bValidResDes;
|
|
}
|
|
|
|
|
|
//
|
|
//This function creates resources for the given subtree rooted at
|
|
//the given device
|
|
//
|
|
//INPUT:
|
|
// pDevice -- the root device of the subtree
|
|
// ResType -- resource type to be created
|
|
// LogConfType -- logconf type to be created from
|
|
//
|
|
//OUTPUT:
|
|
// NONE
|
|
//
|
|
// This function may throw CMemoryException
|
|
//
|
|
void
|
|
CResourceList::CreateSubtreeResourceList(
|
|
CDevice* pDeviceStart,
|
|
RESOURCEID ResType,
|
|
ULONG LogConfType,
|
|
ULONG AltLogConfType
|
|
)
|
|
{
|
|
LOG_CONF lc;
|
|
RES_DES rd, rdPrev;
|
|
RESOURCEID ResId;
|
|
BOOL Forced;
|
|
CMachine* pMachine = pDeviceStart->m_pMachine;
|
|
ASSERT(pMachine);
|
|
|
|
while (pDeviceStart)
|
|
{
|
|
//
|
|
// We will try to get a LogConf for either the LogConfType (which defaults to
|
|
// ALLOC_LOG_CONF) or the AltLogConfType (which defaults to BOOT_LOG_CONF).
|
|
// We need to do this because on Win2000 a device that only has a BOOT_LOG_CONF
|
|
// will still consume those resources, even if it does not have an ALLOC_LOG_CONF.
|
|
// So we need to first check the ALLOC_LOG_CONF and if that fails check the
|
|
// BOOT_LOG_CONF.
|
|
//
|
|
if (pMachine->CmGetFirstLogConf(pDeviceStart->GetDevNode(), &lc, LogConfType) ||
|
|
pMachine->CmGetFirstLogConf(pDeviceStart->GetDevNode(), &lc, AltLogConfType))
|
|
{
|
|
rdPrev = 0;
|
|
|
|
if (pMachine->CmGetNextResDes(&rd, lc, ResType, &ResId))
|
|
{
|
|
ULONG DataSize;
|
|
DWORDLONG dlBase, dlLen;
|
|
|
|
do
|
|
{
|
|
DataSize = pMachine->CmGetResDesDataSize(rd);
|
|
|
|
if (DataSize)
|
|
{
|
|
// need this to use a different image overlay for
|
|
// forced allocated resource
|
|
Forced = pMachine->CmGetFirstLogConf(pDeviceStart->GetDevNode(),
|
|
NULL, FORCED_LOG_CONF);
|
|
|
|
BufferPtr<BYTE> DataPtr(DataSize);
|
|
|
|
if (pMachine->CmGetResDesData(rd, DataPtr, DataSize))
|
|
{
|
|
if (ExtractResourceValue(ResType, DataPtr, &dlBase, &dlLen))
|
|
{
|
|
SafePtr<CResource> ResPtr;
|
|
|
|
CResource* pRes;
|
|
|
|
pRes = new CResource(pDeviceStart, ResType, dlBase,
|
|
dlLen, Forced, FALSE);
|
|
ResPtr.Attach(pRes);
|
|
InsertResourceToList(pRes);
|
|
ResPtr.Detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rdPrev)
|
|
pMachine->CmFreeResDesHandle(rdPrev);
|
|
|
|
rdPrev = rd;
|
|
|
|
}while (pMachine->CmGetNextResDes(&rd, rdPrev, ResType, &ResId));
|
|
|
|
//free the last resource descriptor handle
|
|
pMachine->CmFreeResDesHandle(rd);
|
|
}
|
|
|
|
pMachine->CmFreeLogConfHandle(lc);
|
|
}
|
|
|
|
if (pDeviceStart->GetChild())
|
|
CreateSubtreeResourceList(pDeviceStart->GetChild(), ResType, LogConfType, AltLogConfType);
|
|
|
|
pDeviceStart = pDeviceStart->GetSibling();
|
|
}
|
|
}
|
|
|
|
|
|
// This function creates a resource tree
|
|
// INPUT:
|
|
// ppResRoot -- buffer to receive the tree root
|
|
//
|
|
BOOL
|
|
CResourceList::CreateResourceTree(
|
|
CResource** ppResRoot
|
|
)
|
|
{
|
|
ASSERT(ppResRoot);
|
|
|
|
*ppResRoot = NULL;
|
|
|
|
if (!m_listRes.IsEmpty())
|
|
{
|
|
POSITION pos = m_listRes.GetHeadPosition();
|
|
CResource* pResFirst;
|
|
|
|
pResFirst = m_listRes.GetNext(pos);
|
|
*ppResRoot = pResFirst;
|
|
|
|
while (NULL != pos)
|
|
{
|
|
CResource* pRes = m_listRes.GetNext(pos);
|
|
InsertResourceToTree(pRes, pResFirst, TRUE);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CResourceList::InsertResourceToTree(
|
|
CResource* pRes,
|
|
CResource* pResRoot,
|
|
BOOL ForcedInsert
|
|
)
|
|
{
|
|
CResource* pResLast;
|
|
|
|
while (pResRoot)
|
|
{
|
|
if (pRes->EnclosedBy(*pResRoot))
|
|
{
|
|
// this resource is either the pResRoot child or grand child
|
|
// figure out which one it is
|
|
if (!pResRoot->GetChild())
|
|
{
|
|
pResRoot->SetChild(pRes);
|
|
pRes->SetParent(pResRoot);
|
|
}
|
|
|
|
else if (!InsertResourceToTree(pRes, pResRoot->GetChild(), FALSE))
|
|
{
|
|
// the Resource is not a grand child of pResRoot.
|
|
// search for the last child of pResRoot
|
|
CResource* pResSibling;
|
|
pResSibling = pResRoot->GetChild();
|
|
|
|
while (pResSibling->GetSibling())
|
|
pResSibling = pResSibling->GetSibling();
|
|
|
|
pResSibling->SetSibling(pRes);
|
|
pRes->SetParent(pResRoot);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
pResLast = pResRoot;
|
|
pResRoot = pResRoot->GetSibling();
|
|
}
|
|
|
|
if (ForcedInsert)
|
|
{
|
|
// when we reach here, pResLast is the last child
|
|
pResLast->SetSibling(pRes);
|
|
pRes->SetParent(pResLast->GetParent());
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
CResourceList::~CResourceList()
|
|
{
|
|
if (!m_listRes.IsEmpty())
|
|
{
|
|
POSITION pos = m_listRes.GetHeadPosition();
|
|
|
|
while (NULL != pos)
|
|
{
|
|
delete m_listRes.GetNext(pos);
|
|
}
|
|
|
|
m_listRes.RemoveAll();
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CResourceList::GetFirst(
|
|
CResource** ppRes,
|
|
PVOID& Context
|
|
)
|
|
{
|
|
ASSERT(ppRes);
|
|
|
|
if (!m_listRes.IsEmpty())
|
|
{
|
|
POSITION pos = m_listRes.GetHeadPosition();
|
|
*ppRes = m_listRes.GetNext(pos);
|
|
Context = pos;
|
|
return TRUE;
|
|
}
|
|
|
|
Context = NULL;
|
|
*ppRes = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CResourceList::GetNext(
|
|
CResource** ppRes,
|
|
PVOID& Context
|
|
)
|
|
{
|
|
ASSERT(ppRes);
|
|
|
|
POSITION pos = (POSITION)Context;
|
|
|
|
if (NULL != pos)
|
|
{
|
|
*ppRes = m_listRes.GetNext(pos);
|
|
Context = pos;
|
|
return TRUE;
|
|
}
|
|
|
|
*ppRes = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This function inserts the given resource to class's resource list
|
|
// The resources are kept in accending sorted order
|
|
void
|
|
CResourceList::InsertResourceToList(
|
|
CResource* pRes
|
|
)
|
|
{
|
|
POSITION pos;
|
|
CResource* pSrc;
|
|
DWORDLONG dlBase, dlLen;
|
|
pRes->GetValue(&dlBase, &dlLen);
|
|
|
|
pos = m_listRes.GetHeadPosition();
|
|
|
|
while (NULL != pos)
|
|
{
|
|
POSITION posSave = pos;
|
|
pSrc = m_listRes.GetNext(pos);
|
|
|
|
if (*pRes <= *pSrc)
|
|
{
|
|
m_listRes.InsertBefore(posSave, pRes);
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_listRes.AddTail(pRes);
|
|
}
|
|
|
|
|
|
inline
|
|
CItemIdentifier*
|
|
CResource::CreateIdentifier()
|
|
{
|
|
return new CResourceIdentifier(*this);
|
|
}
|