Tutorial 2 || Einführung in die Gerätetreiberprogrammierung unter Windows von Niklas Bartz
Es geht um die Kommunikation eines Treibers mit einem User-Programm.
Willkomen zum zweiten Treiber Tutorial. Diesmal werden wir die Kommunikation zwischen einem Treiber und einem Programm im User-Mode behandeln.
Um mit einem anderen Programm zu kommunizieren muss ein Treiber I/O Request Packets (IRP) benutzen. Dies ist eine einfach Datenstruktur, welche Puffer mit Daten enthält. Das ganze funktioniert, wie sollte es auch anders sein unter Windows, mit Datei-Handles. Wenn also nun ein User-Programm ein String in das Datei-Handle des Treibers schreibt, dann wird vom Kernel ein IRP erzeugt, welches diesen String enthält. Um nun IRPs verarbeiten zu können muss unser Kerneltreiber eine Funktion zur verarbeitung bereitstellen. Dies funktioniert genau so wie mit der Endladeroutine. Es wird einfach der zugehörige Funktionszeiger gesetzt.
Alle Hauptfunktionen sind in einem Array namens MajorFunction gespeichert. Es sind Werte definiert wodurch man die Funktionen setzten kann.
Code c:
1 2 3 4 5 | theDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateProcessing;
theDriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseProcessing;
theDriverObject->MajorFunction[IRP_MJ_READ] = ReadProcessing;
theDriverObject->MajorFunction[IRP_MJ_WRITE] = WriteProcessing;
theDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControl; |
Die einzelnen Funktionen werden durch folgenden Methoden der Windows-API aufgerufen:
Code c:
1 2 3 4 5 | CreateFile()
CloseHandle()
ReadFile()
WriteFile()
DeviceIoControl() |
Diese Funktionen könnte man nun alle ausformulieren
Code c:
1 2 3 4 5 6 7 8 | NTSTATUS CreateProcessing( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
// Mache irgendetwas
//...
return STATUS_SUCCESS;
}
// u.s.w.
//... |
Abbildung 1: Ein Kerneltreiber kann für jede Hauptfunktion eine eigene Callbackfuntkion definieren
Man sollte sich im klaren seien, dass man ohne ein Request von dem User-Mode-Programm nichts vom Treiber senden kann oder habt ihr schonmal eine Nachricht von einer Datei empfangen?
Also jede Kommunikation erfolgt vom User-Programm aus. Wenn man nun dafür sorgen möchte, dass der Treiber einem immer etwas senden kann, dann muss man immer genügend Request bereitstellen, bevor diese verbraucht sind.
Man sendet also ein Request an den Treiber welches dieses erst verwendet, wenn er Daten zu übermitteln hat. Dann reagiert er auf das Request und sendet etwas an das Programm zurück.
Um nun von einem Programm im User-Mode aus einen Treiber zu verwenden, muss dieses Programm ein Handle zum Treiber öffen. Dies geht jedoch nur, wenn der Treiber zuvor ein Gerät registriert hat. Danach kann man mit dem User-Programm das Gerät öffnen und darauf zugreifen, als sei es eine Datei. So ähnlich ist es in der Regel auch bei UNIX-Systemen.
So wollen wir mal mit unserem Kerneltreiber ein Gerät registrieren:
Code c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #define FILE_DEVICE_TESTDEVICE 0x00008001
const WCHAR deviceNameBuffer[] = L"\\Device\\vicesys";
const WCHAR symbolicLinkNameBuffer[] = L"\\DosDevices\\vicesys";
PDEVICE_OBJECT g_testDevice; // global pointer on the driverdevice
NTSTATUS DriverEntry (IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING theRegistryPath) {
NTSTATUS ntStatus;
UNICODE_STRING deviceNameUni;
UNICODE_STRING symbolicLinkNameUni;
// name and symbolic link init.
RtlInitUnicodeString( &deviceNameUni,
deviceNameBuffer );
RtlInitUnicodeString( &symbolicLinkNameUni,
symbolicLinkNameBuffer );
// device init
ntStatus = IoCreateDevice ( DriverObject,
0, // device extension
&deviceNameUni,
FILE_DEVICE_TESTDEVICE,
0,
TRUE,
&g_TestDevice );
if(NT_SUCCESS(ntStatus)) {
DbgPrint("IoDevice built");
ntStatus = IoCreateSymbolicLink( &symbolicLinkNameUni,
&deviceNameUni );
if (NT_SUCCESS(ntStatus)) {
DbgPrint("Symbolic Link created");
} else {
DbgPrint("Symbolic link creation failed");
}
} else {
DbgPrint("Cant't create a IoDevice");
}
/*
...
*/ |
Wem es entfallen ist: Durch das Präfix L wird ein String als Unicode definiert.
Durch IoCreateDevice() wird ein Gerät registriert und von da an kann man im User-Programm auf den Treiber zugreifen. Da wir jedoch ein Stück kompfortabler auf den Treiber zugreifen wollen erstellen wir noch einen Symbolische-Link.
Da wir einen sybolischen Link erstellt haben, kann ein User-Programm ein Handle für die Datei über den String \\.\vicesys öffnen:
Code c:
1 2 3 4 5 6 7 8 9 10 11 12 13 | hDevice = CreateFile("\\\\.\\vicesys",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDevice == ((HANDLE)-1)) {
cout<<"Device Handle konnte nicht erstellt werden"<<endl;
return FALSE;
}
cout<<"Device Handle erstellt"<<endl;
return true; |
Es ist absolut egal ob Sie einen symbolischen Link erstellen oder nicht. Ich finde es "schöner". Falls Sie ohne einen symbolischen Link erstellt zu haben auf einen Treiber zugreifen wollen, dann geht das wie folgt:
Code c:
1 2 3 4 5 6 7 | hDevice = CreateFile("\\\\Device\\vicesys",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL); |
Im folgenden habe ich ein Beispiel erstellt wodurch ein User-Mode-Programm mit einem Treiber über Events kommuniziert.
Dazu folgendes: Wenn man eine Event im Kernel-Mode erstellt, dann kann ma dies im User-Mode nicht modifizieren, man kann nur den Status abfragen. Allerdings möchten wir, dass unsere GUI Anwendung den Status verändern kann.
Da der Treiber mit höherer Priorität als eine GUI Anwendung läuft, können wir vom Treiber aus auf alles in einer GUI Anwendung zugreifen. Somit müssen wir das Event in unserem Programm erstellen und an den Treiber weiterleiten (referenzieren).
Wenn man eine neue IOCTL schreibt, welcher es möglich seien soll mit User-Mode-Software zu kommunizieren, dann muss diese IRP_MJ_DEVICE_CONTROL Requests benutzen (MSDN(27.10.2011)). Von Windows wird das CTL_CODE Makro geliefert, welches in der Wdm.h und Ntddk.h definiert ist (Def. s.unten oder hier MSDN (27.10.2011)).
An dieser Stelle Zeige ich einfach einmal den ganzen Code des Treibers und der GUI Testanwendung. Der Code sollte eigentlich selbst erklärend seien, für jeden der meint er ist weit genug um Treiber zu programmieren.
Das komplette Beispiel ist auch im Anhang vorhanden.
Treiber:
IOCTL.h // wird von dem Treiber und der GUI App verwendet
Code c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #ifndef __IO_CONTROL_H__
#define __IO_CONTROL_H__
const WCHAR deviceNameBuffer[] = L"\\Device\\vicesys";
const WCHAR symbolicLinkNameBuffer[] = L"\\DosDevices\\vicesys";
/* _____________________________________________________________________________ ______________________________________________________________________________________________
. IOCTL's are defined by the following bit layout.
. [ Common | Device Type | Required Access | Custom | Function Code | Transfer Type ]
. 31 30 16 15 14 13 12 2 1 0
.
. #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
.
. Common - 1 bit. This is set for user-defined device types.
. Device Type - This is the type of device the IOCTL belongs to. This can be user defined (Common bit set). This must match the device type of the device object.
. - Values of less than 0x8000 are reserved for Microsoft.
. Required Access - FILE_READ_DATA, FILE_WRITE_DATA, etc. This is the required access for the device.
. Custom - 1 bit. This is set for user-defined IOCTL's. This is used in the same manner as "WM_USER".
. Function Code - This is the function code that the system or the user defined (custom bit set)
. - Values of less than 0x800 are reserved for Microsoft
. Transfer Type - Indicates how the system will pass data between the caller of DeviceIoControl (or IoBuildDeviceIoControlRequest) and the driver that handles the IRP.
. - METHOD_IN_DIRECT, METHOD_OUT_DIRECT, METHOD_NEITHER, METHOD_BUFFERED.
. _____________________________________________________________________________ ______________________________________________________________________________________________*/
#define FILE_DEVICE_TESTDEVICE 0x00008001
#define IO_REFERENCE_EVENT (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IO_DEREFERENCE_EVENT (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IO_SET_EVENT (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IO_CLEAR_EVENT (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IO_QUERY_EVENT_STATE (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
#endif // #ifndef __IO_CONTROL_H__ |
mydriver.h
Code c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #ifndef __MY_DRIVER_H__
#define __MY_DRIVER_H__
/* __________________________________NOTE_______________________________________
. Maintain device state information.
.
. Every driver that has an ISR must provide storage for a pointer to a set
. of kernel-defined interrupt objects, and most device drivers store this
. pointer in a device extension. Each driver determines the size of
. the device extension when it creates a device object, and each driver
. defines the contents and structure of its own device extensions.
. _____________________________________________________________________________ */
typedef struct deviceExtensions // extensions structure
{
HANDLE hDeviceEvent;
} DEVICE_EXT;
#endif // #ifndef __MY_DRIVER_H__ |
mydriver.c
Code c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | // our second devi cedriver
#include "ntddk.h"
#include "mydriver.h"
#include "..\inc\IOCTL.h"
PDEVICE_OBJECT g_testDevice; // global pointer on the driver device
PVOID g_pEventObject = NULL; // global pointer on the event object
DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD OnUnload;
DRIVER_DISPATCH CreateProcessing;
DRIVER_DISPATCH CloseProcessing;
DRIVER_DISPATCH IoControl;
/* _____________________________________________________________________________
. OnDispatchRoutine is called, if we need to complete a IRP.
.
. This minimal DispatchCreateClose routine completes the create IRP without
. boosting the priority of the originator of the IRP (IO_NO_INCREMENT),
. because the originator is assumed to wait for an indeterminate but very
. small interval for the request to complete.
. _____________________________________________________________________________ */
void OnDispatchRoutine(IN PIRP irp) {
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
}
/* _____________________________________________________________________________
. CreateProcessing is called, if the user-mode programm calls create file.
.
. HANDLE WINAPI CreateFile(
. __in LPCTSTR lpFileName,
. __in DWORD dwDesiredAccess,
. __in DWORD dwShareMode,
. __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
. __in DWORD dwCreationDisposition,
. __in DWORD dwFlagsAndAttributes,
. __in_opt HANDLE hTemplateFile
. );
. _____________________________________________________________________________ */
NTSTATUS CreateProcessing (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
OnDispatchRoutine(irp);
DbgPrint("CreateProcessing called\n");
return STATUS_SUCCESS;
}
/* _____________________________________________________________________________
. CloseProcessing is called, if the user-mode programm calls close handle.
.
. BOOL WINAPI CloseHandle(
. __in HANDLE hObject
. );
. _____________________________________________________________________________ */
NTSTATUS CloseProcessing (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
OnDispatchRoutine(irp);
DbgPrint("CloseProcessing called\n");
return STATUS_SUCCESS;
}
/* _____________________________________________________________________________
. IoControl is called, if the user-mode programm calls device io control.
.
. BOOL WINAPI DeviceIoControl(
. __in HANDLE hDevice,
. __in DWORD dwIoControlCode,
. __in_opt LPVOID lpInBuffer,
. __in DWORD nInBufferSize,
. __out_opt LPVOID lpOutBuffer,
. __in DWORD nOutBufferSize,
. __out_opt LPDWORD lpBytesReturned,
. __inout_opt LPOVERLAPPED lpOverlapped
. );
. _____________________________________________________________________________ */
NTSTATUS IoControl (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
PIO_STACK_LOCATION irpSp;
ULONG functionCode;
HANDLE hEvent;
OBJECT_HANDLE_INFORMATION objHandleInfo;
LONG* outBuffer;
NTSTATUS status = STATUS_SUCCESS;
irpSp = IoGetCurrentIrpStackLocation(irp);
functionCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
switch (functionCode) {
case IO_REFERENCE_EVENT:
hEvent = (HANDLE) irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
status = ObReferenceObjectByHandle(
hEvent,
GENERIC_ALL,
NULL,
KernelMode,
&g_pEventObject,
&objHandleInfo);
if(status != STATUS_SUCCESS)
{
DbgPrint("ObReferenceObjectByHandle failed! ERROR: %x\n", status);
break;
}
DbgPrint("ReferenceObject complete\n");
break;
case IO_DEREFERENCE_EVENT:
if(g_pEventObject) {
ObDereferenceObject(g_pEventObject);
}
DbgPrint("DereferenceObject complete\n");
break;
case IO_SET_EVENT:
KeSetEvent(g_pEventObject, 0, FALSE);
DbgPrint("KeSetEvent complete\n");
break;
case IO_CLEAR_EVENT:
KeClearEvent(g_pEventObject);
DbgPrint("KeClearEvent complete\n");
break;
case IO_QUERY_EVENT_STATE:
outBuffer = (LONG*) irp->UserBuffer;
*outBuffer = KeReadStateEvent(g_pEventObject);
DbgPrint("KeReadStateEvent complete\n");
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = sizeof(LONG);
IoCompleteRequest(irp, IO_NO_INCREMENT);
return status;
default:
break;
}
OnDispatchRoutine(irp);
DbgPrint("IoControl called\n");
return STATUS_SUCCESS;
}
/* _____________________________________________________________________________
. OnUnload is called, if the driver is unloading
. free all used memorie
. _____________________________________________________________________________ */
VOID OnUnload (IN PDRIVER_OBJECT driverObject) {
UNICODE_STRING symbolicLinkName;
RtlInitUnicodeString( &symbolicLinkName,
symbolicLinkNameBuffer );
IoDeleteSymbolicLink(&symbolicLinkName);
IoDeleteDevice(g_testDevice);
DbgPrint("Driver unloaded\n");
}
NTSTATUS DriverEntry (IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING theRegistryPath) {
NTSTATUS ntStatus;
UNICODE_STRING deviceNameUni;
UNICODE_STRING symbolicLinkNameUni;
// name and symbolic link init.
RtlInitUnicodeString( &deviceNameUni,
deviceNameBuffer );
RtlInitUnicodeString( &symbolicLinkNameUni,
symbolicLinkNameBuffer );
// device init
ntStatus = IoCreateDevice ( driverObject,
sizeof(DEVICE_EXT), // device extension
&deviceNameUni,
FILE_DEVICE_TESTDEVICE,
0,
TRUE,
&g_testDevice );
if(NT_SUCCESS(ntStatus)) {
DbgPrint("IoDevice built");
ntStatus = IoCreateSymbolicLink( &symbolicLinkNameUni,
&deviceNameUni );
if (NT_SUCCESS(ntStatus)) {
DbgPrint("Symbolic Link created");
} else {
DbgPrint("Symbolic link creation failed");
}
} else {
DbgPrint("Cant't create a IoDevice");
}
// set the pointer for the unload function
// in our driver object
driverObject->DriverUnload = OnUnload;
// IOCTL init.
driverObject->MajorFunction[IRP_MJ_CREATE] = CreateProcessing;
driverObject->MajorFunction[IRP_MJ_CLOSE] = CloseProcessing;
driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControl;
DbgPrint("Driver loaded!\n");
return STATUS_SUCCESS;
} |
Testanwendung
User_Programm.cpp
Code cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #include <stdio.h>
#include <iostream>
#include "IOAccess.h"
using namespace std;
int main(int argc, char* argv[])
{
IOAccess *pIO = NULL;
int eingabe = 99;
cout<<"________ Menu ________\n"<<endl;
cout<<"- press 0 for exit"<<endl;
cout<<"- press 1 for OPEN DEVICE"<<endl;
cout<<"- press 2 for CLOSE DEVICE"<<endl;
cout<<"- press 3 for QUERY EVENT"<<endl;
cout<<"- press 4 for SET EVENT"<<endl;
cout<<"- press 5 for CLEAR EVENT"<<endl;
cout<<"____________________________\n"<<endl;
while(eingabe != 0) {
cin >> eingabe;
switch(eingabe)
{
case 1: cout<<"OPEN DEVICE"<<endl;
if(!pIO) {
pIO = new IOAccess();
}
break;
case 2: cout<<"CLOSE DEVICE"<<endl;
if(pIO) {
delete pIO;
pIO = NULL;
}
break;
case 3: cout<<"QUERY EVENT"<<endl;
if(pIO) {
pIO->queryEvent();
}
break;
case 4: cout<<"SET EVENT"<<endl;
if(pIO) {
pIO->setEvent();
}
break;
case 5: cout<<"CLEAR EVENT"<<endl;
if(pIO) {
pIO->clearEvent();
}
break;
default:
break;
}
}
return 0;
} |
IoAccess.h
Code cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #ifndef __IO_ACCESS_H__
#define __IO_ACCESS_H__
#include <windows.h>
class IOAccess {
private:
HANDLE hDevice;
HANDLE hEvent;
DWORD dwBytesReturned;
public:
IOAccess();
~IOAccess();
bool openDriver();
void closeDriver();
void queryEvent();
void clearEvent();
void setEvent();
};
#endif // #ifndef __IO_ACCESS_H__ |
IoAccess.cpp
Code cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #include "IOAccess.h"
#include <stdio.h>
#include <winioctl.h>
#include "..\inc\IOCTL.h"
IOAccess::IOAccess()
{
hDevice = NULL;
hEvent = NULL;
openDriver();
}
IOAccess::~IOAccess()
{
closeDriver();
}
bool IOAccess::openDriver()
{
hDevice = CreateFile("\\\\.\\vicesys",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hDevice) {
printf("Device Handle konnte nicht erstellt werden\n");
return FALSE;
}
printf("Device Handle erstellt\n");
hEvent = CreateEvent(NULL, false, false, NULL);
//download event object to device driver
DeviceIoControl(hDevice,
IO_REFERENCE_EVENT,
(LPVOID) hEvent,
0,
NULL,
0,
&dwBytesReturned,
NULL);
return TRUE;
}
void IOAccess::closeDriver()
{
if(hDevice) {
if(hEvent) {
DeviceIoControl(hDevice,
IO_DEREFERENCE_EVENT,
NULL,0,
NULL,0,
&dwBytesReturned,
NULL);
CloseHandle(hEvent);
}
CloseHandle(hDevice);
}
printf("Device Handle geschlossen\n");
}
void IOAccess::queryEvent()
{
ULONG dwState;
if(hEvent)
{
DeviceIoControl(hDevice,
IO_QUERY_EVENT_STATE,
NULL,
0,
(LPVOID) &dwState,
sizeof(dwState),
&dwBytesReturned,
NULL);
if(dwState) {
printf("Event signaled state\n");
}
else {
printf("Event not signaled state\n");
}
}
}
void IOAccess::clearEvent()
{
if(hEvent)
{
DeviceIoControl(hDevice,
IO_CLEAR_EVENT,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL);
}
}
void IOAccess::setEvent()
{
if(hEvent)
{
DeviceIoControl(hDevice,
IO_SET_EVENT,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL);
}
} |
Ok was ist noch zu erwähnen. Vielleicht folgendes:
g_pEventObject ist ein PRKEVENT Objekt, damit kann man KeEvent... undKeWaitFor... benutzen.
Code c:
1 2 3 | //User-Mode
WaitForSingleObject(hEvent, INFINITE);
// Nach dieser Funktion ist das Event auf "not signaled" gesetzt. |
Den folgenden Code benutzen, wenn das Objekt Event eingetreten ist:
Code c:
1 2 | // Dirver
KeSetEvent(g_pEventObject, 0, FALSE); |
So das wars auch schon wieder ich hoffe es hat gefallen und bis zum nächsten mal. Es wird schon bald weiter gehen und dort werde ich zeigen wie man mit einem User-Programm seinen eigenen Treiber läd und wieder entläd.
MFG Cyb3r
Artikel bewerten