Kernel Mode Driver Framework >> Pascal (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.pas
unit main; interface uses DDDK; const DEV_NAME = '\Device\MyDriver'; SYM_NAME = '\DosDevices\MyDriver'; IOCTL_SET = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_ANY_ACCESS shl 14) or ($800 shl 2) or (METHOD_IN_DIRECT); IOCTL_GET = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_ANY_ACCESS shl 14) or ($801 shl 2) or (METHOD_OUT_DIRECT); function __DriverEntry(pOurDriver:PDRIVER_OBJECT; pOurRegistry:PUNICODE_STRING):NTSTATUS; stdcall; implementation var szBuffer: array[0..255] of char; procedure IrpFileCreate(Device:WDFDEVICE; Request:WDFREQUEST; FileObject:WDFFILEOBJECT); stdcall; begin DbgPrint('IrpFileCreate', []); WdfRequestComplete(Request, STATUS_SUCCESS); end; procedure IrpFileClose(FileObject:WDFFILEOBJECT); stdcall; begin DbgPrint('IrpFileClose', []); end; procedure IrpIOCTL(Queue:WDFQUEUE; Request:WDFREQUEST; OutputBufferLength:ULONG; InputBufferLength:ULONG; IoControlCode:ULONG); stdcall; var mdl: PMDL; len: ULONG; buf: Pointer; begin if IoControlCode = IOCTL_SET then begin DbgPrint('IOCTL_SET', []); WdfRequestRetrieveInputWdmMdl(Request, @mdl); buf:= MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); memcpy(@szBuffer, buf, InputBufferLength); DbgPrint('Buffer: %s, Length:%d', [@szBuffer, InputBufferLength]); WdfRequestSetInformation(Request, InputBufferLength); end else if IoControlCode = IOCTL_GET then begin DbgPrint('IOCTL_GET', []); WdfRequestRetrieveOutputWdmMdl(Request, @mdl); buf:= MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); memcpy(buf, @szBuffer, OutputBufferLength); len:= strlen(@szBuffer) + 1; WdfRequestSetInformation(Request, len); end; WdfRequestComplete(Request, STATUS_SUCCESS); end; function AddDevice(pOurDriver:WDFDRIVER; pDeviceInit:PWDFDEVICE_INIT):NTSTATUS; stdcall; var device: WDFDEVICE; suDevName: UNICODE_STRING; szSymName: UNICODE_STRING; file_cfg: WDF_FILEOBJECT_CONFIG; ioqueue_cfg: WDF_IO_QUEUE_CONFIG; begin WdfDeviceInitSetIoType(pDeviceInit, WdfDeviceIoBuffered); WDF_FILEOBJECT_CONFIG_INIT(@file_cfg, @IrpFileCreate, @IrpFileClose, Nil); WdfDeviceInitSetFileObjectConfig(pDeviceInit, @file_cfg, WDF_NO_OBJECT_ATTRIBUTES); RtlInitUnicodeString(@suDevName, DEV_NAME); RtlInitUnicodeString(@szSymName, SYM_NAME); WdfDeviceInitAssignName(pDeviceInit, @suDevName); WdfDeviceCreate(@pDeviceInit, WDF_NO_OBJECT_ATTRIBUTES, @device); WdfDeviceCreateSymbolicLink(device, @szSymName); WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(@ioqueue_cfg, WdfIoQueueDispatchSequential); ioqueue_cfg.EvtIoDeviceControl:= @IrpIOCTL; Result:= WdfIoQueueCreate(device, @ioqueue_cfg, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE); end; function __DriverEntry(pOurDriver:PDRIVER_OBJECT; pOurRegistry:PUNICODE_STRING):NTSTATUS; stdcall; var config: WDF_DRIVER_CONFIG; begin WDF_DRIVER_CONFIG_INIT(@config, AddDevice); WdfDriverCreate(pOurDriver, pOurRegistry, WDF_NO_OBJECT_ATTRIBUTES, @config, WDF_NO_HANDLE); Result:= STATUS_SUCCESS; end; end.
IrpIOCTL()收到IOCTL_SET時,Driver複製User Buffer的內容到szBuffer,而收到IOCTL_GET時,將szBuffer內容又複製回User Buffer,完成暫存的功能,IoStatus.Information的數值就是OutBufferSize回傳的長度。
app.pas
program main; {$APPTYPE CONSOLE} uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, DIALOGS; const METHOD_BUFFERED = 0; METHOD_IN_DIRECT = 1; METHOD_OUT_DIRECT = 2; FILE_ANY_ACCESS = 0; FILE_DEVICE_UNKNOWN = $22; IOCTL_SET = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_ANY_ACCESS shl 14) or ($800 shl 2) or (METHOD_IN_DIRECT); IOCTL_GET = (FILE_DEVICE_UNKNOWN shl 16) or (FILE_ANY_ACCESS shl 14) or ($801 shl 2) or (METHOD_OUT_DIRECT); var fd: DWORD; ret: DWORD; len: DWORD; buf: array[0..255] of char; begin fd:= CreateFile('\\.\MyDriver', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (fd <> INVALID_HANDLE_VALUE) then begin StrCopy(buf, 'I am error'); len:= strlen(buf)+1; DeviceIoControl(fd, IOCTL_SET, @buf, len, Nil, 0, ret, Nil); WriteLn(Output, Format('SET: %s, %d', [buf, len])); FillChar(buf, sizeof(buf), #0); DeviceIoControl(fd, IOCTL_GET, Nil, 0, @buf, len, ret, Nil); WriteLn(Output, Format('GET: %s, %d', [buf, ret])); CloseHandle(fd); end else begin WriteLn(Output, 'failed to open mydriver'); end; end.
結果