Click here to Skip to main content
15,442,338 members
Articles / Programming Languages / C
Posted 5 Nov 2004


66 bookmarked

Exchange data between device drivers and user applications

Rate me:
Please Sign up or sign in to vote.
4.61/5 (14 votes)
5 Nov 20045 min read
Describes how to exchange data between a device driver and a user mode application.


Although this issue was raised many-many times, I found no good and concise explanation of the basic techniques one has to use to communicate between a user mode application and a kernel mode driver. Most of the first tries are about sharing events, and use the SetEvent and the WaitForSingleObject functions to implement notification. While it may serve the purpose in certain cases, this technique is slow and not a good way of doing it.

In this article, I'll describe the way my applications and drivers communicate. This is not the only technique you can use, but I found it convenient and considerably easy to implement.

Sample Projects

Please note that the sample application isn't complete, because I cannot simulate a hardware IRQ. Nevertheless, it has all the code you need to understand what's going on. The user mode application is also un-tested, and uses IOCP ports which makes it a bit more complex.


Here are the assumptions I make about the problem:

  1. You have a hardware that generates interrupts.
  2. When an IRQ fires, you want to read some data from the hardware.
  3. You want to pass this data back to an application.

Note that this general description fits to many problems, such as reading a file from the hard-drive, or collecting data from a custom data acquisition hardware.

Implementation of the user mode application

As I said earlier, my assumption is that the user mode application is waiting for the driver to generate some data. Most probably, you'll want to have a thread that has a loop in it, which will read the data from the driver and do something with it. So, the first step obviously is to open the driver.

1. Open the device

I won't go into too much details here. You have to use the CreateFile function to open the device. Pass in the OPEN_EXISTING flag for the dwCreationDisposition, and the FILE_FLAG_OVERLAPPED for the dwFlagsAndAttributes. The latter one is not necessary, but since your driver is most probably 100% asynchron-ready (is it?), you'll communicate asynchronously with it, anyway. One more note on opening the device: you must call the IoCreateSymbolicLink function in your driver when you create the device object in order to be able to open the device with the CreateFile function.

2. Read that data!

Once you have the device open, you can use the ReadFile function to read data from the device driver. Since we opened the device with the overlapped flag, you have to use an OVERLAPPED structure here. When you call ReadFile, it will immediately return with 0, and GetLastError will return ERROR_IO_PENDING. Now, call one of the wait functions (i.e., WaitForSingleObject, GetQueuedCompletionStatus, etc.) to wait for the data to arrive.

When the wait function returns (with success), the buffer you passed in to ReadFile contains the data you were so keen on getting!

Implementation of the kernel mode driver

Since we want to read data from the driver, you'll need a dispatch read function. You can specify it in your DriverEntry routine by setting the DriverObject->MajorFunction[IRP_MJ_READ] field. This function has the following signature:


The next step is to set up some sort of queue that will store the read requests. For this, I use the LIST_ENTRY structure and initialize it with the InitializeListHead function. You can do it in the DriverEntry routine. Don't forget that this list must be in non-pageable memory! The best thing is to put it in your device extension structure. As you probably know, queues are a real pain when you store IRPs in it and want to be sure that the cancellation of these IRPs are correctly done. God bless Microsoft, for it provided (in newer DDKs) the CSQ routines! They will do most of the hard work. (See DDK for a complete sample.)

So, the next step is to initialize our queue 'cancel-safely'. This is done by calling the IoCsqInitialize function.

After this, we implement the dispatch read function so that it puts the incoming IRP into our queue (just for clarity: 1 ReadFile in user mode = 1 IRP in the dispatch read function). This is done by calling the IoCsqInsertIrp function. Now that we have the IRP queued, we simply return STATUS_PENDING, telling the IO system that the operation is registered and will be served. Note that when we return from the dispatch routine, the ReadFile function in the user mode code will return also, and - as expected - GetLastError returns ERROR_STATUS_PENDING.

Now, when an IRQ arrives, we have to read the data from the device. Do it like this:

  1. Disable the IRQ in the hardware, so we can safely access the hardware memory or ports.
  2. Queue the DPC routine by calling KeInsertQueueDpc. It is of utmost importance that you don't do anything in your IRQ routine except for these two steps! (Of course, you can do whatever you want, but then your Windows will be deadly slow.)
  3. In your DPC routine, call the IoCsqRemoveNextIrp function to de-queue an IRP that the dispatch read function queued. If it returns NULL, the queue is empty.
  4. Get access to the user buffer by doing something like this (Irp is the IRP we've just de-queued):
    PUCHAR UserBuffer = (PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress);
  5. Read data from your hardware and fill the UserBuffer.
  6. Make sure that the buffers are all flushed:
    KeFlushIoBuffers(Irp->MdlAddress, TRUE, FALSE);
  7. Call the IoCompleteRequest and pass in Irp to complete it.
  8. Re-enable the IRQ in the hardware.

When step 7 completes, the wait function in your user mode application returns and the buffer is full of data!


As you can see, it's not so difficult to implement data exchange once you know the techniques. In a real application, there are additional steps you have to make, such as checking the user buffer's size and accessibility. Also, it is usually unnecessary to complete an IRP each time you have an IRQ. You could as well just fill the buffer until it's full, and complete it when there is no space left in the buffer. The size of the buffer is in the current stack location of the IRP:

PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);

