Windows API: 简单 window 没有正确重绘
Windows API: Simple window does not redraw correctly
我正在为 Windows API 开发 Lua 绑定。到目前为止,我已经能够创建一个带有列表框控件的基本 window:
require('Alien')
package.path = 'libs\?.lua;libs\?\init.lua;' .. package.path
local function printf(...) io.write(string.format(...)) end
Windows = require('Windows')
local W = Windows
print("loaded OK")
print("Command line=", W.GetCommandLine())
local windowClassName = "testWindow"
local windowClass, window
local hInstance = assert(W.GetModuleHandle(nil))
assert(W.WM_CREATE)
assert(W.WM_CLOSE)
assert(W.WM_DESTROY)
assert(W.DefWindowProc)
assert(W.DestroyWindow)
assert(W.PostQuitMessage)
local msgID = {}
for k, v in pairs(Windows) do
if k:match('^WM_') then msgID[v] = k end
end
local function wndProc(hWnd, msg, wParam, lParam)
local id = msgID[msg] or ('%08X'):format(msg)
printf('wndProc(%08X, %20s, %08X, %08X\n',
hWnd,id, wParam, lParam)
if msg == W.WM_CREATE then
printf("window %08X create\n", hWnd)
elseif msg == W.WM_CLOSE then W.DestroyWindow(hWnd)
elseif msg == W.WM_DESTROY then W.PostQuitMessage(0)
else return W.DefWindowProc(hWnd, msg, wParam, lParam)
end
return 0
end
local wndProcCB = alien.callback(wndProc,
assert(W.LRESULT), assert(W.HWND),
assert(W.UINT), assert(W.WPARAM), assert(W.LPARAM))
windowClass = assert(W.WNDCLASSEX:new {
cbSize = assert(W.WNDCLASSEX.size),
style = bit.bor(W.CS_HREDRAW, W.CS_VREDRAW),
lpfnWndProc = assert(wndProcCB),
cbClsExtra = 0,
cbWndExtra = 0,
hInstance = assert(hInstance),
hIcon = nil,
hCursor = nil,
hbrBackground = assert(W.COLOR_APPWORKSPACE)+1,
lpszMenuName = nil,
lpszClassName = assert(windowClassName),
hIconSm = nil,
})
assert(W.RegisterClassEx(windowClass))
local dwExStyle = bit.bor(
W.WS_EX_TOOLWINDOW,
W.WS_EX_OVERLAPPEDWINDOW)
local dwStyle = bit.bor(
W.WS_OVERLAPPEDWINDOW,
W.WS_CLIPSIBLINGS,
W.WS_CLIPCHILDREN)
window = assert(W.CreateWindowEx(
assert(dwExStyle), --dwExStyle
assert(windowClassName), --lpClassName
"Test Window", --lpWindowName
assert(dwStyle), --dwStyle
assert(W.CW_USEDEFAULT), --x
assert(W.CW_USEDEFAULT), --y
420, 314, --width, height
nil, nil, assert(hInstance), nil)) --hWndParent, hMenu, hInstance, lpParam
local SWP_SHOW_ONLY = bit.bor(
W.SWP_ASYNCWINDOWPOS,
W.SWP_SHOWWINDOW,
W.SWP_NOACTIVATE,
W.SWP_NOMOVE,
W.SWP_NOSIZE,
W.SWP_NOZORDER)
W.SetWindowPos(assert(window), nil, 0, 0, 0, 0, assert(SWP_SHOW_ONLY))
local msg = assert(W.MSG:new())
while W.GetMessage(msg, 0, 0, 0) do
W.TranslateMessage(msg)
W.DispatchMessage(msg)
end
它创建并显示 window,但 window 没有正确重绘。从控制台输出我可以看到很多 WM_PAINT、WM_ERASEBKGND、WM_NCPAINT 等,我将其传递给 DefWindowProc,但它似乎没有处理它们。
示例截图:
当 window 首次出现时,它要么拍摄其背后的任何图像(如本例),要么显示为纯灰色。当它被拖来拖去时,它会保留那个图像,任何拖过它的东西都会留下一个幻影。 (可以在标题栏上看到,在活动 window 之后我有一个白色的小按钮。)如果我将它拖离屏幕一点,它会开始出现更多故障,但从未成功地重新绘制自己。
我尝试了各种处理 WM_PAINT、WM_ERASEBKGND 等的方法,但 none 都没有效果。示例也没有显示正在处理的这些消息。我做错了什么,还是我需要做其他事情?
好吧,我在 Rohitab API Monitor 的帮助下发现了这个问题(我希望如此)。 Lua 绑定确实存在问题:它已将 DefWindowProc 声明为采用四个 int
参数。这基本上没问题,直到我们收到一条带有 wParam
(设备上下文句柄)的 WM_ERASEBKGND
消息 > 0x7FFFFFFF,并且对 DefWindowProc
的调用无法将其转换为 int
并传递了错误的值。所以 window 永远不会将某些项目渲染到正确的设备上下文。我的调试日志记录(包装 Windows API 函数以将它们的参数打印到控制台)也没有捕捉到这个,因为它在它被破坏之前记录了值。
将声明更改为使用 uint
似乎已解决问题。 (也许我可以使用 API Monitor 的定义文件作为我的绑定,因为我的定义文件似乎有些不准确。)
我正在为 Windows API 开发 Lua 绑定。到目前为止,我已经能够创建一个带有列表框控件的基本 window:
require('Alien')
package.path = 'libs\?.lua;libs\?\init.lua;' .. package.path
local function printf(...) io.write(string.format(...)) end
Windows = require('Windows')
local W = Windows
print("loaded OK")
print("Command line=", W.GetCommandLine())
local windowClassName = "testWindow"
local windowClass, window
local hInstance = assert(W.GetModuleHandle(nil))
assert(W.WM_CREATE)
assert(W.WM_CLOSE)
assert(W.WM_DESTROY)
assert(W.DefWindowProc)
assert(W.DestroyWindow)
assert(W.PostQuitMessage)
local msgID = {}
for k, v in pairs(Windows) do
if k:match('^WM_') then msgID[v] = k end
end
local function wndProc(hWnd, msg, wParam, lParam)
local id = msgID[msg] or ('%08X'):format(msg)
printf('wndProc(%08X, %20s, %08X, %08X\n',
hWnd,id, wParam, lParam)
if msg == W.WM_CREATE then
printf("window %08X create\n", hWnd)
elseif msg == W.WM_CLOSE then W.DestroyWindow(hWnd)
elseif msg == W.WM_DESTROY then W.PostQuitMessage(0)
else return W.DefWindowProc(hWnd, msg, wParam, lParam)
end
return 0
end
local wndProcCB = alien.callback(wndProc,
assert(W.LRESULT), assert(W.HWND),
assert(W.UINT), assert(W.WPARAM), assert(W.LPARAM))
windowClass = assert(W.WNDCLASSEX:new {
cbSize = assert(W.WNDCLASSEX.size),
style = bit.bor(W.CS_HREDRAW, W.CS_VREDRAW),
lpfnWndProc = assert(wndProcCB),
cbClsExtra = 0,
cbWndExtra = 0,
hInstance = assert(hInstance),
hIcon = nil,
hCursor = nil,
hbrBackground = assert(W.COLOR_APPWORKSPACE)+1,
lpszMenuName = nil,
lpszClassName = assert(windowClassName),
hIconSm = nil,
})
assert(W.RegisterClassEx(windowClass))
local dwExStyle = bit.bor(
W.WS_EX_TOOLWINDOW,
W.WS_EX_OVERLAPPEDWINDOW)
local dwStyle = bit.bor(
W.WS_OVERLAPPEDWINDOW,
W.WS_CLIPSIBLINGS,
W.WS_CLIPCHILDREN)
window = assert(W.CreateWindowEx(
assert(dwExStyle), --dwExStyle
assert(windowClassName), --lpClassName
"Test Window", --lpWindowName
assert(dwStyle), --dwStyle
assert(W.CW_USEDEFAULT), --x
assert(W.CW_USEDEFAULT), --y
420, 314, --width, height
nil, nil, assert(hInstance), nil)) --hWndParent, hMenu, hInstance, lpParam
local SWP_SHOW_ONLY = bit.bor(
W.SWP_ASYNCWINDOWPOS,
W.SWP_SHOWWINDOW,
W.SWP_NOACTIVATE,
W.SWP_NOMOVE,
W.SWP_NOSIZE,
W.SWP_NOZORDER)
W.SetWindowPos(assert(window), nil, 0, 0, 0, 0, assert(SWP_SHOW_ONLY))
local msg = assert(W.MSG:new())
while W.GetMessage(msg, 0, 0, 0) do
W.TranslateMessage(msg)
W.DispatchMessage(msg)
end
它创建并显示 window,但 window 没有正确重绘。从控制台输出我可以看到很多 WM_PAINT、WM_ERASEBKGND、WM_NCPAINT 等,我将其传递给 DefWindowProc,但它似乎没有处理它们。
示例截图:
当 window 首次出现时,它要么拍摄其背后的任何图像(如本例),要么显示为纯灰色。当它被拖来拖去时,它会保留那个图像,任何拖过它的东西都会留下一个幻影。 (可以在标题栏上看到,在活动 window 之后我有一个白色的小按钮。)如果我将它拖离屏幕一点,它会开始出现更多故障,但从未成功地重新绘制自己。
我尝试了各种处理 WM_PAINT、WM_ERASEBKGND 等的方法,但 none 都没有效果。示例也没有显示正在处理的这些消息。我做错了什么,还是我需要做其他事情?
好吧,我在 Rohitab API Monitor 的帮助下发现了这个问题(我希望如此)。 Lua 绑定确实存在问题:它已将 DefWindowProc 声明为采用四个 int
参数。这基本上没问题,直到我们收到一条带有 wParam
(设备上下文句柄)的 WM_ERASEBKGND
消息 > 0x7FFFFFFF,并且对 DefWindowProc
的调用无法将其转换为 int
并传递了错误的值。所以 window 永远不会将某些项目渲染到正确的设备上下文。我的调试日志记录(包装 Windows API 函数以将它们的参数打印到控制台)也没有捕捉到这个,因为它在它被破坏之前记录了值。
将声明更改为使用 uint
似乎已解决问题。 (也许我可以使用 API Monitor 的定义文件作为我的绑定,因为我的定义文件似乎有些不准确。)