ReactOS 图形系统分析(12):图形设备注册 — device.c

ReactOS 图形系统分析(12):图形设备注册 --- device.c

1. 概述

device.c 位于 win32ss/gdi/eng/device.c,是 GDI 引擎中图形设备(GRAPHICS_DEVICE)的注册与管理系统。它维护全局设备链表,负责:

  • 设备注册EngpRegisterGraphicsDevice(win32k 的 InitDisplayDriver 调用它)

  • 设备发现EngpUpdateGraphicsDeviceList(扫描 HARDWARE\DEVICEMAP\VIDEO 注册表)

  • 设备查找EngpFindGraphicsDevice(按名或按序号)

  • VGA 设备仲裁:识别/链接 VGA 兼容设备,处理 BASEVIDEO 模式

  • 设备参数:从注册表读取显示设置与加速等级

  • 显示器管理EngpUpdateMonitorDevices(PnP 枚举显示器 PDO)

  • IO 服务EngDeviceIoControl(引擎向设备发 IOCTL 的统一入口)

    videoprt.sys (DEVICEMAP\VIDEO 注册表)
    ↓ 扫描注册表
    EngpUpdateGraphicsDeviceList
    ↓ InitDisplayDriver (ntuser/display.c)
    EngpRegisterGraphicsDevice ← 本分析对象核心
    ├── 打开 \Device\VideoN
    ├── 注册 win32k 回调 (IOCTL_VIDEO_INIT_WIN32K_CALLBACKS)
    ├── 保存驱动名/描述
    ├── 枚举显示器
    └── 插入全局链表

    MDEVOBJ_Create / PDEVOBJ_Create 消费设备


2. 核心数据结构与全局状态

2.1 GRAPHICS_DEVICE(pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L57-L77))

c 复制代码
typedef struct _GRAPHICS_DEVICE
{
    WCHAR            szNtDeviceName[CCHDEVICENAME/2];   // NT 设备名 (\Device\Video0)
    WCHAR            szWinDeviceName[CCHDEVICENAME/2];  // Win32 设备名 (\\.\DISPLAY1)
    struct _GRAPHICS_DEVICE * pNextGraphicsDevice;      // 全局链表
    struct _GRAPHICS_DEVICE * pVgaDevice;               // 关联的 VGA 设备
    PDEVICE_OBJECT   DeviceObject;                      // 设备对象 (videoprt FDO)
    PDEVICE_OBJECT   PhysDeviceHandle;                  // 物理设备句柄 (PDO)
    DWORD            hkClassDriverConfig;
    DWORD            StateFlags;                        // DISPLAY_DEVICE_* 标志
    PDEVMODEINFO     pdevmodeInfo;                      // DEVMODE 链表
    ULONG            cDevModes;                         // 模式总数
    PDEVMODEENTRY    pDevModeList;                      // 模式索引数组
    LPWSTR           pDiplayDrivers;                    // InstalledDisplayDrivers
    LPWSTR           pwszDescription;                   // 设备描述
    DWORD            dwMonCnt;                          // 显示器数量
    PVIDEO_MONITOR_DEVICE pvMonDev;                     // 显示器 PDO 数组
    PFILE_OBJECT     FileObject;                        // 打开的设备文件对象
    DWORD            ProtocolType;
} GRAPHICS_DEVICE, *PGRAPHICS_DEVICE;

2.2 全局状态(device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L15-L21))

c 复制代码
static PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;   // 主显示设备(DISPLAY_DEVICE_PRIMARY_DEVICE)
static PGRAPHICS_DEVICE gpVgaGraphicsDevice;       // VGA 兼容设备
static PGRAPHICS_DEVICE gpGraphicsDeviceFirst;     // 设备链表头
static PGRAPHICS_DEVICE gpGraphicsDeviceLast;      // 设备链表尾
static HSEMAPHORE ghsemGraphicsDeviceList;         // 链表锁
static ULONG giDevNum = 1;                         // Win32 设备编号计数

3. 设备注册 --- EngpRegisterGraphicsDevice

