设备属性表对话框






4.82/5 (21投票s)
显示特定设备的属性表对话框。
引言
在我之前的文章中 (枚举已安装设备的属性),我使用了 Setup API 来枚举已安装设备的所有属性。但是这些属性对于所有设备都是通用的,没有特定属性与其他设备的属性不同。例如,声卡具有一些属性,这些属性与网络适配器的属性不同。
而且,在这几个月里,一些开发者询问我关于设备特定属性以及如何以编程方式更改它们。我在网上搜索了,但找不到任何相关文章。现在,这篇文章是我为解决这个问题所做的努力。
逆向工程设备管理器
第一步是搜索 MSDN。我搜索了 MSDN 以查找与设备管理器相关的任何 API;例如,一个获取设备的一些信息(例如,DevNode,设备 GUID 等)并显示属性的对话框(可能是设备属性表)的 API。但是没有这样的 API。至少,我找不到任何一个,如果有人知道这样的 API,我很乐意被告知。
第二步是更多地研究设备管理器。设备管理器是一个 DLL (DevMgr.dll),它在 MMC 中用作管理单元。图 2 显示了它
我使用了 PE File Explorer(一个带有源代码的免费实用程序)来浏览 PE(可移植可执行)文件,如 *.exe、*.dll、*.ocx 等。
使用这个实用程序,我找到了导出的函数。这给了我一个想法。其他程序如何调用这些导出的(以及未文档化的)函数来显示设备属性表?
图 3 显示了打开 DevMgr.dll 的 PEFileExplorer。
使用 PEFileExplorer 完成了一大步。现在我知道了 DLL 导出了哪些函数,这让我可以搜索关于导出的函数。
从 MSDN,我发现 Rundll.exe 和 Rundll32.exe 允许调用从 DLL 导出的函数。但是,Rundll 和 Rundll32 程序不允许您调用来自任何 DLL 的任何导出的函数。例如,您不能使用这些实用程序程序来调用从系统 DLL 导出的 Win32 API(应用程序编程接口)调用。这些程序只允许您调用 DLL 中的函数,这些函数是专门为它们调用的而编写的。
Rundll32.exe 的命令行如下
Rundll32.exe <name of dll>
<entry point function> <arguments>
通过深入研究导出的函数列表,每个人都可以理解 DevMgr.dll 的入口点是 DeviceProperties_RunDLLA
(ANSI 版本) 和 DeviceProperties_RunDLLW
(UNICODE 版本)。正如 Microsoft 知识库文章 - 164787 建议的那样,Rundll32.exe 可以处理入口点函数而无需使用 A 或 W。换句话说,我们需要如下调用入口点
Rundll32.exe devmgr.dll DeviceProperties_RunDLL /DeviceID
root\system\0000
上面的语法意味着 Rundll32.exe 应该调用 DeviceProperties_RunDll
(作为 DevMgr.dll 的入口点)并将 /DeviceID root\system\0000
作为函数的参数传递。
解决方案
在之前的文章中,我向您展示了我们如何获取已安装设备的 DeviceID
。然后通过使用适当的参数调用 Rundll32.exe,将出现已安装设备的属性表。
以下代码显示了一个示例
void CDevicePropertySheetDialogDlg::OnDeviceProperty() { DEVNODE dn; for (int i=0; i<m_Devices.GetItemCount(); i++) { if (m_Devices.GetItemState(i, LVIS_SELECTED)) //item was selected { dn=(DEVNODE) m_Devices.GetItemData(i); break; } } CString CommandLine; //Enumerate properties for (int j=0; j<DeviceProperty.size(); j++) { if (DeviceProperty[j].dn==dn) { CommandLine.Format(_T("DevMgr.dll DeviceProperties_RunDLL /DeviceID \"%s\""), DeviceProperty[j].Properties[ID_DEVICEID].PropertyValue); break; } } ShellExecute(m_hWnd, _T("open"), _T("Rundll32.exe"), CommandLine, NULL, SW_SHOW); }
我假设列表视图控件 (m_Devices
) 中列出的所有设备以及设备的属性(包括 DeviceID
)都保存在 DevicePropert
向量中。
备用方法
另一种方法是使用 LoadLibrary
API 动态加载 DevMgr.dll。 这是使用 LoadLibrary
API 加载设备属性表而更改的代码
void CDevicePropertySheetDialogDlg::OnDeviceProperty() { DEVNODE dn; for (int i=0; i<m_Devices.GetItemCount(); i++) { if (m_Devices.GetItemState(i, LVIS_SELECTED)) //item was selected { //AfxMessageBox(m_Devices.GetItemText(i, 0)); dn=(DEVNODE) m_Devices.GetItemData(i); break; } } CString CommandLine; //Enumerate properties for (int j=0; j<DeviceProperty.size(); j++) { if (DeviceProperty[j].dn==dn) { CommandLine.Format(_T("/MachineName \"\" /DeviceID %s"), DeviceProperty[j].Properties[ID_DEVICEID].PropertyValue); break; } } PDEVICEPROPERTIES pDeviceProperties; HINSTANCE hInst=AfxGetInstanceHandle(); HINSTANCE hDevMgr = LoadLibrary(_TEXT("devmgr.dll")); if (hDevMgr) { pDeviceProperties = (PDEVICEPROPERTIES) GetProcAddress((HMODULE) hDevMgr, DeviceProperties_RunDLL); } if (pDeviceProperties) { pDeviceProperties(m_hWnd, hInst, CommandLine.GetBuffer(0), SW_SHOW); } }
应该注意的是,DeviceProperties_RunDLL
定义如下
#ifdef _UNICODE #define DeviceProperties_RunDLL "DeviceProperties_RunDLLW" typedef void (_stdcall *PDEVICEPROPERTIES)( HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpCmdLine, int nCmdShow ); #else #define DeviceProperties_RunDLL "DeviceProperties_RunDLLA" typedef void (_stdcall *PDEVICEPROPERTIES)( HWND hwndStub, HINSTANCE hAppInstance, LPSTR lpCmdLine, int nCmdShow ); #endif
感谢 Martin Rubas 的评论。
尽情享用!