如何将信息传递给使用 WinAPI 创建的 window 过程
How to pass information to window proc created using WinAPI
我需要创建一个 window 来处理消息 (WM_HOTKEY),所以我开始使用以下内容进行低级别处理,并使用 SetWindowLong 传递实例信息以在 [=25] 中使用=]proc.
fWindow:=CreateWindowEx(WS_EX_TOOLWINDOW,MsgWndClass.lpszClassName,'',WS_POPUP,0,0,0,0,0,0,HInstance,nil);
SetWindowLong(fWindow,GWL_USERDATA,NativeInt(Self));
并且 windowproc 是
class function TMessageWindow.WindowProc(hWnd: HWND; uMsg: Integer; wParam: WPARAM; lParam: LPARAM): Integer;
begin
var I:=GetWindowLong(hWnd,GWL_USERDATA);
if I=0 then
Exit(DefWindowProc(hWnd,uMsg,wParam,lParam));
Result:=TMessageWindow(I).HandleMessage(uMsg,wParam,lParam);
end;
当我尝试从 TMessageWindow 创建一个继承的 class 并且 HandleMessage 是虚拟的时,我的问题就出现了。
我发现虽然 HandleMessage 函数在继承 class 中被覆盖,但 TMessageWindow(I) 的类型转换正在调用基本方法。
在四处寻找这方面的例子后,我找不到任何使用 SetWindowLong 函数将信息传递给 windowproc 的例子,所以我现在认为必须有更好的方法。
首先,确保您的 class
方法也被标记为 static
以删除隐藏的 Self
参数,并且使用 stdcall
调用约定,如果你还没有这样做,例如:
class function WindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; static;
在那之后,如果你正在为 64 位编译,你的代码需要使用 (Get|Set)WindowLongPtr()
来代替,例如:
private
fWindow: HWND;
fWindow := ...;
SetWindowLongPtr(fWindow, GWLP_USERDATA, LONG_PTR(Self));
class function TMessageWindow.WindowProc(hWnd: HWND; uMsg: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
begin
var I := GetWindowLongPtr(hWnd, GWLP_USERDATA);
if I <> 0 then
Result := TMessageWindow(I).HandleMessage(uMsg, wParam, lParam)
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
或者,使用 SetWindowSubclass()
,例如:
private
fWindow: HWND;
class function SubclassWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall; static;
uses
..., Commctrl;
fWindow := ...;
SetWindowSubclass(fWindow, @TMessageWindow.SubclassWindowProc, 1, DWORD_PTR(Self));
class function TMessageWindow.SubclassWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall;
begin
if uMsg = WM_NCDESTROY then
RemoveWindowSubclass(hWnd, @TMessageWindow.SubclassWindowProc, uIdSubclass);
Result := TMessageWindow(dwRefData).HandleMessage(uMsg, wParam, lParam);
// have HandleMessage() call DefSubclassProc() for any unhandled messages...
end;
也就是说,使用虚拟消息过程创建消息 window 的更简单方法是改用 RTL 的 AllocateHWnd()
函数,例如:
private
fWindow: HWND;
procedure HandleMessage(var Message: TMessage); virtual;
// to create the window:
fWindow := AllocateHWnd(HandleMessage);
// to destroy the window:
DeallocateHWnd(fWindow);
procedure TMessageWindow.HandleMessage(var Message: TMessage);
begin
with Message do
Result := DefWindowProc(fWindow, Msg, WParam, LParam);
end;
我需要创建一个 window 来处理消息 (WM_HOTKEY),所以我开始使用以下内容进行低级别处理,并使用 SetWindowLong 传递实例信息以在 [=25] 中使用=]proc.
fWindow:=CreateWindowEx(WS_EX_TOOLWINDOW,MsgWndClass.lpszClassName,'',WS_POPUP,0,0,0,0,0,0,HInstance,nil);
SetWindowLong(fWindow,GWL_USERDATA,NativeInt(Self));
并且 windowproc 是
class function TMessageWindow.WindowProc(hWnd: HWND; uMsg: Integer; wParam: WPARAM; lParam: LPARAM): Integer;
begin
var I:=GetWindowLong(hWnd,GWL_USERDATA);
if I=0 then
Exit(DefWindowProc(hWnd,uMsg,wParam,lParam));
Result:=TMessageWindow(I).HandleMessage(uMsg,wParam,lParam);
end;
当我尝试从 TMessageWindow 创建一个继承的 class 并且 HandleMessage 是虚拟的时,我的问题就出现了。
我发现虽然 HandleMessage 函数在继承 class 中被覆盖,但 TMessageWindow(I) 的类型转换正在调用基本方法。
在四处寻找这方面的例子后,我找不到任何使用 SetWindowLong 函数将信息传递给 windowproc 的例子,所以我现在认为必须有更好的方法。
首先,确保您的 class
方法也被标记为 static
以删除隐藏的 Self
参数,并且使用 stdcall
调用约定,如果你还没有这样做,例如:
class function WindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; static;
在那之后,如果你正在为 64 位编译,你的代码需要使用 (Get|Set)WindowLongPtr()
来代替,例如:
private
fWindow: HWND;
fWindow := ...;
SetWindowLongPtr(fWindow, GWLP_USERDATA, LONG_PTR(Self));
class function TMessageWindow.WindowProc(hWnd: HWND; uMsg: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
begin
var I := GetWindowLongPtr(hWnd, GWLP_USERDATA);
if I <> 0 then
Result := TMessageWindow(I).HandleMessage(uMsg, wParam, lParam)
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
或者,使用 SetWindowSubclass()
,例如:
private
fWindow: HWND;
class function SubclassWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall; static;
uses
..., Commctrl;
fWindow := ...;
SetWindowSubclass(fWindow, @TMessageWindow.SubclassWindowProc, 1, DWORD_PTR(Self));
class function TMessageWindow.SubclassWindowProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uIdSubclass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall;
begin
if uMsg = WM_NCDESTROY then
RemoveWindowSubclass(hWnd, @TMessageWindow.SubclassWindowProc, uIdSubclass);
Result := TMessageWindow(dwRefData).HandleMessage(uMsg, wParam, lParam);
// have HandleMessage() call DefSubclassProc() for any unhandled messages...
end;
也就是说,使用虚拟消息过程创建消息 window 的更简单方法是改用 RTL 的 AllocateHWnd()
函数,例如:
private
fWindow: HWND;
procedure HandleMessage(var Message: TMessage); virtual;
// to create the window:
fWindow := AllocateHWnd(HandleMessage);
// to destroy the window:
DeallocateHWnd(fWindow);
procedure TMessageWindow.HandleMessage(var Message: TMessage);
begin
with Message do
Result := DefWindowProc(fWindow, Msg, WParam, LParam);
end;