#mermaid-svg-gwQkmT8mVWj6CcAO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gwQkmT8mVWj6CcAO .error-icon{fill:#552222;}#mermaid-svg-gwQkmT8mVWj6CcAO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gwQkmT8mVWj6CcAO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gwQkmT8mVWj6CcAO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gwQkmT8mVWj6CcAO .marker.cross{stroke:#333333;}#mermaid-svg-gwQkmT8mVWj6CcAO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gwQkmT8mVWj6CcAO p{margin:0;}#mermaid-svg-gwQkmT8mVWj6CcAO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster-label text{fill:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster-label span{color:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster-label span p{background-color:transparent;}#mermaid-svg-gwQkmT8mVWj6CcAO .label text,#mermaid-svg-gwQkmT8mVWj6CcAO span{fill:#333;color:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO .node rect,#mermaid-svg-gwQkmT8mVWj6CcAO .node circle,#mermaid-svg-gwQkmT8mVWj6CcAO .node ellipse,#mermaid-svg-gwQkmT8mVWj6CcAO .node polygon,#mermaid-svg-gwQkmT8mVWj6CcAO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gwQkmT8mVWj6CcAO .rough-node .label text,#mermaid-svg-gwQkmT8mVWj6CcAO .node .label text,#mermaid-svg-gwQkmT8mVWj6CcAO .image-shape .label,#mermaid-svg-gwQkmT8mVWj6CcAO .icon-shape .label{text-anchor:middle;}#mermaid-svg-gwQkmT8mVWj6CcAO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gwQkmT8mVWj6CcAO .rough-node .label,#mermaid-svg-gwQkmT8mVWj6CcAO .node .label,#mermaid-svg-gwQkmT8mVWj6CcAO .image-shape .label,#mermaid-svg-gwQkmT8mVWj6CcAO .icon-shape .label{text-align:center;}#mermaid-svg-gwQkmT8mVWj6CcAO .node.clickable{cursor:pointer;}#mermaid-svg-gwQkmT8mVWj6CcAO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gwQkmT8mVWj6CcAO .arrowheadPath{fill:#333333;}#mermaid-svg-gwQkmT8mVWj6CcAO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gwQkmT8mVWj6CcAO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gwQkmT8mVWj6CcAO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gwQkmT8mVWj6CcAO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gwQkmT8mVWj6CcAO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gwQkmT8mVWj6CcAO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster text{fill:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO .cluster span{color:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gwQkmT8mVWj6CcAO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gwQkmT8mVWj6CcAO rect.text{fill:none;stroke-width:0;}#mermaid-svg-gwQkmT8mVWj6CcAO .icon-shape,#mermaid-svg-gwQkmT8mVWj6CcAO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gwQkmT8mVWj6CcAO .icon-shape p,#mermaid-svg-gwQkmT8mVWj6CcAO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gwQkmT8mVWj6CcAO .icon-shape .label rect,#mermaid-svg-gwQkmT8mVWj6CcAO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gwQkmT8mVWj6CcAO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gwQkmT8mVWj6CcAO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gwQkmT8mVWj6CcAO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败


EngpRegisterGraphicsDevice

(设备名, 驱动列表, 描述)
分配 GRAPHICS_DEVICE

ExAllocatePoolZero
IoGetDeviceObjectPointer

打开 \\Device\\VideoN

(FILE_READ/WRITE_DATA)
释放返回 NULL
保存 DeviceObject / FileObject
注册 win32k 回调

IOCTL_VIDEO_INIT_WIN32K_CALLBACKS

Callout = VideoPortCallout
复制 NT 设备名

生成 Win32 名 \\\\.\\DISPLAY%d
分配字符串缓冲

驱动列表 + 描述
EngpUpdateMonitorDevices

枚举显示器 PDO
加锁,EngpLinkGraphicsDevice

插入全局链表
giDevNum++
已有屏幕 DC?
为该设备创建 DC

IntGdiCreateDC + 绘制桌面壁纸

(多显示器热插拔)
跳过
返回设备

关键代码:

