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
修改
实现重枚举逻辑:
- 遍历已存在的子设备列表
- 对每个设备重新调用
HwGetVideoChildDescriptor - 如果设备不再存在,从列表中移除(PnP管理器负责清理)
- 继续探测新设备
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