65.9K
CodeProject 正在变化。 阅读更多。
Home

获取 Windows OS 上运行的驱动程序列表并使用纯 Win32 调用在 ListView 中显示

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (13投票s)

2009年7月19日

CPOL

4分钟阅读

viewsIcon

68607

downloadIcon

3474

本文介绍如何获取 Windows OS 上运行的驱动程序列表,并使用 Win32 调用将其显示在 ListView 中。

引言

本文档介绍了以下内容

  • 如何获取 Windows 计算机上运行的驱动程序列表
  • 如何使用纯 Win32 调用将驱动程序列表显示在 ListView 中

注意:本文档包含一篇早期文章的最新代码,并更新了本文中介绍的功能。请单击此处访问我的早期文章。

如何获取计算机上的驱动程序列表

驱动程序实际上是在操作系统上运行的服务。要获取服务列表,首先打开 SCM(服务控制管理器),然后调用 EnumServicesStatus() 枚举服务。

如果需要比 EnumServicesStatus() 返回的信息更多的信息(本例中就是如此),那么必须使用 OpenService() 打开服务,并使用 QueryServiceConfig() 查询一些必需的详细信息。这些调用在 CDriverInfoData::Initialize() 函数中完成。EnumServicesStatus() 将获取以下信息

  • 驱动程序名称
  • 驱动程序描述
  • 驱动程序的当前状态
  • 系统中安装的驱动程序数量
  • 驱动程序类型

以下是枚举服务的代码片段

ENUM_SERVICE_STATUS struct_ServiceStatus;
ENUM_SERVICE_STATUS *lpServiceStatus;
BOOL b_RetVal = FALSE;
DWORD dw_BytesNeeded;
DWORD dw_ServiceCount;
DWORD dw_ResumeHandle = 0;
DWORD dw_ServiceType;
DWORD dw_ServiceState;
//We are interested only in drivers
dw_ServiceType = SERVICE_DRIVER;
// interested to know about drivers in all states
dw_ServiceState = SERVICE_STATE_ALL;
//Call EnumServicesStatus using the handle returned by OpenSCManager
b_RetVal = ::EnumServicesStatus(h_SCM,
                                dw_ServiceType,
                                dw_ServiceState,
                                &struct_ServiceStatus,
                                sizeof(struct_ServiceStatus),
                                &dw_BytesNeeded,
                                &dw_ServiceCount,
                                &dw_ResumeHandle);
DWORD dw_Error = GetLastError();
// Verify if EnumServicesStatus needs more memory space
if ((b_RetVal == FALSE) || dw_Error == ERROR_MORE_DATA) 
{
     DWORD dw_Bytes = dw_BytesNeeded + sizeof(ENUM_SERVICE_STATUS);
     lpServiceStatus = new ENUM_SERVICE_STATUS [dw_Bytes];
     EnumServicesStatus(h_SCM,
                    dw_ServiceType,
                    dw_ServiceState,
                    lpServiceStatus,
                    dw_Bytes,
                    &dw_BytesNeeded,
                    &dw_ServiceCount,
                    &dw_ResumeHandle);
}

尽管使用了 EnumServicesStatus(),但驱动程序的启动类型和驱动程序路径仍然缺失。为此,必须使用 OpenService() 打开服务,并使用 QueryServiceConfig() 查询有关服务的信息。使用 QueryServiceConfig(),将获得以下附加信息

  • 驱动程序的启动类型
  • 驱动程序文件的路径

以下代码片段显示了打开服务和查询服务的代码

h_ServiceHandle = NULL;
h_ServiceHandle = OpenService(h_SCM, 
  lpServiceStatus[i].lpServiceName, SERVICE_ALL_ACCESS);
if(NULL != h_ServiceHandle)
{
     LPQUERY_SERVICE_CONFIG lpqscBuf; 
     DWORD dwBytesNeeded; 
     BOOL bSuccess=TRUE;
     // Allocate a buffer for the configuration information.
     lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096); 
     if (lpqscBuf == NULL) 
     {
         return FALSE;
     }
     // Get the configuration information. 
     if (!QueryServiceConfig(h_ServiceHandle, 
                         lpqscBuf, 
                         4096, // buffer size 
                         &dwBytesNeeded)) 
     {
         bSuccess = FALSE; 
     }

驱动程序信息子窗口中每个字段的含义

  • 序号。:驱动程序列表的序号,仅供有兴趣的用户查看驱动程序计数。
  • 驱动程序名称:这是驱动程序名称,通常与驱动程序文件名相同。
  • 描述:驱动程序的描述。
  • 驱动程序类型:显示驱动程序的类型,无论是内核驱动程序还是文件系统驱动程序
  • 启动类型:显示启动的类型。有四种启动模式加上一种禁用状态。它们是
    • 自动启动 - 在系统启动期间由 SCM 自动启动。
    • 引导启动 - 在驱动程序的情况下由系统加载程序启动。
    • 按需启动 - 通过调用 StartService() 按需启动。
    • 系统启动 - 在驱动程序的情况下由 IoInitSystem() 调用启动。
    • 禁用 - 此状态表示驱动程序已被禁用。
  • 状态:显示驱动程序的当前状态。驱动程序可能处于以下状态之一:未运行、正在启动、正在运行、暂停待定、已暂停、继续待定、正在停止。
  • 映像路径:驱动程序文件的路径。

如何创建、添加标题以及向列表视图添加元素

列表视图在 CDriverInfoView 类中的 CDriverInfoView::Draw() 函数中创建。创建列表视图的调用如下所示。需要 LVS_REPORT 样式才能获取列标题。