c 复制代码
PGRAPHICS_DEVICE EngpRegisterGraphicsDevice(pustrDeviceName, pustrDiplayDrivers, pustrDescription)
{
    /* 1. 打开设备对象(videoprt 创建的 FDO) */
    Status = IoGetDeviceObjectPointer(pustrDeviceName,
                                      FILE_READ_DATA | FILE_WRITE_DATA,
                                      &pFileObject, &pDeviceObject);

    /* 2. 注册 win32k 回调(videoprt → win32k 的通知通道) */
    Win32kCallbacks.PhysDisp = pGraphicsDevice;
    Win32kCallbacks.Callout = VideoPortCallout;      // ← 本文件实现的回调
    EngDeviceIoControl((HANDLE)pDeviceObject, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
                       &Win32kCallbacks, sizeof(Win32kCallbacks), ...);
    pGraphicsDevice->PhysDeviceHandle = Win32kCallbacks.pPhysDeviceObject;

    /* 3. 复制设备名/驱动列表/描述 */
    RtlStringCbCopyNW(szNtDeviceName, ..., pustrDeviceName->Buffer, ...);
    RtlStringCbPrintfW(szWinDeviceName, ..., L"\\\\.\\DISPLAY%d", (int)giDevNum);
    /* 驱动列表与描述存入同一缓冲(连续布局) */
    pGraphicsDevice->pDiplayDrivers = pwsz;
    pGraphicsDevice->pwszDescription = pwsz + 驱动列表长度;

    /* 4. 枚举显示器 */
    EngpUpdateMonitorDevices(pGraphicsDevice);

    /* 5. 入全局链表 */
    EngpLinkGraphicsDevice(pGraphicsDevice);
    giDevNum++;
    ...
}

要点:

  • 设备名格式 :NT 名 \Device\VideoN(与 videoprt 的 DEVICEMAP 注册一致),Win32 名 \\.\DISPLAYn(供 EnumDisplayDevices 等使用)
  • win32k 回调注册 :通过 IOCTL_VIDEO_INIT_WIN32K_CALLBACKSVideoPortCallout 注册给 videoprt------这是 videoprt 反向通知 win32k 的通道(如显示器热插拔后刷新)

4. 设备发现与更新 --- EngpUpdateGraphicsDeviceList

#mermaid-svg-RwvQzydE40Rj1Umf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RwvQzydE40Rj1Umf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RwvQzydE40Rj1Umf .error-icon{fill:#552222;}#mermaid-svg-RwvQzydE40Rj1Umf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RwvQzydE40Rj1Umf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RwvQzydE40Rj1Umf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RwvQzydE40Rj1Umf .marker.cross{stroke:#333333;}#mermaid-svg-RwvQzydE40Rj1Umf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RwvQzydE40Rj1Umf p{margin:0;}#mermaid-svg-RwvQzydE40Rj1Umf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RwvQzydE40Rj1Umf .cluster-label text{fill:#333;}#mermaid-svg-RwvQzydE40Rj1Umf .cluster-label span{color:#333;}#mermaid-svg-RwvQzydE40Rj1Umf .cluster-label span p{background-color:transparent;}#mermaid-svg-RwvQzydE40Rj1Umf .label text,#mermaid-svg-RwvQzydE40Rj1Umf span{fill:#333;color:#333;}#mermaid-svg-RwvQzydE40Rj1Umf .node rect,#mermaid-svg-RwvQzydE40Rj1Umf .node circle,#mermaid-svg-RwvQzydE40Rj1Umf .node ellipse,#mermaid-svg-RwvQzydE40Rj1Umf .node polygon,#mermaid-svg-RwvQzydE40Rj1Umf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RwvQzydE40Rj1Umf .rough-node .label text,#mermaid-svg-RwvQzydE40Rj1Umf .node .label text,#mermaid-svg-RwvQzydE40Rj1Umf .image-shape .label,#mermaid-svg-RwvQzydE40Rj1Umf .icon-shape .label{text-anchor:middle;}#mermaid-svg-RwvQzydE40Rj1Umf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RwvQzydE40Rj1Umf .rough-node .label,#mermaid-svg-RwvQzydE40Rj1Umf .node .label,#mermaid-svg-RwvQzydE40Rj1Umf .image-shape .label,#mermaid-svg-RwvQzydE40Rj1Umf .icon-shape .label{text-align:center;}#mermaid-svg-RwvQzydE40Rj1Umf .node.clickable{cursor:pointer;}#mermaid-svg-RwvQzydE40Rj1Umf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RwvQzydE40Rj1Umf .arrowheadPath{fill:#333333;}#mermaid-svg-RwvQzydE40Rj1Umf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RwvQzydE40Rj1Umf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RwvQzydE40Rj1Umf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RwvQzydE40Rj1Umf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RwvQzydE40Rj1Umf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RwvQzydE40Rj1Umf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RwvQzydE40Rj1Umf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RwvQzydE40Rj1Umf .cluster text{fill:#333;}#mermaid-svg-RwvQzydE40Rj1Umf .cluster span{color:#333;}#mermaid-svg-RwvQzydE40Rj1Umf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RwvQzydE40Rj1Umf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RwvQzydE40Rj1Umf rect.text{fill:none;stroke-width:0;}#mermaid-svg-RwvQzydE40Rj1Umf .icon-shape,#mermaid-svg-RwvQzydE40Rj1Umf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RwvQzydE40Rj1Umf .icon-shape p,#mermaid-svg-RwvQzydE40Rj1Umf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RwvQzydE40Rj1Umf .icon-shape .label rect,#mermaid-svg-RwvQzydE40Rj1Umf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RwvQzydE40Rj1Umf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RwvQzydE40Rj1Umf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RwvQzydE40Rj1Umf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

