1

我正在为 Windows API 开发 Lua 绑定。到目前为止,我已经能够创建一个带有列表框控件的基本窗口:

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

它创建并显示一个窗口,但该窗口没有正确重绘。从控制台输出中,我可以看到很多 WM_PAINT、WM_ERASEBKGND、WM_NCPAINT 等,我将它们传递给 DefWindowProc,但它似乎没有处理它们。

示例截图: 例子

当窗口第一次出现时,它要么拍摄它后面的任何图像(如本例所示),要么显示为纯灰色。当它被拖动时,它会保留该图像,并且任何拖动它的东西都会留下一个重影图像。(可以在标题栏上看到,在活动窗口周围我有一个白色的小按钮。)如果我将它拖出屏幕一点,它开始变得更加故障,但从未成功地重新绘制自己。

我尝试了各种处理 WM_PAINT、WM_ERASEBKGND 等的方法,但都没有任何效果。示例也没有显示正在处理的这些消息。我做错了什么,还是我需要做其他事情?

4

1 回答 1

1

好吧,我在 Rohitab API Monitor 的帮助下发现了这个问题(我希望如此)。这确实是 Lua 绑定的一个问题:它声明 DefWindowProc 带有四个int参数。这基本上没问题,直到我们收到一条WM_ERASEBKGND消息,其wParam(设备上下文的句柄)> 0x7FFFFFFF,并且调用DefWindowProc无法将其转换为 anint并传递了错误的值。因此,窗口永远不会将某些项目呈现给正确的设备上下文。我的调试日志记录(它包装了 Windows API 函数以将其参数打印到控制台)也没有捕捉到这一点,因为它在值被破坏之前记录了它。

更改要使用的声明uint似乎已经解决了它。(也许我可以为我的绑定使用 API Monitor 的定义文件,因为我的那些似乎有些不准确。)

于 2015-01-23T15:06:15.067 回答