A normal thing a Windows driver does is to create a device in the device tree. If the device is symbolically linked to \DosDevices\, then you just use CreateFile to open it. However, if the driver doesn't do the linking and the device exists only in \\Device\ tree, then you need to use NtCreateFile.
Using NtCreateFile requires some more code, so I created this code snippet in case I ever need it again:
open_device.h
#pragma once
// Open Device for Windows
// code by gynvael.coldwind//vx (2012)
// This code is public domain. Expect bugs.
//
// I've compiled this code with MinGW GCC. It might require some additional
// declarations or structure definitions or (other changes in fact) to work
// with e.g. Visual C++.
//
#include <windows.h>
HANDLE OpenDeviceA(LPCSTR DeviceName, ACCESS_MASK DesiredAccess);
HANDLE OpenDeviceW(LPCWSTR DeviceName, ACCESS_MASK DesiredAccess);
// ErrString should containt an address of a pointer that will be filled
// with the address of a LocalAlloc'ated string with textual description
// of what happened.
// You can pass NULL here; if you use this, you need to run LocalFree
// later on the string address to free memory.
HANDLE OpenDeviceExA(LPCSTR DeviceName, ACCESS_MASK DesiredAccess, LPVOID ErrString);
HANDLE OpenDeviceExW(LPCWSTR DeviceName, ACCESS_MASK DesiredAccess, LPVOID ErrString);
#ifdef UNICODE
# define OpenDevice OpenDeviceW
# define OpenDeviceEx OpenDeviceExW
#else
# define OpenDevice OpenDeviceA
# define OpenDeviceEx OpenDeviceExA
#endif
open_device.c
// Open Device for Windows
// code by gynvael.coldwind//vx (2012)
// This code is public domain. Expect bugs.
#include <stdio.h>
#include <windows.h>
#include <ntdef.h>
#include "open_device.h"
// IO_STATUS_BLOCK from MSDN:
// http://msdn.microsoft.com/en-us/library/windows/hardware/ff550671.aspx
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
// NtCreateFile prototype from MSDN:
// http://msdn.microsoft.com/en-us/library/bb432380.aspx
typedef NTSTATUS (WINAPI *funcptr_NtCreateFile)(
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
PVOID EaBuffer,
ULONG EaLength
);
// RtlInitUnicodeString prototype from MSDN:
// http://msdn.microsoft.com/en-us/library/ms648420.aspx
typedef VOID (WINAPI *funcptr_RtlInitUnicodeString)(
PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
static HANDLE
OpenDeviceWorker(LPCWSTR DeviceName, ACCESS_MASK DesiredAccess, LPVOID ErrString,
BOOL IsUnicode) {
HANDLE hDev;
UNICODE_STRING UDeviceName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
// Get function pointers.
funcptr_NtCreateFile pNtCreateFile =
(funcptr_NtCreateFile)GetProcAddress(
GetModuleHandle("ntdll.dll"), "NtCreateFile");
funcptr_RtlInitUnicodeString pRtlInitUnicodeString =
(funcptr_RtlInitUnicodeString)GetProcAddress(
GetModuleHandle("ntdll.dll"), "RtlInitUnicodeString");
if(pNtCreateFile == NULL || pRtlInitUnicodeString == NULL)
return INVALID_HANDLE_VALUE; // This should never happen.
// Open the device.
pRtlInitUnicodeString(&UDeviceName, DeviceName);
InitializeObjectAttributes(&ObjectAttributes, &UDeviceName, 0, NULL, NULL);
Status = pNtCreateFile(&hDev, DesiredAccess, &ObjectAttributes,
&IoStatusBlock, NULL,
0, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF, 0, NULL, 0);
if(NT_SUCCESS(Status))
return hDev;
// Error.
if(ErrString == NULL)
return INVALID_HANDLE_VALUE;
if(IsUnicode)
{
FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle("ntdll.dll"),
Status,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR) ErrString,
0, NULL );
} else {
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle("ntdll.dll"),
Status,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) ErrString,
0, NULL );
}
return INVALID_HANDLE_VALUE;
}
HANDLE
OpenDeviceA(LPCSTR DeviceName, ACCESS_MASK DesiredAccess) {
return OpenDeviceExA(DeviceName, DesiredAccess, NULL);
}
HANDLE
OpenDeviceW(LPCWSTR DeviceName, ACCESS_MASK DesiredAccess) {
return OpenDeviceWorker(DeviceName, DesiredAccess, NULL, TRUE);
}
HANDLE
OpenDeviceExA(LPCSTR DeviceName, ACCESS_MASK DesiredAccess, LPVOID ErrString) {
// ANSI -> Unicode.
// First call to get the size (return value includes terminator).
// Second to do the actual conversion.
int ReqSize = MultiByteToWideChar(CP_ACP, 0, DeviceName, -1, NULL, 0);
if(ReqSize <= 0)
return INVALID_HANDLE_VALUE; // Strange.
LPWSTR DeviceNameW = (LPWSTR)malloc(ReqSize * 2); // Int Overflow anyone?
if(DeviceNameW == NULL)
return INVALID_HANDLE_VALUE;
MultiByteToWideChar(CP_ACP, 0, DeviceName, -1, DeviceNameW, ReqSize);
// Call the wrapper.
HANDLE h = OpenDeviceWorker(DeviceNameW, DesiredAccess, ErrString, FALSE);
// Finalize.
free(DeviceNameW);
return h;
}
HANDLE
OpenDeviceExW(LPCWSTR DeviceName, ACCESS_MASK DesiredAccess, LPVOID ErrString) {
return OpenDeviceWorker(DeviceName, DesiredAccess, ErrString, TRUE);
}
test.c
#include <stdio.h>
#include <windows.h>
#include "open_device.h"
int
main(void) {
// How to use:
const char *errmsg = NULL;
HANDLE h = OpenDeviceEx(
"\\Device\\Beep", GENERIC_READ | GENERIC_WRITE,
&errmsg);
// Error checking.
if(h == INVALID_HANDLE_VALUE) {
if(errmsg == NULL) {
printf("error: something in OpenDeviceEx failed\n");
} else {
printf("error: %s\n", errmsg);
}
} else {
printf("info: device was successfully open (HANDLE: %p)\n",
(void*)h);
CloseHandle(h);
}
return 0;
}
I've tested this using MinGW GCC and it works well. I tried compiling it using cl.exe (Visual C++) with newest Platform SDK, but it had some compilation time errors (sorry about these), which I ignored, since I mostly use GCC anyway. I guess it's a matter of resolving some more dependencies (as in structure definitions and such) to get it running on cl.exe too.