遍历完




EngpUpdateGraphicsDeviceList
打开

HARDWARE\\DEVICEMAP\\VIDEO
读 MaxObjectNumber

(设备总数-1)
循环 iDevNum 0..MaxObjectNumber
构造 \\Device\\Video%lu

和 \\\\.\\DISPLAY%lu
EngpFindGraphicsDevice

已存在?
跳过(已注册)
读 DEVICEMAP 中的注册表路径
InitDisplayDriver

(ntuser) → EngpRegisterGraphicsDevice
选择 VGA 设备

(VGA_COMPATIBLE 标志 + VGASave 服务)
gbBaseVideo 模式?
移除所有非 VGA 设备

(保留基础视频)
保留全部
已有主设备?
选择主设备

(优先非 VGA 驱动设备)
保持
设置 DISPLAY_DEVICE_PRIMARY_DEVICE
VGA 设备链接到主设备

pVgaDevice 字段
完成

VGA 驱动检测(EngpHasVgaDriver):

c 复制代码
static BOOLEAN EngpHasVgaDriver(PGRAPHICS_DEVICE pGraphicsDevice)
{
    /* 1. 打开 DEVICEMAP\VIDEO,读该设备的注册表路径 */
    /* 2. 把路径末尾 "DeviceN" 替换为 "Video"(服务子键) */
    RtlStringCchCopyW(lastBkSlash + 1, ..., L"Video");
    /* 3. 读 "Service" 值,判断是否为 "VGASave" */
    return (_wcsicmp(awcServiceName, L"VGASave") == 0);
}

5. 设备查找 --- EngpFindGraphicsDevice

