Kernel Mode Driver Framework >> C/C++ (PNP) >> IOCTL
METHOD_IN_DIRECT、METHOD_OUT_DIRECT
參考資訊:
1. Source Code
METHOD_IN_DIRECT、METHOD_OUT_DIRECT的作法就是直接Mapping User Buffer,然後驅動程式使用該Mapped的MDL(Memory Description List)操作,相較於METHOD_BUFFERED,因為不須I/O Manager更新回User Buffer,因此,效率會比較好,而相比File的WdfDeviceIoDirect,IOCTL細分成IN和OUT兩種,這是因為IOCTL有區分Input和Output Buffer的緣故,因此,會有方向性的考量,Microsoft針對這部份的描述,僅說明MDL描述會有讀寫存取方向的區分,但是司徒實際測試,發現DeviceIoControl()的Input和Output Buffer是可以混用的,意思就是Input Buffer可以充當Input或Output Buffer使用,而Output Buffer也可以充當Input或Output Buffer使用,只要驅動程式跟User Application定義好即可,當然,METHOD_IN_DIRECT和METHOD_OUT_DIRECT也是可以混用的,關於這部份的細節,有興趣的使用者可以研讀WRK代碼,不過,深怕未知問題可能發生,建議還是依照Microsoft規定去撰寫驅動程式會比較保險。
Microsoft針對I/O部份的說明:
buffer-descriptions-for-i-o-control-codes
記憶體指標:
Buffer | Length | |
---|---|---|
Input | Step 1. WdfRequestRetrieveInputWdmMdl() Step 2. MmGetSystemAddressForMdlSafe() |
傳入的第四個參數 |
Output | Step 1. WdfRequestRetrieveOutputWdmMdl() Step 2. MmGetSystemAddressForMdlSafe() |
傳入的第三個參數 |
main.c
#include <ntddk.h> #include <wdf.h> #define DEV_NAME L"\\Device\\MyDriver" #define SYM_NAME L"\\DosDevices\\MyDriver" #define IOCTL_SET CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_GET CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) char szBuffer[255]={0}; void IrpFileCreate(WDFDEVICE Device, WDFREQUEST Request, WDFFILEOBJECT FileObject) { DbgPrint("IrpFieCreate"); WdfRequestComplete(Request, STATUS_SUCCESS); } void IrpFileClose(WDFFILEOBJECT FileObject) { DbgPrint("IrpFieClose"); } void IrpIOCTL(WDFQUEUE Queue, WDFREQUEST Request, size_t OutputBufferLength, size_t InputBufferLength, ULONG IoControlCode) { PMDL mdl; ULONG len; UCHAR *buf; WDFMEMORY memory; switch(IoControlCode){ case IOCTL_SET: DbgPrint("IOCTL_SET"); WdfRequestRetrieveInputWdmMdl(Request, &mdl); buf = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); memcpy(szBuffer, buf, InputBufferLength); DbgPrint("Buffer: %s, Length:%d", szBuffer, InputBufferLength); WdfRequestSetInformation(Request, InputBufferLength); break; case IOCTL_GET: DbgPrint("IOCTL_GET"); WdfRequestRetrieveOutputWdmMdl(Request, &mdl); buf = MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); memcpy(buf, szBuffer, OutputBufferLength); len = strlen(szBuffer) + 1; WdfRequestSetInformation(Request, len); break; } WdfRequestComplete(Request, STATUS_SUCCESS); } NTSTATUS AddDevice(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) { WDFDEVICE device; UNICODE_STRING suDevName; UNICODE_STRING szSymName; WDF_FILEOBJECT_CONFIG file_cfg; WDF_IO_QUEUE_CONFIG ioqueue_cfg; RtlInitUnicodeString(&suDevName, DEV_NAME); RtlInitUnicodeString(&szSymName, SYM_NAME); WdfDeviceInitAssignName(pDeviceInit, &suDevName); WdfDeviceInitSetIoType(pDeviceInit, WdfDeviceIoBuffered); WDF_FILEOBJECT_CONFIG_INIT(&file_cfg, IrpFileCreate, IrpFileClose, NULL); WdfDeviceInitSetFileObjectConfig(pDeviceInit, &file_cfg, WDF_NO_OBJECT_ATTRIBUTES); WdfDeviceCreate(&pDeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); WdfDeviceCreateSymbolicLink(device, &szSymName); WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioqueue_cfg, WdfIoQueueDispatchSequential); ioqueue_cfg.EvtIoDeviceControl = IrpIOCTL; return WdfIoQueueCreate(device, &ioqueue_cfg, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE); } NTSTATUS DriverEntry(PDRIVER_OBJECT pOurDriver, PUNICODE_STRING pRegistry) { WDF_DRIVER_CONFIG config; WDF_DRIVER_CONFIG_INIT(&config, AddDevice); return WdfDriverCreate(pOurDriver, pRegistry, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE); }
IrpIOCTL()收到IOCTL_SET時,Driver複製User Buffer的內容到szBuffer,而收到IOCTL_GET時,將szBuffer內容又複製回User Buffer,完成暫存的功能,IoStatus.Information的數值就是OutBufferSize回傳的長度。
app.c
#define INITGUID #include <windows.h> #include <winioctl.h> #include <strsafe.h> #include <setupapi.h> #include <stdio.h> #include <stdlib.h> #define IOCTL_SET CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_GET CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) int __cdecl main(int argc, char* argv[]) { DWORD dwRet = 0; HANDLE hFile = NULL; char szBuffer[255]={"I am error"}; hFile = CreateFile("\\\\.\\MyDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("failed to open mydriver"); return 1; } printf("SET: %s, %d\n", szBuffer, strlen(szBuffer)+1); DeviceIoControl(hFile, IOCTL_SET, szBuffer, strlen(szBuffer)+1, NULL, 0, &dwRet, NULL); memset(szBuffer, 0, sizeof(szBuffer)); DeviceIoControl(hFile, IOCTL_GET, NULL, 0, szBuffer, sizeof(szBuffer), &dwRet, NULL); printf("GET: %s, %d\n", szBuffer, dwRet); CloseHandle(hFile); return 0; }
結果