Introduction
This article explains the following:
- How to get the list of drivers running on a Windows machine
- How to display the list of drivers in a ListView using pure Win32 calls
Note: This article has the latest code for an earlier article, and is updated with the features explained in this article. Please click here to reach my earlier article.
How to get the list of drivers on the machine
Drivers are, in fact, services which run on the OS. To get the list of services, first open the SCM (Service Control Manager) and then call EnumServicesStatus()
to enumerate the services.
If more information is required than the ones returned by EnumServicesStatus()
(which is the case here), then the service will have to be opened using OpenService()
to query some of the required details using QueryServiceConfig()
. These calls are made in the function CDriverInfoData::Initialize()
. EnumServicesStatus()
will get following information:
- Driver name
- Driver description
- Current state of Driver
- Number of Drivers installed in the system
- Driver type
Here is the code snippet to enumerate the services:
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;
dw_ServiceType = SERVICE_DRIVER;
dw_ServiceState = SERVICE_STATE_ALL;
b_RetVal = ::EnumServicesStatus(h_SCM,
dw_ServiceType,
dw_ServiceState,
&struct_ServiceStatus,
sizeof(struct_ServiceStatus),
&dw_BytesNeeded,
&dw_ServiceCount,
&dw_ResumeHandle);
DWORD dw_Error = GetLastError();
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);
}
Despite using EnumServicesStatus()
, the driver startup type and path of the driver is still missing. For this, OpenService()
will have to be used to open the service and query the information regarding the service using QueryServiceConfig()
. Using QueryServiceConfig()
, the following additional information will be obtained:
- Driver startup type
- Path of driver file
The code snippet to open the service and query the services is shown below:
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;
lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
if (lpqscBuf == NULL)
{
return FALSE;
}
if (!QueryServiceConfig(h_ServiceHandle,
lpqscBuf,
4096, &dwBytesNeeded))
{
bSuccess = FALSE;
}
Meaning of each field in the Driver Information child window
- Serial No.: Sequence number of list of drivers, just allows interested users to see the count of drivers.
- Driver Name: This is the driver name and is mostly the same as the driver file name.
- Description: Description of the driver.
- Driver Type: Shows the type of driver, whether itβs a Kernel driver or a File System driver.
- Start Type: Shows the kind of start up. There are four types of startup modes plus one disable state. They are:
- Auto start- Automatically started by SCM during system startup.
- Boot start- Started by the system loader in case of drivers.
- Demand start- Started on demand by calling
StartService()
. - System start- Started by the
IoInitSystem()
call in the case of drivers. - Disabled- This state mentions that the driver is disabled.
- Status: Shows the current status of the driver. The driver could be in one of the following states: Not Running, Starting, Running, Pause is pending, Paused, Continue is pending, Stopping.
- Image Path: Path of the driver file.
How to create, add headers, and add elements to the list view
The listview is created in the CDriverInfoView
class in the function CDriverInfoView::Draw()
. The call to create the list view is shown below. The LVS_REPORT
style is required to get the header for the columns.
The listview is created by the following call. WC_LISTVIEW
should be used as the class name. This is done in the function bool CDriverInfoView::Draw()
. The code snippet to do this is shown below:
RECT rect;
GetClientRect(m_hwndParent, &rect);
m_hwndListView = CreateWindowEx(WS_EX_CLIENTEDGE,
WC_LISTVIEW,
"", WS_CHILD | WS_VISIBLE | LVS_REPORT,
0,
0,
rect.right, rect.bottom, m_hwndParent,
NULL,
(HINSTANCE) GetWindowLong (m_hwndParent, GWL_HINSTANCE),
NULL);
SendMessage(m_hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE,
0, LVS_EX_FULLROWSELECT);
Adding column headers is done by calling SendMessage()
with LVM_INSERTCOLUMN
. wParam
represents the column number. This is done in the function bool CDriverInfoView::FillData(CDriverInfoData &pDriverInfoData)
as shown below:
LVCOLUMN ListviewCol;
memset((void*)&ListviewCol, 0, sizeof(ListviewCol));
ListviewCol.mask = LVCF_TEXT | LVCF_WIDTH; ListviewCol.cx = 0x40; ListviewCol.pszText = "Serial No";
SendMessage(m_hwndListView,LVM_INSERTCOLUMN,0,(LPARAM)&ListviewCol);
ListviewCol.pszText = "Driver Name";
SendMessage(m_hwndListView,LVM_INSERTCOLUMN,1,(LPARAM)&ListviewCol);
Inserting rows is done by first inserting the first cell in a row with the value and then subsequent cells are set with values. SendMessage
with the LVM_INSERTITEM
and LVM_SETITEM
messages are used to achieve this. The code snippet to do the same is shown below:
DRIVERINFO_STRUCT structDriverInfo;
memset((void*)&structDriverInfo, 0, sizeof(structDriverInfo));
pDriverInfoData.Get_FirstDriverInfo(&structDriverInfo);
int i_Col = 0;
TCHAR szSlNo[12];
do
{
ListviewItem.iItem = i_Col;
ListviewItem.iSubItem = DRIVERINFO_SERIALNO_COL;
memset((void*)szSlNo, 0, sizeof(TCHAR[12]));
sprintf(szSlNo, "%d", i_Col + 1);
ListviewItem.pszText = TEXT(szSlNo);
SendMessage(m_hwndListView,LVM_INSERTITEM,0,(LPARAM)&ListviewItem);
ListviewItem.iItem = i_Col;
ListviewItem.iSubItem = DRIVERINFO_NAME_COL; ListviewItem.pszText = TEXT(structDriverInfo.DriverName);
SendMessage(m_hwndListView,LVM_SETITEM,0,(LPARAM)&ListviewItem);
Creation of Driver Information MDI child window
The MDI child window which shows Driver Information is created on getting the command message of ID_INFORMATION_DRIVERINFORMATION
in the main frame window proc of SigmaFrameWndProc
. Here, a CDriverInfo
object is created on the heap. This g_pDriverInfo
object is then used to create the window and fill in the list control.
CDriverInfo::CreateDriverInfoWindow()
registers the child window and creates the MDI child window. The code snippet is shown below:
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) ;
if(NULL == m_hwndDriverInformation)
{
return 0;
}
MDICREATESTRUCT
is used to set the child window parameters. This is passed as the last parameter of SendMessage
. SendMessage
is used to send WM_MDICREATE
messages to the client area of the main frame window.
InitCommonControlsEx()
is called to initialize the common controls. This is required for the list control to be created.
Classes and their functionality
CDriverInfo
β This class holds member elements representing Data and View by means of the CDriverInfoData
and CDriverInfoView
classes, respectively. A child window is created in this class.CDriverInfoData
β This stores the data that should be shown in the listview.CDriverInfoView
β This class is used to initialize the listview and fill in data that is available in CDriverInfoData
into the listview.
Environment
This article had been developed and tested under the following environment only: Lenovo T61 laptop, VC++ 6.0, UNICODE, C++, and XP SP3.