c 复制代码
PGRAPHICS_DEVICE EngpFindGraphicsDevice(PUNICODE_STRING pustrDevice, ULONG iDevNum)
{
    EngAcquireSemaphore(ghsemGraphicsDeviceList);

    if (pustrDevice && pustrDevice->Buffer)
    {
        /* 按 Win32 设备名查找 */
        for (pGraphicsDevice = gpGraphicsDeviceFirst; pGraphicsDevice;
             pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
        {
            RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
            if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
                break;
        }
    }
    else
    {
        /* 按序号查找(MDEVOBJ_Create 枚举全部设备用) */
        for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
             pGraphicsDevice && i < iDevNum;
             pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
    }

    EngReleaseSemaphore(ghsemGraphicsDeviceList);
    return pGraphicsDevice;
}

6. 设备参数读取

6.1 显示设置 --- EngpGetDisplayDriverParameters

从设备的注册表键(EngpGetRegistryHandleFromDeviceMap 通过 DEVICEMAP 找到)读取默认显示参数:

c 复制代码
NTSTATUS EngpGetDisplayDriverParameters(PGRAPHICS_DEVICE pGraphicsDevice, PDEVMODEW pdm)
{
    RTL_QUERY_REGISTRY_TABLE DisplaySettingsTable[] =
    {
        READ(dmBitsPerPel, "DefaultSettings.BitsPerPel")
        READ(dmPelsWidth, "DefaultSettings.XResolution")
        READ(dmPelsHeight, "DefaultSettings.YResolution")
        READ(dmDisplayFlags, "DefaultSettings.Flags")
        READ(dmDisplayFrequency, "DefaultSettings.VRefresh")
        READ(dmPanningWidth, "DefaultSettings.XPanning")
        READ(dmPanningHeight, "DefaultSettings.YPanning")
        READ(dmDisplayOrientation, "DefaultSettings.Orientation")
        READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput")
        READ(dmPosition.x, "Attach.RelativeX")
        READ(dmPosition.y, "Attach.RelativeY")
        {0}
    };
    hKey = EngpGetRegistryHandleFromDeviceMap(pGraphicsDevice);
    RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)hKey, DisplaySettingsTable, ...);
}

注册表路径解析(EngpGetRegistryHandleFromDeviceMap):

复制代码
HARDWARE\DEVICEMAP\VIDEO
    \Device\Video0 = \Registry\Machine\System\CurrentControlSet\Services\vga\Device0  (REG_SZ)
        ↓ 读取此值得到设备键
CurrentControlSet\Services\vga\Device0
    DefaultSettings.BitsPerPel = 32
    DefaultSettings.XResolution = 800
    ...
    Acceleration.Level = 0

6.2 加速等级 --- EngpGetDisplayDriverAccelerationLevel

读取 Acceleration.Level 注册表值(0~5),供 PDEVOBJ_Create 决定驱动加速能力(见《ReactOS 图形系统分析_9.md》第 4.2 节)。


7. 显示器管理 --- EngpUpdateMonitorDevices

#mermaid-svg-0KPafaqfqlgXYAY4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0KPafaqfqlgXYAY4 .error-icon{fill:#552222;}#mermaid-svg-0KPafaqfqlgXYAY4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0KPafaqfqlgXYAY4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0KPafaqfqlgXYAY4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0KPafaqfqlgXYAY4 .marker.cross{stroke:#333333;}#mermaid-svg-0KPafaqfqlgXYAY4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0KPafaqfqlgXYAY4 p{margin:0;}#mermaid-svg-0KPafaqfqlgXYAY4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster-label text{fill:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster-label span{color:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster-label span p{background-color:transparent;}#mermaid-svg-0KPafaqfqlgXYAY4 .label text,#mermaid-svg-0KPafaqfqlgXYAY4 span{fill:#333;color:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 .node rect,#mermaid-svg-0KPafaqfqlgXYAY4 .node circle,#mermaid-svg-0KPafaqfqlgXYAY4 .node ellipse,#mermaid-svg-0KPafaqfqlgXYAY4 .node polygon,#mermaid-svg-0KPafaqfqlgXYAY4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0KPafaqfqlgXYAY4 .rough-node .label text,#mermaid-svg-0KPafaqfqlgXYAY4 .node .label text,#mermaid-svg-0KPafaqfqlgXYAY4 .image-shape .label,#mermaid-svg-0KPafaqfqlgXYAY4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-0KPafaqfqlgXYAY4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0KPafaqfqlgXYAY4 .rough-node .label,#mermaid-svg-0KPafaqfqlgXYAY4 .node .label,#mermaid-svg-0KPafaqfqlgXYAY4 .image-shape .label,#mermaid-svg-0KPafaqfqlgXYAY4 .icon-shape .label{text-align:center;}#mermaid-svg-0KPafaqfqlgXYAY4 .node.clickable{cursor:pointer;}#mermaid-svg-0KPafaqfqlgXYAY4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0KPafaqfqlgXYAY4 .arrowheadPath{fill:#333333;}#mermaid-svg-0KPafaqfqlgXYAY4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0KPafaqfqlgXYAY4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0KPafaqfqlgXYAY4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0KPafaqfqlgXYAY4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0KPafaqfqlgXYAY4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0KPafaqfqlgXYAY4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster text{fill:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 .cluster span{color:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0KPafaqfqlgXYAY4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0KPafaqfqlgXYAY4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-0KPafaqfqlgXYAY4 .icon-shape,#mermaid-svg-0KPafaqfqlgXYAY4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0KPafaqfqlgXYAY4 .icon-shape p,#mermaid-svg-0KPafaqfqlgXYAY4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0KPafaqfqlgXYAY4 .icon-shape .label rect,#mermaid-svg-0KPafaqfqlgXYAY4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0KPafaqfqlgXYAY4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0KPafaqfqlgXYAY4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0KPafaqfqlgXYAY4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败
失败


