ReactOS user32/win32k Bug Fixes Summary 20260628

ReactOS user32/win32k Bug Fixes Summary

Date: 2026-06-28

Branch: master (03f980c)

Author: caimouse

Overview

This document summarizes bugs fixed in ReactOS's win32k.sys, user32.dll, and related components during a debugging session focused on reducing user32 Wine test failures.


1. CreateWindowEx WM_SIZE/WM_MOVE 顺序修复

问题

ReactOS 在 CreateWindowEx 中发送 WM_SIZE/WM_MOVE 的消息顺序与 Windows 不一致。子窗口在父窗口 WM_CREATE 中创建后,WM_SIZE/WM_MOVE 的发送时机错误。

文件

win32ss/user/ntuser/window.c

修改

co_UserCreateWindowEx 中,将 WM_SIZE/WM_MOVE 的发送改为 inline 实现,确保始终发送 SIZE_RESTORED(即使窗口有 WS_MAXIMIZE,因为在创建阶段窗口尚未被最大化):

c 复制代码
if (!(Window->state & WNDS_SENDSIZEMOVEMSGS))
{
    RECTL Rect;
    LPARAM lParam;
    IntGetClientRect(Window, &Rect);
    lParam = MAKELONG(Rect.right-Rect.left, Rect.bottom-Rect.top);
    co_IntSendMessageNoWait(UserHMGetHandle(Window), WM_SIZE, SIZE_RESTORED, lParam);
    // ... WM_MOVE ...
    Window->state &= ~WNDS_SENDSIZEMOVEMSGS;
}

效果

CreateWindowEx 测试从 58 失败 → 0 失败


2. NC_DoNCPaint 中 WNDS_ACTIVEFRAME 标志同步

问题

频繁输出 Wnd is active and not set active! ERR 消息。NC_DoNCPaint 通过队列判断窗口应显示为激活状态,但 WNDS_ACTIVEFRAME 标志未同步设置。

文件

win32ss/user/ntuser/nonclient.c

修改

在检测到不一致时同步设置标志,而非输出 ERR:

c 复制代码
if (pWnd->state & WNDS_ACTIVEFRAME)
    Flags |= DC_ACTIVE;
else
{
    pWnd->state |= WNDS_ACTIVEFRAME;
    Flags |= DC_ACTIVE;
}

3. IntImeCanDestroyDefIME 空指针崩溃

问题

IntImeCanDestroyDefIME 在遍历 pwndTarget->spwndOwner 链时,窗口已被销毁导致访问无效内存,引发 0xc0000005 崩溃。

文件

win32ss/user/ntuser/ime.c

修改

在遍历 Owner 链和 IntImeSetFutureOwner 中访问 pwndNode 时添加 _SEH2_TRY/_EXCEPT 保护。

效果

IME 销毁崩溃被修复


4. Initializing input window station ERR → TRACE

问题

Initializing input window station 以 ERR 级别输出,但这是 Winlogon 第一次创建窗口站时的正常初始化路径。

文件

win32ss/user/ntuser/winsta.c

修改

c 复制代码
ERR("Initializing input window station\n") → TRACE("Initializing input window station\n")

5. VideoPortEnumerateChildren 重枚举实现

问题

VideoPortEnumerateChildren 第二次调用时输出 FIXME,且仅返回 STATUS_SUCCESS,不做任何处理。

文件

win32ss/drivers/videoprt/videoprt.c

修改

实现重枚举逻辑:

  1. 遍历已存在的子设备列表
  2. 对每个设备重新调用 HwGetVideoChildDescriptor
  3. 如果设备不再存在,从列表中移除(PnP管理器负责清理)
  4. 继续探测新设备
c 复制代码
if (!IsListEmpty(&DeviceExtension->ChildDeviceList))
{
    /* Walk existing children and probe each one */
    for (...)
    {
        Status = HwGetVideoChildDescriptor(...);
        if (Status != VIDEO_ENUM_MORE_DEVICES)
            RemoveEntryList(&CurrentExt->ListEntry);
    }
    i = NextChildId;
}

6. EngpUpdateMonitorDevices PDO 检查

问题

EngpPnPTargetRelationRequest 失败时输出 ERR,且 pDeviceRelations->Count 可能为 0,导致后续 ASSERT 崩溃。

文件

win32ss/gdi/eng/device.c

修改

添加 Count 检查和 TRACE(非 ERR)级别输出:

