子类化说起来好像很玄妙,其实很简单。这个技术的目标就是,假设你要改变一个已经存在的、具有确定行为的窗口的行为,就使用子类化技术来完成。其实际的动作,就是调用 SetWindowLong 把目标窗口的回调函数修改掉,用你自己的函数接管。这个技术本身没有任何难度,也没有什么神奇。真正的问题在于,你要仔细去了解你要修改的目标当前的行为,然后找到最合适的切入点来做你想做的事情。然而,你千万不要把子类化看作万能的灵丹妙药,从而希望它能够解决所有窗口相关的问题。由于你要子类化的窗口往往不是你自己实现的,或者系统本身有其他制约,那就需要考虑别的解决办法,或者多种方法相结合。拿你现在的需求来说。有两种情况:“发送”是一个单独的按钮窗口,或者是另一个大窗口的一个部分。如果是前者,你就可以直接子类化按钮窗口,处理它的鼠标按下消息;或者子类化它的父窗口,处理 WM_COMMAND 消息(当然要分辨是不是真正来自于“发送”按钮,可以用 ID 来识别)。如果是后一种情况,那你就需要子类化大窗口,处理鼠标点击消息,并检查该次点击是不是位于“发送”所在的区域内。这些都是见招拆招的自然想法,没什么高深的东西。希望对你有帮助。
library TmHook;uses SysUtils, Classes, Windows, Messages, CommCtrl; var hhook: Windows.HHOOK; hwndListView: HWND; na:array[0..300] of char; oldWndProc: Integer; {$R *.res} function WindowProc(hwnd: HWND; msg: UINT; wp: WPARAM; lp: LPARAM): LRESULT; stdcall; begin case Msg of WM_LBUTTONDOWN: MessageBox(hwndListView,'你好!','提示',MB_OK); end; Result := CallWindowProc(TFNWndProc(oldWndProc), hwnd, msg, wp, lp); end; function TmHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall; var cwp: PCwpStruct; m:TPoint; begin Result := 0; if (nCode < 0) then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; cwp := PCwpStruct(lParam); if cwp^.message <> WM_COMMAND then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; GetCursorPos(m); GetWindowText(WindowFromPoint(m),na,299); if lstrcmpi('发送(S)',na)<>0 then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; hwndListView:=WindowFromPoint(m); oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc)); end; procedure Hook(bEnable: Boolean); stdcall; export; begin if bEnable then begin if hhook = 0 then begin hhook := SetWindowsHookEx(WH_CALLWNDPROC, TmHookProc, HInstance, 0); end; end else begin if hhook <> 0 then begin UnhookWindowsHookEx(hhook); hhook := 0; end; end; end; exports Hook; begin hhook := 0; hwndListView := 0; oldWndProc:=0; end. 到底哪里错了,一运行,qq就自动关闭??
library QqHook;{ Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project-View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL--even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. }uses SysUtils, Classes, Windows, Messages, CommCtrl;var hhook: Windows.HHOOK; hwndTarget: HWND; oldWndProc: Integer;{$R *.res}function WindowProc(hwnd: HWND; msg: UINT; wp: WPARAM; lp: LPARAM): LRESULT; stdcall; begin if (msg = WM_COMMAND) and (HIWORD(wp) = BN_CLICKED) and (LOWORD(wp) = $077E) then begin MessageBox(0, 'Is the message correct?', ':-)', MB_OK); end; if msg = WM_DESTROY then begin SetWindowLongA(hwnd, GWL_WNDPROC, Integer(oldWndProc)); hwndTarget := 0; end; result := CallWindowProc(TFNWndProc(oldWndProc), hwnd, msg, wp, lp); end;function QqHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall; var cwp: PCwpStruct; hwnd: Windows.HWND; hwndParent: Windows.HWND; wndLong: DWORD; found: Boolean; textBuffer: Array[0..MAX_PATH] of AnsiChar; fileName: PAnsiChar;begin Result := 0; if (nCode < 0) then // or (nCode = HC_NOREMOVE) begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; cwp := PCwpStruct(lParam); if cwp^.message <> WM_CREATE then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; // 此处判断是不是 QQ 进程 found := false; GetModuleFileNameA(0, textBuffer, MAX_PATH); fileName := StrRScan(textBuffer, '\'); fileName := fileName + 1; if AnsiStrIComp(fileName, 'QQ.exe') <> 0 then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; hwnd := cwp^.hwnd; OutputDebugStringA('Is child window?'); // 首先判断是不是个子窗口 wndLong := GetWindowLongA(hwnd, GWL_STYLE); if (wndLong and WS_CHILD) = WS_CHILD then begin OutputDebugStringA(' Yes'); OutputDebugStringA('Is parent window a dialog?'); // 其次判断其父窗口是不是一个对话框 hwndParent := GetParent(hwnd); GetClassNameA(hwndParent, @textBuffer, 80); if AnsiStrIComp(textBuffer, '#32770') = 0 then begin OutputDebugStringA(' Yes'); OutputDebugStringA('Is its ID 0x077E?'); // 再次判断其 ID 是不是 0x077E,这是 QQ 2008 对话窗口中发送按钮的 ID 值 wndLong := GetWindowLongA(hwnd, GWL_ID); if wndLong = $0000077E then begin OutputDebugStringA(' Yes'); // 注意:子类化父窗口 hwndTarget := hwndParent; oldWndProc := SetWindowLongA(hwndTarget, GWL_WNDPROC, Integer(@WindowProc)); found := true; OutputDebugStringA('The target window found!'); end else OutputDebugStringA(' No'); end else OutputDebugStringA(' No'); end else OutputDebugStringA(' No'); if not found then begin Result := CallNextHookEx(hhook, nCode, wParam, lParam); exit; end; end;procedure Hook(bEnable: Boolean); stdcall; export; begin if bEnable then begin if hhook = 0 then begin hhook := SetWindowsHookEx(WH_CALLWNDPROC, QqHookProc, HInstance, 0); end; end else begin if hhook <> 0 then begin UnhookWindowsHookEx(hhook); hhook := 0; end; end; end;procedure DllEntry(reason: Integer); begin if reason = DLL_PROCESS_DETACH then begin if (oldWndProc <> 0) and (hwndTarget <> 0) then begin SetWindowLongA(hwndTarget, GWL_WNDPROC, oldWndProc); end; end; end;exports Hook;begin hhook := 0; hwndTarget := 0; oldWndProc := 0; DllProc := DllEntry; end.
SysUtils,
Classes,
Windows,
Messages,
CommCtrl;
var
hhook: Windows.HHOOK;
hwndListView: HWND;
na:array[0..300] of char;
oldWndProc: Integer;
{$R *.res}
function WindowProc(hwnd: HWND; msg: UINT; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;
begin
case Msg of
WM_LBUTTONDOWN:
MessageBox(hwndListView,'你好!','提示',MB_OK);
end;
Result := CallWindowProc(TFNWndProc(oldWndProc), hwnd, msg, wp, lp);
end;
function TmHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
var
cwp: PCwpStruct;
m:TPoint;
begin
Result := 0;
if (nCode < 0) then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end;
cwp := PCwpStruct(lParam);
if cwp^.message <> WM_COMMAND then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end;
GetCursorPos(m);
GetWindowText(WindowFromPoint(m),na,299);
if lstrcmpi('发送(S)',na)<>0 then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end;
hwndListView:=WindowFromPoint(m);
oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc));
end;
procedure Hook(bEnable: Boolean); stdcall; export;
begin
if bEnable then
begin
if hhook = 0 then
begin
hhook := SetWindowsHookEx(WH_CALLWNDPROC, TmHookProc, HInstance, 0);
end;
end
else
begin
if hhook <> 0 then
begin
UnhookWindowsHookEx(hhook);
hhook := 0;
end;
end;
end;
exports
Hook;
begin
hhook := 0;
hwndListView := 0;
oldWndProc:=0;
end.
到底哪里错了,一运行,qq就自动关闭??
oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc));
两句的作用应该是一样的吧?
都会得到前窗口过程.
说得好,受益非浅!处理 WM_COMMAND 消息的时候,如何分辨是正真来自于“发送”按扭?以ID识别是什么意思 ?我发现用cwp := PCwpStruct(lParam); 而得到的cwp^.HWND句柄不对,取到的是该按扭的父句柄,而不是当前按扭句柄。楼主用WindowFromPoint是取当前鼠标位置的句柄,这样如果是用鼠标单击该按扭而激发WM_COMMAND消息,那么肯定取到的句柄是对的。但这种写法显示很不爽,还不如直接用鼠标钩子。最主要的BUG是如果该按扭不是用鼠标点击的而激发WM_COMMAND 消息,用WindowFromPoint取句柄根本就是错误的。如何取到WM_COMMAND消息时对应的按扭句柄?是不是菜单的单击也会有WM_COMMAND 消息?
我想让你帮我改一下代码,如果代码错的离谱,就帮我写一个贴上来.
因为,我觉得你在这方面很在行,所以开帖向你请教如果你认为我不钻研,哪我只能说,弄了十多天,已经没心思了.如果你没时间的写代码的话,哪就不麻烦你了.
library QqHook;{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }uses
SysUtils,
Classes,
Windows,
Messages,
CommCtrl;var
hhook: Windows.HHOOK;
hwndTarget: HWND;
oldWndProc: Integer;{$R *.res}function WindowProc(hwnd: HWND; msg: UINT; wp: WPARAM; lp: LPARAM): LRESULT; stdcall;
begin
if (msg = WM_COMMAND) and (HIWORD(wp) = BN_CLICKED) and (LOWORD(wp) = $077E) then
begin
MessageBox(0, 'Is the message correct?', ':-)', MB_OK);
end; if msg = WM_DESTROY then
begin
SetWindowLongA(hwnd, GWL_WNDPROC, Integer(oldWndProc));
hwndTarget := 0;
end; result := CallWindowProc(TFNWndProc(oldWndProc), hwnd, msg, wp, lp);
end;function QqHookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): Integer; stdcall;
var
cwp: PCwpStruct;
hwnd: Windows.HWND;
hwndParent: Windows.HWND;
wndLong: DWORD;
found: Boolean;
textBuffer: Array[0..MAX_PATH] of AnsiChar;
fileName: PAnsiChar;begin
Result := 0; if (nCode < 0) then // or (nCode = HC_NOREMOVE)
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end; cwp := PCwpStruct(lParam);
if cwp^.message <> WM_CREATE then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end; // 此处判断是不是 QQ 进程
found := false; GetModuleFileNameA(0, textBuffer, MAX_PATH);
fileName := StrRScan(textBuffer, '\');
fileName := fileName + 1;
if AnsiStrIComp(fileName, 'QQ.exe') <> 0 then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end; hwnd := cwp^.hwnd; OutputDebugStringA('Is child window?'); // 首先判断是不是个子窗口
wndLong := GetWindowLongA(hwnd, GWL_STYLE);
if (wndLong and WS_CHILD) = WS_CHILD then
begin
OutputDebugStringA(' Yes'); OutputDebugStringA('Is parent window a dialog?'); // 其次判断其父窗口是不是一个对话框
hwndParent := GetParent(hwnd);
GetClassNameA(hwndParent, @textBuffer, 80);
if AnsiStrIComp(textBuffer, '#32770') = 0 then
begin
OutputDebugStringA(' Yes'); OutputDebugStringA('Is its ID 0x077E?'); // 再次判断其 ID 是不是 0x077E,这是 QQ 2008 对话窗口中发送按钮的 ID 值
wndLong := GetWindowLongA(hwnd, GWL_ID);
if wndLong = $0000077E then
begin
OutputDebugStringA(' Yes'); // 注意:子类化父窗口
hwndTarget := hwndParent;
oldWndProc := SetWindowLongA(hwndTarget, GWL_WNDPROC, Integer(@WindowProc)); found := true;
OutputDebugStringA('The target window found!');
end
else
OutputDebugStringA(' No');
end
else
OutputDebugStringA(' No');
end
else
OutputDebugStringA(' No'); if not found then
begin
Result := CallNextHookEx(hhook, nCode, wParam, lParam);
exit;
end;
end;procedure Hook(bEnable: Boolean); stdcall; export;
begin
if bEnable then
begin
if hhook = 0 then
begin
hhook := SetWindowsHookEx(WH_CALLWNDPROC, QqHookProc, HInstance, 0);
end;
end
else
begin
if hhook <> 0 then
begin
UnhookWindowsHookEx(hhook);
hhook := 0;
end;
end;
end;procedure DllEntry(reason: Integer);
begin
if reason = DLL_PROCESS_DETACH then
begin
if (oldWndProc <> 0) and (hwndTarget <> 0) then
begin
SetWindowLongA(hwndTarget, GWL_WNDPROC, oldWndProc);
end;
end;
end;exports
Hook;begin
hhook := 0;
hwndTarget := 0;
oldWndProc := 0; DllProc := DllEntry;
end.