EngpUpdateMonitorDevices(pGraphicsDevice)
PnP TargetDeviceRelation 请求

获取 PDO 列表
返回
IoSynchronousInvalidateDeviceRelations

强制 videoprt 重新枚举显示器
IOCTL_VIDEO_ENUM_MONITOR_PDO

获取显示器 PDO 数组
统计显示器数量
已有 pvMonDev?
释放旧数组
跳过
分配新数组并保存

dwMonCnt / pvMonDev
完成


8. win32k 回调 --- VideoPortCallout

videoprt 通过 IOCTL_VIDEO_INIT_WIN32K_CALLBACKS 注册的回调,在设备事件发生时反向通知 win32k:

c 复制代码
VOID NTAPI VideoPortCallout(PVOID Params)
{
    PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = Params;

    switch (CallbackParams->CalloutType)
    {
        case VideoFindAdapterCallout:
            if (CallbackParams->Param == TRUE)
                UserRefreshDisplay(gpmdev->ppdevGlobal);   // 重新启用显示
            break;

        case VideoPowerNotifyCallout:
        case VideoDisplaySwitchCallout:
        case VideoEnumChildPdoNotifyCallout:
        case VideoChangeDisplaySettingsCallout:
        case VideoPnpNotifyCallout:
        case ...:
            ERR("CalloutType 0x%x is UNIMPLEMENTED!\n");
            CallbackParams->Status = STATUS_NOT_IMPLEMENTED;
            break;
    }
}

9. IO 服务 --- EngDeviceIoControl

这是引擎(及 framebuf 等显示驱动)向视频设备发送 IOCTL 的统一入口device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L992-L1072)):

c 复制代码
DWORD APIENTRY EngDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode,
                                  LPVOID lpInBuffer, DWORD cjInBufferSize,
                                  LPVOID lpOutBuffer, DWORD cjOutBufferSize,
                                  LPDWORD lpBytesReturned)
{
    /* 构造同步 IOCTL IRP 并下发 */
    Irp = IoBuildDeviceIoControlRequest(dwIoControlCode, DeviceObject, ...);
    Status = IoCallDriver(DeviceObject, Irp);
    if (Status == STATUS_PENDING)
        KeWaitForSingleObject(&Event, ...);

    /* NTSTATUS → Win32 错误码映射 */
    switch (Status)
    {
        case STATUS_INSUFFICIENT_RESOURCES: return ERROR_NOT_ENOUGH_MEMORY;
        case STATUS_BUFFER_OVERFLOW:        return ERROR_MORE_DATA;
        case STATUS_NOT_IMPLEMENTED:        return ERROR_INVALID_FUNCTION;
        case STATUS_INVALID_PARAMETER:      return ERROR_INVALID_PARAMETER;
        case STATUS_BUFFER_TOO_SMALL:       return ERROR_INSUFFICIENT_BUFFER;
        case STATUS_DEVICE_DOES_NOT_EXIST:  return ERROR_DEV_NOT_EXIST;
        case STATUS_PENDING:                return ERROR_IO_PENDING;
    }
    return Status;
}