c 复制代码
Status = EngpPnPTargetRelationRequest(...);
if (!NT_SUCCESS(Status) || pDeviceRelations->Count == 0)
{
    TRACE("EngpPnPTargetRelationRequest() failed or no PDO\n");
    return Status;
}

7. GreMovePointer NULL DC 保护

问题

IntGetScreenDC() 返回 NULL(桌面初始化期间),GreMovePointer(NULL, ...) 导致 Failed to lock the DC ERR。

文件

win32ss/user/ntuser/msgqueue.c

修改

在 3 处添加 NULL 保护:

c 复制代码
// Line 687:
} else if (hdcScreen)
    GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y);

// Line 691:
else if (hdcScreen && CurInfo->ShowingCursor >= 0)
    GreMovePointer(hdcScreen, -1, -1);

// Line 156 (already had null check from line 130):
if (hdcScreen)
    GreMovePointer(hdcScreen, -1, -1);

8. RtlCreateTagHeap 实现

问题

RtlCreateTagHeap 输出 UNIMPLEMENTED,堆标签追踪未实现导致 heap.c:4043 警告。

文件

sdk/lib/rtl/heap.c

修改

实现伪标签追踪:

c 复制代码
RtlCreateTagHeap(...)
{
    if (!HeapHandle) return 0;
    Heap = (PHEAP)HeapHandle;
    if (!Heap->PseudoTagEntries)
        Heap->PseudoTagEntries = (PVOID)(ULONG_PTR)HEAP_PSEUDO_TAG_FLAG;
    TagIndex = ...;
    return TagIndex;
}

需要部署:ntoskrnl.exe + ntdll.dll


9. RpcMgmtSetServerStackSize 实现

问题

RpcMgmtSetServerStackSize 输出 FIXME stub。

文件

dll/win32/rpcrt4/rpc_server.c

修改

实现栈大小设置功能:

c 复制代码
static ULONG RpcServerStackSize = 0;

RPC_STATUS WINAPI RpcMgmtSetServerStackSize(ULONG ThreadStackSize)
{
    TRACE("(0x%lx)\n", ThreadStackSize);
    RpcServerStackSize = ThreadStackSize;
    return RPC_S_OK;
}

并将所有 CreateThread(NULL, 0, ...) 改为 CreateThread(NULL, RpcServerStackSize, ...)

需要部署:rpcrt4.dll


10. DlgDirSelectEx 错误码保持

问题

DlgDirSelectEx 调用中 GetLastError()SendMessage 内部调用覆盖,从 0xDEADBEEF 变为 0x00000000

文件

win32ss/user/user32/windows/dialog.c

修改

c 复制代码
DWORD dwLastError = GetLastError();
// ... 
if (!listbox) { SetLastError(dwLastError); return FALSE; }
// ...
if (item == LB_ERR) { SetLastError(dwLastError); return FALSE; }

11. SYSTEMCUR(ARROW) == NULL 日志降级

问题

系统类注册时系统光标尚未加载引发 ERR 输出。

文件

win32ss/user/ntuser/class.c

修改

c 复制代码
ERR("SYSTEMCUR(ARROW) == NULL, should not happen!!") 
→ TRACE("SYSTEMCUR(ARROW) == NULL, cursors not yet loaded")

12. DrawText EFix (回退)

问题

DrawText DT_EDITCONTROL 模式的 ret/bottom 值不正确(2个失败)。

分析

尝试了 last_line 循环控制和返回值修正方案,但导致更多测试失败(2→45→19),最终回退到原始代码。这是字体渲染引擎的差异,不是 DrawText 本身的逻辑问题。


Deploy Instructions

Each fix requires deploying the updated binaries to the ReactOS VHD:

bash 复制代码
# Mount VHD
cmd /c "diskpart /s mount_vhd.txt"
# Copy binaries
cp output/win32ss/win32k.sys /z/ReactOS/system32/win32k.sys
cp output/win32ss/user/user32/user32.dll /z/ReactOS/system32/user32.dll
cp output/dll/ntdll/ntdll.dll /z/ReactOS/system32/ntdll.dll
cp output/*/videoprt.sys /z/ReactOS/system32/drivers/videoprt.sys
# Dismount
cmd /c "diskpart /s detach_vhd.txt"
# Clone VDI
VBoxManage clonemedium disk vhd vdi --format VDI
VBoxManage storageattach VM --type hdd --medium vdi
# Start VM
VBoxManage startvm VM --type headless