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