使用方: framebuf 的 EngDeviceIoControl(hDriver, IOCTL_VIDEO_SET_CURRENT_MODE, ...)(见《ReactOS 图形系统分析_3.md》第 9.1 节 IOCTL 依赖清单)------所有显示驱动与 videoprt 的交互都经此函数。


10. 完整数据流(设备从注册到使用)

MDEVOBJ_Create 设备链表 device.c (EngpRegisterGraphicsDevice) ntuser/display.c (InitVideo/InitDisplayDriver) win32k 初始化 videoprt.sys MDEVOBJ_Create 设备链表 device.c (EngpRegisterGraphicsDevice) ntuser/display.c (InitVideo/InitDisplayDriver) win32k 初始化 videoprt.sys #mermaid-svg-7Mj1vE4BIjGnR1om{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7Mj1vE4BIjGnR1om .error-icon{fill:#552222;}#mermaid-svg-7Mj1vE4BIjGnR1om .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7Mj1vE4BIjGnR1om .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7Mj1vE4BIjGnR1om .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7Mj1vE4BIjGnR1om .marker.cross{stroke:#333333;}#mermaid-svg-7Mj1vE4BIjGnR1om svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7Mj1vE4BIjGnR1om p{margin:0;}#mermaid-svg-7Mj1vE4BIjGnR1om .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7Mj1vE4BIjGnR1om text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7Mj1vE4BIjGnR1om .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-7Mj1vE4BIjGnR1om .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-7Mj1vE4BIjGnR1om #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-7Mj1vE4BIjGnR1om .sequenceNumber{fill:white;}#mermaid-svg-7Mj1vE4BIjGnR1om #sequencenumber{fill:#333;}#mermaid-svg-7Mj1vE4BIjGnR1om #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-7Mj1vE4BIjGnR1om .messageText{fill:#333;stroke:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7Mj1vE4BIjGnR1om .labelText,#mermaid-svg-7Mj1vE4BIjGnR1om .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .loopText,#mermaid-svg-7Mj1vE4BIjGnR1om .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7Mj1vE4BIjGnR1om .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-7Mj1vE4BIjGnR1om .noteText,#mermaid-svg-7Mj1vE4BIjGnR1om .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-7Mj1vE4BIjGnR1om .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7Mj1vE4BIjGnR1om .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7Mj1vE4BIjGnR1om .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7Mj1vE4BIjGnR1om .actorPopupMenu{position:absolute;}#mermaid-svg-7Mj1vE4BIjGnR1om .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-7Mj1vE4BIjGnR1om .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7Mj1vE4BIjGnR1om .actor-man circle,#mermaid-svg-7Mj1vE4BIjGnR1om line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-7Mj1vE4BIjGnR1om :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建设备对象 + 写 DEVICEMAP\VIDEOInitVideoEngpUpdateGraphicsDeviceList (扫描注册表)InitDisplayDriver (每设备)EngpRegisterGraphicsDeviceIoGetDeviceObjectPointer (打开设备)IOCTL_VIDEO_INIT_WIN32K_CALLBACKS (注册回调)保存驱动名/描述 + 枚举显示器EngpLinkGraphicsDevice (入链表)返回 GRAPHICS_DEVICEMDEVOBJ_Create (枚举设备建 PDEV)EngpFindGraphicsDevice (遍历)EngpGetDisplayDriverParameters (读注册表参数)PDEVOBJ_Create (建立物理设备)


