Reputation: 170
You know - in C++, you can obtain a handle to an I/O device by passing its path as the first parameter in a call to CreateFile function. But I wonder how CreateFile locates and identifies the device by the path given. One day, I discovered something about this topic using WinObj utility, which may be probably true.
In WinObj, there is a directory named Global?? which is located under the root directory, and can be seen on the left pane. This directory contains lots of items of type "SymbolicLink".
When you pass the path of an I/O device (such as "\.\C:" or "\.\Changer0") as the first parameter in a call to CreateFile
function, the CreateFile function parses out the path and removes "\.\" from it to find a portion of the path. Then it searches the Global?? directory for a SymbolicLink with the same name as that portion. Then it finds the address that the symbolic link refers to, which is the primary Physical Device Object Name (for example, "\Device\CdRom0").
So, is my discovery true? Are the devices contained within Global?? directory the only devices that CreateFile
function can open? Just to mention something: not all of the items in Global?? directory refer to a device, I guess.
One day, I wanted to decrease the screen brightness of my monitor programmatically, by sending an IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS
control code to my monitor. The problem was that I didn't know what to specify for the first parameter of CreateFile
function. So I did the following:
DISPLAY#Default_Monitor#5&1193a8c7&0&UID100663553#{866519b5-3f07-4c97-b7df-24c5d8a8ccb8}
CreateFile
function. Any ideas on how to do all of this operation programmatically? I mean, I'd like my program itself to obtain the value of lpFileName
parameter. Not I place this string directly in my program's source code.
Upvotes: 1
Views: 3170
Reputation: 33706
at first in CreateFile
we can use any name from NT namespace, if prefix it with \\?\globalroot
or even better \\?\global\globalroot
prefix. say if we want open \Device\0000006f
we can use \\?\globalroot\Device\0000006f
or \\?\global\globalroot\Device\0000006f
name. the win32 sybsystem when convert win32 path to NT-path - if view '\?` prefix - simply convert it to \??\
. so nt path will be look like \??\[global\]globalroot\Device\0000006f
. the \??
is virtual directory - it not exist really. object manager look in Local and Global MS-DOS Device Names when view \??\
. first in local (which look like \Sessions\0\DosDevices\<luid>
) and if not found in global - \GLOBAL??
. however in any local dos device directory exist symbolic link Global
which point to \Global??
. as result after parse \??\global
we will be redirected to \Global??
. then in \Global??
folder exist symbolic link GLOBALROOT
which is empty - point to root of NT namespace.
as result after parse
\\?\global\globalroot\Device\0000006f
will be re-parsed to \Device\0000006f
and \\?\globalroot\Device\0000006f
also, if no globalroot
object in Local device namespace (usually not exist, but suddenly somebody create it).
now about how open monitor device. we must not hard-code any device name. instead we need search for devices which support GUID_DEVINTERFACE_MONITOR
by call CM_Get_Device_Interface_ListW
and use returned name in call CreateFile
#include <ntddvdeo.h>
CONFIGRET EnumMonitors()
{
CONFIGRET err;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
ULONG BufferLen = 0, NeedLen = 128;
union {
PVOID buf;
PWSTR pszDeviceInterface;
};
for(;;)
{
if (BufferLen < NeedLen)
{
BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
}
switch (err = CM_Get_Device_Interface_ListW(const_cast<PGUID>(&GUID_DEVINTERFACE_MONITOR),
0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, const_cast<PGUID>(&GUID_DEVINTERFACE_MONITOR),
0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
default:
return err;
}
continue;
case CR_SUCCESS:
while (*pszDeviceInterface)
{
HANDLE hFile = CreateFileW(pszDeviceInterface, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov = {};
DISPLAY_BRIGHTNESS db;
DeviceIoControl(hFile, IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS, 0, 0,
&db, sizeof(db), 0, &ov);
CloseHandle(hFile);
DbgPrint("(%x,%x,%x) %S\n", db.ucDisplayPolicy, db.ucACBrightness, db.ucDCBrightness, pszDeviceInterface);
}
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return 0;
}
}
}
Upvotes: 5