子类化问题?

zhangxiaommmm 2008-11-18 04:56:41
在qq的聊天窗口,当按下"发送"按钮之前,执行一段我的代码
希望能用全局消息钩子加子类化函数实现
WH_CALLWNDPROC setwindowslong
因为,用全局钩子消息,我已会

我在学子类化函数的用法?
...全文
267 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
CaiBirdy 2008-11-28
  • 打赏
  • 举报
回复
发现你原先子类化时传的句柄不是窗口句柄而是“发送”按钮的句柄
dandycheung 2008-11-26
  • 打赏
  • 举报
回复

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.
zhangxiaommmm 2008-11-26
  • 打赏
  • 举报
回复
想说的很多......,算了,多余的话不说
谢谢
zhangxiaommmm 2008-11-24
  • 打赏
  • 举报
回复
up
无条件为你 2008-11-23
  • 打赏
  • 举报
回复
顶贴。

路过——
zhangxiaommmm 2008-11-22
  • 打赏
  • 举报
回复
哪位帮解决一下 ,分不是问题.
dandycheung 2008-11-21
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 zhangxiaommmm 的回复:]
oldProc:=Pointer(GetWindowLong(Handle,GWL_WNDPROC));
oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc));
两句的作用应该是一样的吧?
都会得到前窗口过程.
[/Quote]

都能得到前窗口过程是没错,但是他们两个不一样。一个是单纯获取,一个是设置新的之后顺便把老的给你。

不要怪我说你,我感觉你自己好像没有什么钻研精神,老等着看别人的代码,这样不好。
zhangxiaommmm 2008-11-21
  • 打赏
  • 举报
回复
up
zhangxiaommmm 2008-11-21
  • 打赏
  • 举报
回复
dandycheung
我想让你帮我改一下代码,如果代码错的离谱,就帮我写一个贴上来.
因为,我觉得你在这方面很在行,所以开帖向你请教

如果你认为我不钻研,哪我只能说,弄了十多天,已经没心思了.

如果你没时间的写代码的话,哪就不麻烦你了.

zhangxiaommmm 2008-11-21
  • 打赏
  • 举报
回复
弄了太久时间了,我已经无能为力了
如果能的话,就不发帖子了.
zhangxiaommmm 2008-11-20
  • 打赏
  • 举报
回复
oldProc:=Pointer(GetWindowLong(Handle,GWL_WNDPROC));
oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc));
两句的作用应该是一样的吧?
都会得到前窗口过程.
CaiBirdy 2008-11-20
  • 打赏
  • 举报
回复
oldProc:=Pointer(GetWindowLong(Handle,GWL_WNDPROC));
zhangxiaommmm 2008-11-20
  • 打赏
  • 举报
回复
WindowFromPoint,我用它取到确实是发送按钮的句柄,我用spy++比对过
zhangxiaommmm 2008-11-20
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 dandycheung 的回复:]
我看了你代码,有很多问题,实在说不上你自己把钩子和子类化搞明白了。钩子给你的信息里分明就有消息的目标窗口,你怎么还需要调用什么 WindowFromPoint?这个函数只能返回顶级窗口,不能返回子窗口的,而你的发送按钮肯定是个子窗口(如果是个窗口的话)。

而且最大的问题是:你在子类化的时候,竟然没有判断之前是不是已经子类化过了,这样的结果是,收到第二条 WM_COMMAND 消息你又会子类化一次,然后 oldWndProc 就会变成…
[/Quote]

麻烦你给一下正确的代码?
dandycheung 2008-11-20
  • 打赏
  • 举报
回复
我看了你代码,有很多问题,实在说不上你自己把钩子和子类化搞明白了。钩子给你的信息里分明就有消息的目标窗口,你怎么还需要调用什么 WindowFromPoint?这个函数只能返回顶级窗口,不能返回子窗口的,而你的发送按钮肯定是个子窗口(如果是个窗口的话)。

而且最大的问题是:你在子类化的时候,竟然没有判断之前是不是已经子类化过了,这样的结果是,收到第二条 WM_COMMAND 消息你又会子类化一次,然后 oldWndProc 就会变成 WindowProc 自己,然后在 WindowProc 里面的 CallWindowProc 就会递归调用回来,如此反复递归,导致对栈溢出,QQ 不退出才怪。
zhangxiaommmm 2008-11-20
  • 打赏
  • 举报
回复
up
zhangxiaommmm 2008-11-20
  • 打赏
  • 举报
回复
渴望看到正确的代码!
无条件为你 2008-11-20
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 dandycheung 的回复:]
子类化说起来好像很玄妙,其实很简单。这个技术的目标就是,假设你要改变一个已经存在的、具有确定行为的窗口的行为,就使用子类化技术来完成。其实际的动作,就是调用 SetWindowLong 把目标窗口的回调函数修改掉,用你自己的函数接管。

处理 WM_COMMAND 消息(当然要分辨是不是真正来自于“发送”按钮,可以用 ID 来识别)…
[/Quote]

说得好,受益非浅!

处理 WM_COMMAND 消息的时候,如何分辨是正真来自于“发送”按扭?以ID识别是什么意思 ?

我发现用cwp := PCwpStruct(lParam); 而得到的cwp^.HWND句柄不对,取到的是该按扭的父句柄,而不是当前按扭句柄。楼主用WindowFromPoint是取当前鼠标位置的句柄,这样如果是用鼠标单击该按扭而激发WM_COMMAND消息,那么肯定取到的句柄是对的。但这种写法显示很不爽,还不如直接用鼠标钩子。最主要的BUG是如果该按扭不是用鼠标点击的而激发WM_COMMAND 消息,用WindowFromPoint取句柄根本就是错误的。

如何取到WM_COMMAND消息时对应的按扭句柄?是不是菜单的单击也会有WM_COMMAND 消息?
zhangxiaommmm 2008-11-19
  • 打赏
  • 举报
回复
哪位大侠对子类化熟悉的,帮看看代码哪里错了?
okmnji79513 2008-11-19
  • 打赏
  • 举报
回复
关注 帮顶 学习
加载更多回复(8)

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