11. 设计特点总结

  1. 注册表驱动发现 :以 HARDWARE\DEVICEMAP\VIDEO + MaxObjectNumber 为准扫描设备,与 videoprt 的注册逻辑闭环。
  2. 全局设备链表gpGraphicsDeviceFirst/Last 双向维护,ghsemGraphicsDeviceList 保护,支持按名/按序号查找。
  3. 主/VGA 设备仲裁gpPrimaryGraphicsDevice(主显示器)与 gpVgaGraphicsDevice(VGA 兼容设备,服务名 VGASave 识别),VGA 设备链接到主设备(pVgaDevice)。
  4. BASEVIDEO 模式gbBaseVideo 时移除所有非 VGA 设备,保证安全模式可用基础显示。
  5. 双向回调通道IOCTL_VIDEO_INIT_WIN32K_CALLBACKS + VideoPortCallout 让 videoprt 能反向通知 win32k(显示器热插拔刷新)。
  6. 统一 IO 入口EngDeviceIoControl 封装 IRP 构造与错误码映射,显示驱动与引擎共享。
  7. 热插拔壁纸绘制:新设备注册时若已有屏幕 DC,自动创建 DC 并绘制桌面(多显示器扩展)。
  8. 字符串紧凑布局:驱动列表与描述存入同一缓冲(连续内存),减少分配次数。

12. 源码索引

位置 关键内容
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L15-L33) 全局状态、InitDeviceImpl
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L35-L99) EngpHasVgaDriver(VGASave 服务检测)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L104-L171) EngpLinkGraphicsDevice / EngpUnlinkGraphicsDevice
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L181-L331) EngpUpdateGraphicsDeviceList(设备发现/主/VGA 仲裁)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L333-L371) EngpGetRegistryHandleFromDeviceMap
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L373-L449) EngpGetDisplayDriverParameters / 加速等级
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L454-L513) VideoPortCallout(win32k 回调)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L515-L659) EngpUpdateMonitorDevices(显示器 PnP)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L661-L792) EngpRegisterGraphicsDevice(核心注册)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L794-L847) EngpFindGraphicsDevice(查找)
device.c(file:///d:/reactos/win32ss/gdi/eng/device.c#L992-L1072) EngDeviceIoControl(统一 IO)

关联模块:

  • ntuser/display.c(file:///d:/reactos/win32ss/user/ntuser/display.c) --- InitDisplayDriver 调用 EngpRegisterGraphicsDevice
  • pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L57-L77) --- GRAPHICS_DEVICE 结构定义
  • mdevobj.c(file:///d:/reactos/win32ss/gdi/eng/mdevobj.c) --- 通过 EngpFindGraphicsDevice 枚举设备
  • videoprt(file:///d:/reactos/win32ss/drivers/videoprt/videoprt.c) --- 设备对象与 DEVICEMAP 来源

关联文档:

  • 《ReactOS 图形系统分析_4.md》--- GDI 引擎整体架构
  • 《ReactOS 图形系统分析_11.md》--- MDEVOBJ(设备集合,消费 GRAPHICS_DEVICE)
  • 《ReactOS 图形系统分析_2.md》--- videoprt 层(设备对象来源)

本文档基于 ReactOS 源代码 win32ss/gdi/eng/device.c 分析(2026 年 8 月)

相关推荐
朋克洛德的码农5 小时前
Go并发-sync包四剑客:Mutex、RWMutex、WaitGroup、Once-从入门到原理
开发语言·后端·golang
程序员与背包客_CoderZ5 小时前
Linux D-Bus通信协议详解:从入门到C/C++编码实战
linux·服务器·c语言·c++·嵌入式硬件·嵌入式软件·dbus
淼澄研学6 小时前
Kimi API黑产倒卖技术解析与Python合规接入指南
开发语言·网络·python
冻柠檬飞冰走茶6 小时前
PTA基础编程题目集 7-35有理数均值(C++语言实现)
开发语言·数据结构·c++·算法·均值算法
wangchen_06 小时前
C++正则表达式
开发语言·c++·正则表达式
何以解忧,唯有..6 小时前
Python 元组(tuple)详解:使用、遍历与排序
开发语言·python
KANGBboy6 小时前
Python eval安全隐患
开发语言·python
IT爱学堂6 小时前
java版数据结构和算法+AI算法和技能学习指南
java·开发语言
嵌入式阿蔡7 小时前
HAL库 vs LL库 vs 寄存器:到底用哪个?
c语言·stm32·单片机·嵌入式硬件·嵌入式实时数据库
Yyyyyy~7 小时前
【C】C语言常见概念
c语言