Javad Bayat
Javad Bayat

Reputation: 170

Can CreateFile function open a handle to the devices that are only listed under Global?? directory in WinObj utility?

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.

The second part of my question

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:

  1. I opened Device Manager and found my monitor device among the list. The name of that device was "Generic Non-PnP Monitor".
  2. I right clicked on that list item and clicked on Properties.
  3. In the Properties window, I clicked on Details tab. Then I selected Physical Device Object name from the Property drop-down list.
  4. On the value section, I found the text "\Device\0000006f".
  5. In WinObj, I searched the Global?? directory for a SymbolicLink which refers to "\Device\0000006f". The name of that SymbolicLink was long:

DISPLAY#Default_Monitor#5&1193a8c7&0&UID100663553#{866519b5-3f07-4c97-b7df-24c5d8a8ccb8}

  1. Then I put a "\.\" before the aforementioned SymbolicLink name and specified it as the first parameter of 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

Answers (1)

RbMm
RbMm

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

Related Questions