ULONG BufferSize = IrpStack->Parameters.Read.Length;


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Written By
Software Developer (Senior) AnyAspect Inc.
Hungary Hungary
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

QuestionHi Tamás Friend... could you please help me? Pin
luixilva10-Oct-07 12:48
Memberluixilva10-Oct-07 12:48 
i need to replace a printer for another computer to send the "output" of the lpt1 to a file.

I have some problems to read the data, status and control ports, I mean, i can't manage the busy and acknowledge in the second computer, in fact, i can't read what the first computer is sending to the printer. Could you please help me? or telling me what can i do for solve my problem?

Thank you in advance for your attention.

GeneralKeInitializeDpc is missing ??!! Pin
bubu10116-Aug-07 22:30
Memberbubu10116-Aug-07 22:30 
Questionhelp for device driver vcp Pin
neelamw18-Apr-07 4:23
Memberneelamw18-Apr-07 4:23 
GeneralNull read question Pin
tbnz12-Apr-07 0:36
Membertbnz12-Apr-07 0:36 
GeneralDriver couldn't be opened. Error=2 Pin
joshbond7-Nov-06 23:29
Memberjoshbond7-Nov-06 23:29 
GeneralRe: Driver couldn't be opened. Error=2 Pin
Tamas Karoly17-Nov-06 0:24
MemberTamas Karoly17-Nov-06 0:24 
QuestionHelp Needed Pin
wahiajay21-Sep-06 21:37
Memberwahiajay21-Sep-06 21:37 
AnswerRe: Help Needed Pin
Tamas Karoly17-Nov-06 0:32
MemberTamas Karoly17-Nov-06 0:32 
GeneralHelp Pin
nhthiemsvt23-Aug-06 21:34
Membernhthiemsvt23-Aug-06 21:34 
GeneralRe: Help Pin
Tamas Karoly17-Nov-06 0:37
MemberTamas Karoly17-Nov-06 0:37 
Questionprinter Pin
ahme oguz mermerkaya4-Apr-06 22:13
Memberahme oguz mermerkaya4-Apr-06 22:13 
AnswerRe: printer Pin
Tamas Karoly4-Apr-06 22:56
MemberTamas Karoly4-Apr-06 22:56 
GeneralRe: printer Pin
ahme oguz mermerkaya5-Apr-06 5:06
Memberahme oguz mermerkaya5-Apr-06 5:06 
ekzot6-Sep-05 4:51
Memberekzot6-Sep-05 4:51 
Tamas Karoly3-Dec-05 10:59
MemberTamas Karoly3-Dec-05 10:59 
GeneralIRQ number Pin
Member 6216712-Aug-05 12:23
MemberMember 6216712-Aug-05 12:23 
GeneralRe: IRQ number Pin
Tamas Karoly15-Aug-05 3:23
MemberTamas Karoly15-Aug-05 3:23 
GeneralRe: IRQ number Pin
Member 6216715-Aug-05 4:06
MemberMember 6216715-Aug-05 4:06 
GeneralRe: IRQ number Pin
Tamas Karoly15-Aug-05 5:16
MemberTamas Karoly15-Aug-05 5:16 
QuestionHow to build drive Pin
Georgi Petrov27-Dec-04 23:45
MemberGeorgi Petrov27-Dec-04 23:45 
AnswerRe: How to build drive Pin
Anonymous28-Dec-04 1:27
MemberAnonymous28-Dec-04 1:27 
GeneralReadFileEx Pin
waleri13-Dec-04 9:19
Memberwaleri13-Dec-04 9:19 
GeneralRe: ReadFileEx Pin
Tamas Karoly13-Dec-04 9:42
MemberTamas Karoly13-Dec-04 9:42 
GeneralRe: ReadFileEx Pin
waleri13-Dec-04 19:29
Memberwaleri13-Dec-04 19:29 
GeneralRe: ReadFileEx Pin
Tamas Karoly13-Dec-04 21:57
MemberTamas Karoly13-Dec-04 21:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.