列表视图通过以下调用创建。应将 WC_LISTVIEW 用作类名。这在 bool CDriverInfoView::Draw() 函数中完成。下面显示了执行此操作的代码片段

RECT rect;
GetClientRect(m_hwndParent, &rect);
m_hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE,
                                WC_LISTVIEW,
                                "", //caption not required
                                WS_CHILD | WS_VISIBLE | LVS_REPORT,
                                0,  
                                0, 
                                rect.right, // width
                                rect.bottom,// height
                                m_hwndParent,
                                NULL,
                               (HINSTANCE) GetWindowLong (m_hwndParent, GWL_HINSTANCE),
                                NULL);
// Set the single row select style
SendMessage(m_hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 
                  0, LVS_EX_FULLROWSELECT); // Set style

通过调用 SendMessage() 并使用 LVM_INSERTCOLUMN 来添加列标题。wParam 代表列号。这在 bool CDriverInfoView::FillData(CDriverInfoData &pDriverInfoData) 函数中完成,如下所示

LVCOLUMN ListviewCol;
memset((void*)&ListviewCol, 0, sizeof(ListviewCol));
    
ListviewCol.mask = LVCF_TEXT | LVCF_WIDTH; // Mask
ListviewCol.cx = 0x40;                       // column width
ListviewCol.pszText = "Serial No";                         
SendMessage(m_hwndListView,LVM_INSERTCOLUMN,0,(LPARAM)&ListviewCol);
ListviewCol.pszText = "Driver Name";                         
SendMessage(m_hwndListView,LVM_INSERTCOLUMN,1,(LPARAM)&ListviewCol);

插入行是通过首先插入具有值的行的第一个单元格,然后为后续单元格设置值来完成的。使用 SendMessageLVM_INSERTITEMLVM_SETITEM 消息来实现此目的。下面显示了执行相同操作的代码片段

DRIVERINFO_STRUCT structDriverInfo;
memset((void*)&structDriverInfo, 0, sizeof(structDriverInfo));
pDriverInfoData.Get_FirstDriverInfo(&structDriverInfo);
int i_Col = 0;
TCHAR szSlNo[12];
do
{
     // Fill Serial number
     // main item number, this can be considered as row number 
     ListviewItem.iItem = i_Col;
     // Put in the first column
     ListviewItem.iSubItem = DRIVERINFO_SERIALNO_COL;
     memset((void*)szSlNo, 0, sizeof(TCHAR[12]));
     sprintf(szSlNo, "%d", i_Col + 1);
     // Text to display (can be from a char variable) (Items)
     ListviewItem.pszText = TEXT(szSlNo);
     SendMessage(m_hwndListView,LVM_INSERTITEM,0,(LPARAM)&ListviewItem);
     // Send to the Listview
  
     // Fill Driver Name column
     // main item number, this can be considered as row number 
     ListviewItem.iItem = i_Col;
     ListviewItem.iSubItem = DRIVERINFO_NAME_COL;  // Put in second coluom
     // Text to display (can be from a char variable) (Items)
     ListviewItem.pszText = TEXT(structDriverInfo.DriverName);
     SendMessage(m_hwndListView,LVM_SETITEM,0,(LPARAM)&ListviewItem);
     // Send to the Listview

创建驱动程序信息 MDI 子窗口

显示驱动程序信息的 MDI 子窗口是在主框架窗口过程 SigmaFrameWndProc 中收到 ID_INFORMATION_DRIVERINFORMATION 命令消息时创建的。在此,在堆上创建了一个 CDriverInfo 对象。然后使用此 g_pDriverInfo 对象来创建窗口并填充列表控件。

CDriverInfo::CreateDriverInfoWindow() 注册子窗口并创建 MDI 子窗口。代码片段如下所示

MDICREATESTRUCT    MDIChildCreateStruct;
MDIChildCreateStruct.szClass = TEXT("SigmaDriverInfoWnd");
MDIChildCreateStruct.szTitle = TEXT("Driver Information");
MDIChildCreateStruct.hOwner  = ghInstance;
MDIChildCreateStruct.x   = CW_USEDEFAULT;
MDIChildCreateStruct.y   = CW_USEDEFAULT;
MDIChildCreateStruct.cx   = CW_USEDEFAULT;
MDIChildCreateStruct.cy   = CW_USEDEFAULT;
MDIChildCreateStruct.style  = 0;
MDIChildCreateStruct.lParam  = 0;
 
m_hwndDriverInformation = (HWND) SendMessage(ghMDIClientArea,
                                          WM_MDICREATE, 
                                          0,
                              (LPARAM) (LPMDICREATESTRUCT) &MDIChildCreateStruct) ;

// return if its not possible to create the child window
if(NULL == m_hwndDriverInformation)
{
    return 0;
}

MDICREATESTRUCT 用于设置子窗口参数。这作为 SendMessage 的最后一个参数传递。SendMessage 用于向主框架窗口的客户端区域发送 WM_MDICREATE 消息。

调用 InitCommonControlsEx() 来初始化通用控件。这是创建列表控件所必需的。

类及其功能

  • CDriverInfo - 此类通过 CDriverInfoDataCDriverInfoView 类分别表示数据和视图的成员元素。在此类中创建子窗口。
  • CDriverInfoData - 此类存储应在列表视图中显示的数据。
  • CDriverInfoView - 此类用于初始化列表视图,并将 CDriverInfoData 中可用的数据填充到列表视图中。

环境

本文档仅在以下环境开发和测试:联想 T61 笔记本电脑、VC++ 6.0、UNICODE、C++ 和 XP SP3。

© . All rights reserved.