關於Non-PnP Driver Sample官方有一份詳細的README可供參考,這裡則是以Non-PnP Driver Sample來大致說明驅動程式的架構。由WDF所撰寫的驅動程式會包含一個函式進入點DriverEntry
和一些對應特定事件發生時所呼叫的callback函式。後續會針對較重要及較貼近這次主題的部分作介紹,下方為Non-PnP Driver Sample的DriverEntry
:
NTSTATUS
DriverEntry(
IN OUT PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called by the Operating System to initialize the driver.
It creates the device object, fills in the dispatch entry points and
completes the initialization.
Arguments:
DriverObject - a pointer to the object that represents this device
driver.
RegistryPath - a pointer to our Services key in the registry.
Return Value:
STATUS_SUCCESS if initialized; an error otherwise.
--*/
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDFDRIVER hDriver;
PWDFDEVICE_INIT pInit = NULL;
WDF_OBJECT_ATTRIBUTES attributes;
KdPrint(("Driver Frameworks NONPNP Legacy Driver Example\n"));
WDF_DRIVER_CONFIG_INIT(
&config,
WDF_NO_EVENT_CALLBACK // This is a non-pnp driver.
);
//
// Tell the framework that this is non-pnp driver so that it doesn't
// set the default AddDevice routine.
//
config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
//
// NonPnp driver must explicitly register an unload routine for
// the driver to be unloaded.
//
config.EvtDriverUnload = NonPnpEvtDriverUnload;
//
// Register a cleanup callback so that we can call WPP_CLEANUP when
// the framework driver object is deleted during driver unload.
//
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = NonPnpEvtDriverContextCleanup;
//
// Create a framework driver object to represent our driver.
//
status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
&hDriver);
if (!NT_SUCCESS(status)) {
KdPrint (("NonPnp: WdfDriverCreate failed with status 0x%x\n", status));
return status;
}
//
// Since we are calling WPP_CLEANUP in the DriverContextCleanup
// callback we should initialize WPP Tracing after WDFDRIVER
// object is created to ensure that we cleanup WPP properly
// if we return failure status from DriverEntry. This
// eliminates the need to call WPP_CLEANUP in every path
// of DriverEntry.
//
WPP_INIT_TRACING( DriverObject, RegistryPath );
//
// On Win2K system, you will experience some delay in getting trace events
// due to the way the ETW is activated to accept trace messages.
//
KdPrint(("NonPnp: DriverEntry: tracing enabled\n"));
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
"Driver Frameworks NONPNP Legacy Driver Example");
//
//
// In order to create a control device, we first need to allocate a
// WDFDEVICE_INIT structure and set all properties.
//
pInit = WdfControlDeviceInitAllocate(
hDriver,
&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R
);
if (pInit == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}
//
// Call NonPnpDeviceAdd to create a deviceobject to represent our
// software device.
//
status = NonPnpDeviceAdd(hDriver, pInit);
return status;
}
由於這是個Non-Pnp的驅動程式,需要設定WdfDriverInitNonPnpDriver
。
config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
在DriverEntry
中必須呼叫WdfDriverCreate
建立驅動程式物件,讓驅動程式可以使用其他的WDF架構函式。
status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
&hDriver);
若有使用WPP軟體追蹤,需要使用WPP_INIT_TRACING
來做初始化。
WPP_INIT_TRACING( DriverObject, RegistryPath );
而為了讓應用程式可以使用這個驅動程式,要先設定安全性描述項定義語言 (SDDL)。
pInit = WdfControlDeviceInitAllocate(
hDriver,
&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R
);
在Non-Pnp驅動程式的DriverEntry
必須先呼叫EvtDeviceAdd
,因為Non-PnP驅動程式沒有對應的實體裝置會被偵測,所以必須在驅動程式進入點就建立裝置。
status = NonPnpDeviceAdd(hDriver, pInit);
在DriverEntry
呼叫EvtDeviceAdd
後,就繼續進行建立裝置的步驟,下方為NonPnpDeviceAdd
的實作:
NTSTATUS
NonPnpDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
Called by the DriverEntry to create a control-device. This call is
responsible for freeing the memory for DeviceInit.
Arguments:
DriverObject - a pointer to the object that represents this device
driver.
DeviceInit - Pointer to a driver-allocated WDFDEVICE_INIT structure.
Return Value:
STATUS_SUCCESS if initialized; an error otherwise.
--*/
{
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
WDF_FILEOBJECT_CONFIG fileConfig;
WDFQUEUE queue;
WDFDEVICE controlDevice;
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ;
DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ;
UNREFERENCED_PARAMETER( Driver );
PAGED_CODE();
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT,
"NonPnpDeviceAdd DeviceInit %p\n", DeviceInit);
//
// Set exclusive to TRUE so that no more than one app can talk to the
// control device at any time.
//
WdfDeviceInitSetExclusive(DeviceInit, TRUE);
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceInitAssignName failed %!STATUS!", status);
goto End;
}
WdfControlDeviceInitSetShutdownNotification(DeviceInit,
NonPnpShutdown,
WdfDeviceShutdown);
//
// Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
// framework whether you are interested in handling Create, Close and
// Cleanup requests that gets generated when an application or another
// kernel component opens an handle to the device. If you don't register
// the framework default behaviour would be to complete these requests
// with STATUS_SUCCESS. A driver might be interested in registering these
// events if it wants to do security validation and also wants to maintain
// per handle (fileobject) context.
//
WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
NonPnpEvtDeviceFileCreate,
NonPnpEvtFileClose,
WDF_NO_EVENT_CALLBACK // not interested in Cleanup
);
WdfDeviceInitSetFileObjectConfig(DeviceInit,
&fileConfig,
WDF_NO_OBJECT_ATTRIBUTES);
//
// In order to support METHOD_NEITHER Device controls, or
// NEITHER device I/O type, we need to register for the
// EvtDeviceIoInProcessContext callback so that we can handle the request
// in the calling threads context.
//
WdfDeviceInitSetIoInCallerContextCallback(DeviceInit,
NonPnpEvtDeviceIoInCallerContext);
//
// Specify the size of device context
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
CONTROL_DEVICE_EXTENSION);
status = WdfDeviceCreate(&DeviceInit,
&attributes,
&controlDevice);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreate failed %!STATUS!", status);
goto End;
}
//
// Create a symbolic link for the control object so that usermode can open
// the device.
//
status = WdfDeviceCreateSymbolicLink(controlDevice,
&symbolicLinkName);
if (!NT_SUCCESS(status)) {
//
// Control device will be deleted automatically by the framework.
//
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreateSymbolicLink failed %!STATUS!", status);
goto End;
}
//
// Configure a default queue so that requests that are not
// configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
// other queues get dispatched here.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchSequential);
ioQueueConfig.EvtIoRead = FileEvtIoRead;
ioQueueConfig.EvtIoWrite = FileEvtIoWrite;
ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
//
// Since we are using Zw function set execution level to passive so that
// framework ensures that our Io callbacks called at only passive-level
// even if the request came in at DISPATCH_LEVEL from another driver.
//
//attributes.ExecutionLevel = WdfExecutionLevelPassive;
//
// By default, Static Driver Verifier (SDV) displays a warning if it
// doesn't find the EvtIoStop callback on a power-managed queue.
// The 'assume' below causes SDV to suppress this warning. If the driver
// has not explicitly set PowerManaged to WdfFalse, the framework creates
// power-managed queues when the device is not a filter driver. Normally
// the EvtIoStop is required for power-managed queues, but for this driver
// it is not needed b/c the driver doesn't hold on to the requests or
// forward them to other drivers. This driver completes the requests
// directly in the queue's handlers. If the EvtIoStop callback is not
// implemented, the framework waits for all driver-owned requests to be
// done before moving in the Dx/sleep states or before removing the
// device, which is the correct behavior for this type of driver.
// If the requests were taking an indeterminate amount of time to complete,
// or if the driver forwarded the requests to a lower driver/another stack,
// the queue should have an EvtIoStop/EvtIoResume.
//
__analysis_assume(ioQueueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate(controlDevice,
&ioQueueConfig,
&attributes,
&queue // pointer to default queue
);
__analysis_assume(ioQueueConfig.EvtIoStop == 0);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed %!STATUS!", status);
goto End;
}
//
// Control devices must notify WDF when they are done initializing. I/O is
// rejected until this call is made.
//
WdfControlFinishInitializing(controlDevice);
End:
//
// If the device is created successfully, framework would clear the
// DeviceInit value. Otherwise device create must have failed so we
// should free the memory ourself.
//
if (DeviceInit != NULL) {
WdfDeviceInitFree(DeviceInit);
}
return status;
}
為了相容於部分舊的應用程式需要特定裝置名稱,這裡指派裝置名稱給裝置物件。
status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
使用WDF_FILEOBJECT_CONFIG_INIT
初始化檔案物件設定,依序為EvtDeviceFileCreate
、EvtFileClose
及EvtFileCleanup
。
WDF_FILEOBJECT_CONFIG_INIT(
&fileConfig,
NonPnpEvtDeviceFileCreate,
NonPnpEvtFileClose,
WDF_NO_EVENT_CALLBACK // not interested in Cleanup
);
WdfDeviceInitSetFileObjectConfig(DeviceInit,
&fileConfig,
WDF_NO_OBJECT_ATTRIBUTES);
根據先前的設定建立裝置物件。
status = WdfDeviceCreate(&DeviceInit,
&attributes,
&controlDevice);
建立指定裝置的symbolic link,讓應用程式可以透過symbolic link存取裝置。
status = WdfDeviceCreateSymbolicLink(controlDevice,
&symbolicLinkName);
這裡分別設定了EvtIoRead
、EvtIoWrite
、EvtIoDeviceControl
的callback函式,當裝置有讀取、寫入或其他I/O控制要求時,便會呼叫所設定的callback函式。
ioQueueConfig.EvtIoRead = FileEvtIoRead;
ioQueueConfig.EvtIoWrite = FileEvtIoWrite;
ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl;
建立裝置的 I/O 佇列並註冊先前的callback函式。
status = WdfIoQueueCreate(controlDevice,
&ioQueueConfig,
&attributes,
&queue // pointer to default queue
);
以上就是對於DriverEntry
和EvtDeviceAdd
大致上的介紹。
Non-PnP Driver Sample
適用于 WDF 驅動程式的 DriverEntry 常式 - Windows drivers | Microsoft Learn
控制 KMDF 驅動程式中的裝置存取 - Windows drivers | Microsoft Learn