ReactOS 图形系统分析(9):物理设备对象 --- PDEVOBJ
1. 概述
PDEVOBJ(物理设备对象,Physical Device Object)位于 win32ss/gdi/eng/pdevobj.c(实现)与 pdevobj.h(定义),是 GDI 引擎中代表一个物理显示/打印设备的核心对象。它绑定"逻辑设备(LDEV)的 DDI 函数表"与"具体显示驱动实例(DHPDEV)",管理设备表面、调色板、DEVMODE、加速等级等全部设备状态。
GRAPHICS_DEVICE (图形设备:设备名/DEVICEMAP 映射)
↓ PDEVOBJ_Create
PDEVOBJ (物理设备对象 ← 本分析对象)
├── pldev → LDEVOBJ (驱动加载 + DDI 函数表)
├── dhpdev → 驱动实例句柄 (DrvEnablePDEV 返回)
├── pSurface → 显示表面 (DrvEnableSurface 创建)
├── devinfo / gdiinfo → 设备能力
└── pfn → DRIVER_FUNCTIONS (DDI 回调表副本)
在引擎渲染时,SURFOBJ->hdev 实际就是 PDEVOBJ 指针,GDIDEVFUNCS(pso) 宏从 PDEVOBJ 取 DDI 函数表------因此 PDEVOBJ 是引擎调度显示驱动的枢纽。
2. 核心数据结构 --- PDEVOBJ(pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L79-L149))
c
typedef struct _PDEVOBJ
{
BASEOBJECT BaseObject;
struct _PDEVOBJ * ppdevNext; // 全局 PDEV 链表
LONG cPdevRefs; // 引用计数
LONG cPdevOpenRefs;
struct _PDEVOBJ * ppdevParent; // 父 PDEV(克隆)
FLONG flFlags; // PDEV_* 标志
HSEMAPHORE hsemDevLock; // 设备锁
POINTL ptlPointer; // 指针位置
HSURF ahsurf[HS_DDI_MAX]; // 引擎图案表面句柄数组
PWSTR pusPrtDataFileName; // 打印机数据文件
ULONG cDirectDrawDisableLocks;
PVOID pvGammaRamp; // Gamma 曲线
SIZEL szlMetaRes; // 元文件设备分辨率
PFN_DrvMovePointer pfnMovePointer; // 指针移动函数(默认为 EngMovePointer)
struct _LDEVOBJ * pldev; // ← 逻辑设备对象
DHPDEV dhpdev; // ← 驱动设备句柄
struct _PALETTE* ppalSurf; // 设备调色板
DEVINFO devinfo; // ← 设备信息(DrvEnablePDEV 填)
GDIINFO gdiinfo; // ← GDI 信息(DrvEnablePDEV 填)
PSURFACE pSurface; // ← 显示表面
HANDLE hSpooler; // 设备对象(图形设备)
PGRAPHICS_DEVICE pGraphicsDevice; // ← 图形设备
POINTL ptlOrigion;
PDEVMODEW pdmwDev; // 当前 DEVMODE
FLONG DxDd_Flags; // DirectDraw 状态
DWORD dwAccelerationLevel; // ← 加速等级 (0-5)
union
{
DRIVER_FUNCTIONS DriverFunctions; // ← DDI 函数表副本
PVOID apfn[INDEX_LAST];
};
GDIPOINTER Pointer; // 软件光标状态
struct _EDD_DIRECTDRAW_GLOBAL * pEDDgpl; // DirectDraw 全局
} PDEVOBJ, *PPDEVOBJ;
2.1 PDEV 标志(flFlags)
| 标志 | 含义 |
|---|---|
| PDEV_DISPLAY | 显示设备 |
| PDEV_HARDWARE_POINTER / PDEV_SOFTWARE_POINTER | 硬件/软件光标 |
| PDEV_GOTFONTS | 已加载字体驱动 |
| PDEV_PRINTER | 打印设备 |
| PDEV_DISABLED | 设备已禁用 |
| PDEV_SYNCHRONIZE_ENABLED | 同步已启用 |
| PDEV_FONTDRIVER | 字体设备 |
| PDEV_UMPD | 用户模式驱动 |
| PDEV_META_DEVICE | 元文件设备 |
| PDEV_CLONE_DEVICE | 克隆设备 |
2.2 相关结构
GDIPOINTER --- 软件光标状态:
c
typedef struct _GDIPOINTER
{
BOOL Enabled; // 是否启用
SIZEL Size; // 光标尺寸
POINTL HotSpot; // 热点
SURFACE *psurfColor; // 颜色表面
SURFACE *psurfMask; // 掩码表面
SURFACE *psurfSave; // 保存表面(光标下像素)
FLONG flags;
RECTL Exclude; // 排除区域
} GDIPOINTER, *PGDIPOINTER;
GRAPHICS_DEVICE --- 图形设备(每个显示设备一份):
c
typedef struct _GRAPHICS_DEVICE
{
WCHAR szNtDeviceName[CCHDEVICENAME/2]; // NT 设备名 (\Device\Video0)
WCHAR szWinDeviceName[CCHDEVICENAME/2]; // Win32 设备名 (DISPLAY1)
PDEVICE_OBJECT DeviceObject; // 设备对象
DWORD StateFlags; // DISPLAY_DEVICE_*
PDEVMODEINFO pdevmodeInfo; // DEVMODE 链表
ULONG cDevModes;
LPWSTR pDiplayDrivers; // InstalledDisplayDrivers
LPWSTR pwszDescription; // 设备描述
} GRAPHICS_DEVICE, *PGRAPHICS_DEVICE;
3. 生命周期管理
3.1 分配 / 引用 / 释放
c
PPDEVOBJ PDEVOBJ_AllocPDEV(VOID)
{
ppdev = ExAllocatePoolWithTag(PagedPool, sizeof(PDEVOBJ), GDITAG_PDEV);
RtlZeroMemory(ppdev, sizeof(PDEVOBJ));
ppdev->hsemDevLock = EngCreateSemaphore(); // 设备锁
ppdev->pEDDgpl = ExAllocatePoolWithTag(...); // DirectDraw 全局
ppdev->cPdevRefs = 1; // 初始引用
return ppdev;
}
/* 引用:正在销毁的 PDEV 拒绝引用 */
FORCEINLINE VOID PDEVOBJ_vReference(PPDEVOBJ ppdev)
{
if (ppdev->cPdevRefs == 0) { ASSERT(FALSE); return; }
InterlockedIncrement(&ppdev->cPdevRefs);
}
/* 释放:计数归零时销毁表面、调色板并删除 PDEV */
VOID PDEVOBJ_vRelease(PPDEVOBJ ppdev)
{
if (InterlockedDecrement(&ppdev->cPdevRefs) == 0)
{
if (ppdev->pSurface) /* 调 DrvDisableSurface */
{
SURFACE_ShareUnlockSurface(ppdev->pSurface);
ppdev->pfn.DisableSurface(ppdev->dhpdev);
}
if (ppdev->ppalSurf) /* 释放调色板 */
PALETTE_ShareUnlockPalette(ppdev->ppalSurf);
if (ppdev->pfn.DisablePDEV) /* 调 DrvDisablePDEV */
ppdev->pfn.DisablePDEV(ppdev->dhpdev);
PDEVOBJ_vDeletePDEV(ppdev); /* 释放 DEVMODE/锁/对象 */
}
}
全局链表: gppdevList(受 ghsemPDEV 信号量保护)串联所有 PDEV;InitPDEVImpl 在 win32k 初始化时创建信号量。
4. PDEVOBJ_Create --- 设备创建核心
#mermaid-svg-chMJEYbpnpo1qsDU{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-chMJEYbpnpo1qsDU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-chMJEYbpnpo1qsDU .error-icon{fill:#552222;}#mermaid-svg-chMJEYbpnpo1qsDU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-chMJEYbpnpo1qsDU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-chMJEYbpnpo1qsDU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-chMJEYbpnpo1qsDU .marker.cross{stroke:#333333;}#mermaid-svg-chMJEYbpnpo1qsDU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-chMJEYbpnpo1qsDU p{margin:0;}#mermaid-svg-chMJEYbpnpo1qsDU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-chMJEYbpnpo1qsDU .cluster-label text{fill:#333;}#mermaid-svg-chMJEYbpnpo1qsDU .cluster-label span{color:#333;}#mermaid-svg-chMJEYbpnpo1qsDU .cluster-label span p{background-color:transparent;}#mermaid-svg-chMJEYbpnpo1qsDU .label text,#mermaid-svg-chMJEYbpnpo1qsDU span{fill:#333;color:#333;}#mermaid-svg-chMJEYbpnpo1qsDU .node rect,#mermaid-svg-chMJEYbpnpo1qsDU .node circle,#mermaid-svg-chMJEYbpnpo1qsDU .node ellipse,#mermaid-svg-chMJEYbpnpo1qsDU .node polygon,#mermaid-svg-chMJEYbpnpo1qsDU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-chMJEYbpnpo1qsDU .rough-node .label text,#mermaid-svg-chMJEYbpnpo1qsDU .node .label text,#mermaid-svg-chMJEYbpnpo1qsDU .image-shape .label,#mermaid-svg-chMJEYbpnpo1qsDU .icon-shape .label{text-anchor:middle;}#mermaid-svg-chMJEYbpnpo1qsDU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-chMJEYbpnpo1qsDU .rough-node .label,#mermaid-svg-chMJEYbpnpo1qsDU .node .label,#mermaid-svg-chMJEYbpnpo1qsDU .image-shape .label,#mermaid-svg-chMJEYbpnpo1qsDU .icon-shape .label{text-align:center;}#mermaid-svg-chMJEYbpnpo1qsDU .node.clickable{cursor:pointer;}#mermaid-svg-chMJEYbpnpo1qsDU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-chMJEYbpnpo1qsDU .arrowheadPath{fill:#333333;}#mermaid-svg-chMJEYbpnpo1qsDU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-chMJEYbpnpo1qsDU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-chMJEYbpnpo1qsDU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-chMJEYbpnpo1qsDU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-chMJEYbpnpo1qsDU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-chMJEYbpnpo1qsDU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-chMJEYbpnpo1qsDU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-chMJEYbpnpo1qsDU .cluster text{fill:#333;}#mermaid-svg-chMJEYbpnpo1qsDU .cluster span{color:#333;}#mermaid-svg-chMJEYbpnpo1qsDU 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-chMJEYbpnpo1qsDU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-chMJEYbpnpo1qsDU rect.text{fill:none;stroke-width:0;}#mermaid-svg-chMJEYbpnpo1qsDU .icon-shape,#mermaid-svg-chMJEYbpnpo1qsDU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-chMJEYbpnpo1qsDU .icon-shape p,#mermaid-svg-chMJEYbpnpo1qsDU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-chMJEYbpnpo1qsDU .icon-shape .label rect,#mermaid-svg-chMJEYbpnpo1qsDU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-chMJEYbpnpo1qsDU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-chMJEYbpnpo1qsDU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-chMJEYbpnpo1qsDU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
是
否
否
失败
是
否
失败
失败
PDEVOBJ_Create
(pGraphicsDevice, pdm,
dwAccelerationLevel, ldevtype)
非 META 设备?
遍历 gppdevList 查找匹配
(同 GraphicsDevice + 同 DEVMODE
- 同加速等级)
找到匹配 PDEV?
PDEVOBJ_vEnableDisplay
重新启用后返回(复用)
PDEVOBJ_bDisableDisplay
(关闭不匹配的旧 PDEV)
LDEVOBJ_pLoadInternal
(元文件内部驱动)
LDEVOBJ_pLoadDriver
(加载显示驱动)
返回 NULL
PDEVOBJ_AllocPDEV 分配
保存 GraphicsDevice/hSpooler
复制 DEVMODE
flFlags = PDEV_DISPLAY
指针排除区初始化
复制 DDI 函数表
pfn = pldev->pfn
加速等级≥5 或需平移?
用 gPanDispDrvFn 覆盖
平移显示函数表
保持原函数表
PDEVOBJ_bEnablePDEV
→ DrvEnablePDEV
PDEVOBJ_vCompletePDEV
→ DrvCompletePDEV
PDEVOBJ_pSurface
→ DrvEnableSurface 创建表面
PDEVOBJ_bEnableDirectDraw
PDEVOBJ_vFilterDriverHooks
(按加速等级过滤 DDI)
设置 pfnMovePointer
(无驱动实现则 EngMovePointer)
插入 gppdevList 链表
返回 ppdev
关键点:
- PDEV 复用:相同设备 + 相同模式 + 相同加速等级的 PDEV 直接复用,避免重复创建
- 平移显示(Panning) :加速等级 ≥5 或请求平移模式时,用
gPanDispDrvFn覆盖部分 DDI 函数表实现平移显示 - 创建失败回滚 :任一步失败即
PDEVOBJ_vRelease+EngUnloadImage释放驱动
4.1 PDEVOBJ_bEnablePDEV --- 调用 DrvEnablePDEV
c
BOOL NTAPI PDEVOBJ_bEnablePDEV(PPDEVOBJ ppdev, PDEVMODEW pdevmode, PWSTR pwszLogAddress)
{
pfnEnablePDEV = ppdev->pfn.EnablePDEV;
/* 调用驱动 DrvEnablePDEV,驱动填充 gdiinfo/devinfo 并返回 dhpdev */
ppdev->dhpdev = pfnEnablePDEV(pdevmode, pwszLogAddress,
HS_DDI_MAX, ppdev->ahsurf, // 图案表面数组
sizeof(GDIINFO), (PULONG)&ppdev->gdiinfo,
sizeof(DEVINFO), &ppdev->devinfo,
(HDEV)ppdev, // hdev = PDEVOBJ!
pwszDescription, DeviceObject);
if (ppdev->dhpdev == NULL) return FALSE;
/* 修正缺失值 */
if (ppdev->gdiinfo.ulLogPixelsX == 0) ppdev->gdiinfo.ulLogPixelsX = 96;
if (ppdev->gdiinfo.ulLogPixelsY == 0) ppdev->gdiinfo.ulLogPixelsY = 96;
/* 设置光栅能力(RC_*) */
ppdev->gdiinfo.flRaster = RC_OP_DX_OUTPUT | RC_GDI20_OUTPUT | RC_BIGFONT;
if (非绘图仪/字符流设备)
flRaster |= RC_STRETCHDIB | RC_STRETCHBLT | RC_DIBTODEV | RC_BITBLT ...;
if (DT_RASDISPLAY) flRaster |= RC_FLOODFILL;
if (GCAPS_PALMANAGED) flRaster |= RC_PALETTE;
/* 锁定默认调色板;用引擎影线画刷补全空位 */
ppdev->ppalSurf = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
for (i = 0; i < HS_DDI_MAX; i++)
if (ppdev->ahsurf[i] == NULL)
ppdev->ahsurf[i] = gahsurfHatch[i]; // 引擎影线表面
return TRUE;
}
要点: hdev 参数直接传入 (HDEV)ppdev------这就是为什么 SURFOBJ->hdev 能通过 GDIDEVFUNCS 取回 DDI 函数表。
4.2 加速等级过滤 --- PDEVOBJ_vFilterDriverHooks
注册表 Accelerations.Level(0~5)控制驱动加速能力:
| 等级 | 行为 |
|---|---|
| ≥1 | 移除 DrvSetPointerShape、DrvCreateDeviceBitmap |
| ≥2 | 移除表面 HOOK_STRETCHBLT/FILLPATH/GRADIENTFILL/LINETO/ALPHABLEND/TRANSPARENTBLT |
| ≥3 | 禁用 DirectDraw/Direct3D 加速(FIXME 未实现) |
| ≥4 | 移除几乎所有 HOOK(仅保留 BITBLT/COPYBITS/TEXTOUT/STROKEPATH/SYNCHRONIZE) |
5. 模式管理
5.1 PDEVOBJ_bSwitchMode --- 切换显示模式
#mermaid-svg-s9Fbp2nF2syXuBda{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-s9Fbp2nF2syXuBda .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s9Fbp2nF2syXuBda .error-icon{fill:#552222;}#mermaid-svg-s9Fbp2nF2syXuBda .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s9Fbp2nF2syXuBda .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s9Fbp2nF2syXuBda .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s9Fbp2nF2syXuBda .marker.cross{stroke:#333333;}#mermaid-svg-s9Fbp2nF2syXuBda svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s9Fbp2nF2syXuBda p{margin:0;}#mermaid-svg-s9Fbp2nF2syXuBda .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s9Fbp2nF2syXuBda .cluster-label text{fill:#333;}#mermaid-svg-s9Fbp2nF2syXuBda .cluster-label span{color:#333;}#mermaid-svg-s9Fbp2nF2syXuBda .cluster-label span p{background-color:transparent;}#mermaid-svg-s9Fbp2nF2syXuBda .label text,#mermaid-svg-s9Fbp2nF2syXuBda span{fill:#333;color:#333;}#mermaid-svg-s9Fbp2nF2syXuBda .node rect,#mermaid-svg-s9Fbp2nF2syXuBda .node circle,#mermaid-svg-s9Fbp2nF2syXuBda .node ellipse,#mermaid-svg-s9Fbp2nF2syXuBda .node polygon,#mermaid-svg-s9Fbp2nF2syXuBda .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s9Fbp2nF2syXuBda .rough-node .label text,#mermaid-svg-s9Fbp2nF2syXuBda .node .label text,#mermaid-svg-s9Fbp2nF2syXuBda .image-shape .label,#mermaid-svg-s9Fbp2nF2syXuBda .icon-shape .label{text-anchor:middle;}#mermaid-svg-s9Fbp2nF2syXuBda .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s9Fbp2nF2syXuBda .rough-node .label,#mermaid-svg-s9Fbp2nF2syXuBda .node .label,#mermaid-svg-s9Fbp2nF2syXuBda .image-shape .label,#mermaid-svg-s9Fbp2nF2syXuBda .icon-shape .label{text-align:center;}#mermaid-svg-s9Fbp2nF2syXuBda .node.clickable{cursor:pointer;}#mermaid-svg-s9Fbp2nF2syXuBda .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s9Fbp2nF2syXuBda .arrowheadPath{fill:#333333;}#mermaid-svg-s9Fbp2nF2syXuBda .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s9Fbp2nF2syXuBda .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s9Fbp2nF2syXuBda .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s9Fbp2nF2syXuBda .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s9Fbp2nF2syXuBda .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s9Fbp2nF2syXuBda .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s9Fbp2nF2syXuBda .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s9Fbp2nF2syXuBda .cluster text{fill:#333;}#mermaid-svg-s9Fbp2nF2syXuBda .cluster span{color:#333;}#mermaid-svg-s9Fbp2nF2syXuBda 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-s9Fbp2nF2syXuBda .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s9Fbp2nF2syXuBda rect.text{fill:none;stroke-width:0;}#mermaid-svg-s9Fbp2nF2syXuBda .icon-shape,#mermaid-svg-s9Fbp2nF2syXuBda .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s9Fbp2nF2syXuBda .icon-shape p,#mermaid-svg-s9Fbp2nF2syXuBda .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s9Fbp2nF2syXuBda .icon-shape .label rect,#mermaid-svg-s9Fbp2nF2syXuBda .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s9Fbp2nF2syXuBda .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s9Fbp2nF2syXuBda .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s9Fbp2nF2syXuBda :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败
PDEVOBJ_bSwitchMode(ppdev, pdm)
加锁
hsemDevLock + ghsemPDEV
PDEVOBJ_bDisableDisplay
临时禁用当前 PDEV
恢复 DirectDraw 后退出
PDEVOBJ_bEnablePDEV
用新模式重新启用(DrvEnablePDEV)
PDEVOBJ_pSurface
创建新表面
PDEVOBJ_bEnableDirectDraw
PDEVOBJ_bDynamicModeChange
(新旧 PDEV 交换状态)
解锁并返回
5.2 PDEVOBJ_bDynamicModeChange --- 动态模式切换
切换模式时创建临时 PDEV (新模式),然后与旧 PDEV 交换全部状态:
c
BOOL NTAPI PDEVOBJ_bDynamicModeChange(PPDEVOBJ ppdev, PPDEVOBJ ppdev2)
{
/* 交换:DDI 函数表 / LDEV / DHPDEV / 表面 / devinfo / gdiinfo / DEVMODE / 状态标志 */
temp.pfn = ppdev->pfn; ppdev->pfn = ppdev2->pfn; ppdev2->pfn = temp.pfn;
SwitchPointer(&ppdev->pldev, &ppdev2->pldev);
SwitchPointer(&ppdev->dhpdev, &ppdev2->dhpdev);
SwitchPointer(&ppdev->pSurface, &ppdev2->pSurface);
/* 表面重新关联新 PDEV */
ppdev->pSurface->SurfObj.hdev = (HDEV)ppdev;
ppdev2->pSurface->SurfObj.hdev = (HDEV)ppdev2;
/* 通知驱动新的 HDEV 关联 */
ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev);
ppdev2->pfn.CompletePDEV(ppdev2->dhpdev, (HDEV)ppdev2);
/* 切换 DirectDraw 模式 */
PDEVOBJ_vSwitchDirectDraw(ppdev, ppdev2);
}
优点: 无需销毁重建 PDEV,模式切换期间所有表面/句柄连续有效。
6. 与显示驱动的完整交互
显示驱动 (framebuf/vga) PDEVOBJ LDEVOBJ (加载驱动) PDEVOBJ_Create win32k 初始化 显示驱动 (framebuf/vga) PDEVOBJ LDEVOBJ (加载驱动) PDEVOBJ_Create win32k 初始化 #mermaid-svg-tLdur69NNO8pav6D{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-tLdur69NNO8pav6D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tLdur69NNO8pav6D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tLdur69NNO8pav6D .error-icon{fill:#552222;}#mermaid-svg-tLdur69NNO8pav6D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tLdur69NNO8pav6D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tLdur69NNO8pav6D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tLdur69NNO8pav6D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tLdur69NNO8pav6D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tLdur69NNO8pav6D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tLdur69NNO8pav6D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tLdur69NNO8pav6D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tLdur69NNO8pav6D .marker.cross{stroke:#333333;}#mermaid-svg-tLdur69NNO8pav6D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tLdur69NNO8pav6D p{margin:0;}#mermaid-svg-tLdur69NNO8pav6D .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tLdur69NNO8pav6D text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tLdur69NNO8pav6D .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tLdur69NNO8pav6D .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tLdur69NNO8pav6D .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tLdur69NNO8pav6D .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tLdur69NNO8pav6D #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tLdur69NNO8pav6D .sequenceNumber{fill:white;}#mermaid-svg-tLdur69NNO8pav6D #sequencenumber{fill:#333;}#mermaid-svg-tLdur69NNO8pav6D #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tLdur69NNO8pav6D .messageText{fill:#333;stroke:none;}#mermaid-svg-tLdur69NNO8pav6D .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tLdur69NNO8pav6D .labelText,#mermaid-svg-tLdur69NNO8pav6D .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tLdur69NNO8pav6D .loopText,#mermaid-svg-tLdur69NNO8pav6D .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tLdur69NNO8pav6D .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-tLdur69NNO8pav6D .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tLdur69NNO8pav6D .noteText,#mermaid-svg-tLdur69NNO8pav6D .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tLdur69NNO8pav6D .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tLdur69NNO8pav6D .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tLdur69NNO8pav6D .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tLdur69NNO8pav6D .actorPopupMenu{position:absolute;}#mermaid-svg-tLdur69NNO8pav6D .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-tLdur69NNO8pav6D .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tLdur69NNO8pav6D .actor-man circle,#mermaid-svg-tLdur69NNO8pav6D line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tLdur69NNO8pav6D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 桌面就绪 创建显示 PDEV (DEVMODE) LDEVOBJ_pLoadDriver(设备名) pldev (含 DDI 函数表) PDEVOBJ_AllocPDEV DrvEnablePDEV(DEVMODE, hdev=PDEVOBJ) dhpdev + gdiinfo/devinfo DrvCompletePDEV(hdev) DrvEnableSurface → 表面 hSurfEng (ahsurf/表面句柄) 设置 pfnMovePointer / DirectDraw 返回 PDEVOBJ 渲染 → GDIDEVFUNCS(pso) 从 PDEVOBJ 取 DDI 切换模式 → PDEVOBJ_bSwitchMode 关闭显示 → PDEVOBJ_bDisableDisplay
7. 设计特点总结
- 设备调度枢纽 :PDEVOBJ 聚合"LDEV 函数表 + 驱动实例 dhpdev + 表面 + 设备信息",
SURFOBJ->hdev == PDEVOBJ,引擎由此统一调度驱动 DDI。 - PDEV 复用:相同设备/模式/加速等级复用已有 PDEV,避免重复初始化。
- 引用计数生命周期 :
cPdevRefs计数保护,归零时依次释放表面(DrvDisableSurface)、调色板、PDEV(DrvDisablePDEV)。 - 模式切换无损 :
PDEVOBJ_bDynamicModeChange交换全部状态而非重建,切换期间句柄连续有效。 - 加速等级可调 :注册表
Accelerations.Level(0~5)逐级移除驱动加速,用于调试和兼容。 - 软件光标兜底 :
pfnMovePointer无驱动实现时回退EngMovePointer。 - 平移显示支持:加速等级 ≥5 时用平移函数表覆盖 DDI,支持大桌面小屏幕。
- 影线画刷补齐:驱动未提供的图案表面用引擎影线画刷(gahsurfHatch)补全。
8. 源码索引
| 位置 | 关键内容 |
|---|---|
| pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L4-L23) | PDEVFLAGS 标志定义 |
| pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L27-L48) | GDIPOINTER、DEVMODEINFO 结构 |
| pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L57-L77) | GRAPHICS_DEVICE 结构 |
| pdevobj.h(file:///d:/reactos/win32ss/gdi/eng/pdevobj.h#L79-L149) | PDEVOBJ 结构定义 |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L61-L87) | PDEVOBJ_AllocPDEV |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L102-L171) | PDEVOBJ_vRelease(资源释放顺序) |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L172-L241) | PDEVOBJ_bEnablePDEV(DrvEnablePDEV 调用) |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L253-L300) | PDEVOBJ_vFilterDriverHooks(加速等级) |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L462-L626) | PDEVOBJ_Create(创建流程) |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L643-L699) | PDEVOBJ_bDynamicModeChange(状态交换) |
| pdevobj.c(file:///d:/reactos/win32ss/gdi/eng/pdevobj.c#L702-L760) | PDEVOBJ_bSwitchMode(模式切换) |
关联模块:
- ldevobj.h(file:///d:/reactos/win32ss/gdi/eng/ldevobj.h) / ldevobj.c --- 逻辑设备对象(PDEV 的 pldev 引用)
- mdevobj.h(file:///d:/reactos/win32ss/gdi/eng/mdevobj.h) / mdevobj.c --- 映射设备对象(多显示器,聚合多个 PDEV)
- device.c(file:///d:/reactos/win32ss/gdi/eng/device.c) --- GRAPHICS_DEVICE 注册(PDEV 的 pGraphicsDevice)
关联文档:
- 《ReactOS 图形系统分析_4.md》--- GDI 引擎整体架构(本文件是设备对象管理子模块)
- 《ReactOS 图形系统分析_2.md》--- videoprt 层(PDEV 与驱动的关系)
- 《ReactOS图形系统架构分析.md》--- 四层设备对象体系
本文档基于 ReactOS 源代码
win32ss/gdi/eng/pdevobj.c、pdevobj.h分析(2026 年 8 月)