在qq的聊天窗口,当按下"发送"按钮之前,执行一段我的代码
希望能用全局消息钩子加子类化函数实现
WH_CALLWNDPROC setwindowslong
因为,用全局钩子消息,我已会我在学子类化函数的用法?

解决方案 »

  1.   

    子类化说起来好像很玄妙,其实很简单。这个技术的目标就是,假设你要改变一个已经存在的、具有确定行为的窗口的行为,就使用子类化技术来完成。其实际的动作,就是调用 SetWindowLong 把目标窗口的回调函数修改掉,用你自己的函数接管。这个技术本身没有任何难度,也没有什么神奇。真正的问题在于,你要仔细去了解你要修改的目标当前的行为,然后找到最合适的切入点来做你想做的事情。然而,你千万不要把子类化看作万能的灵丹妙药,从而希望它能够解决所有窗口相关的问题。由于你要子类化的窗口往往不是你自己实现的,或者系统本身有其他制约,那就需要考虑别的解决办法,或者多种方法相结合。拿你现在的需求来说。有两种情况:“发送”是一个单独的按钮窗口,或者是另一个大窗口的一个部分。如果是前者,你就可以直接子类化按钮窗口,处理它的鼠标按下消息;或者子类化它的父窗口,处理 WM_COMMAND 消息(当然要分辨是不是真正来自于“发送”按钮,可以用 ID 来识别)。如果是后一种情况,那你就需要子类化大窗口,处理鼠标点击消息,并检查该次点击是不是位于“发送”所在的区域内。这些都是见招拆招的自然想法,没什么高深的东西。希望对你有帮助。
      

  2.   

     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就自动关闭??
      

  3.   

    我看了你代码,有很多问题,实在说不上你自己把钩子和子类化搞明白了。钩子给你的信息里分明就有消息的目标窗口,你怎么还需要调用什么 WindowFromPoint?这个函数只能返回顶级窗口,不能返回子窗口的,而你的发送按钮肯定是个子窗口(如果是个窗口的话)。而且最大的问题是:你在子类化的时候,竟然没有判断之前是不是已经子类化过了,这样的结果是,收到第二条 WM_COMMAND 消息你又会子类化一次,然后 oldWndProc 就会变成 WindowProc 自己,然后在 WindowProc 里面的 CallWindowProc 就会递归调用回来,如此反复递归,导致对栈溢出,QQ 不退出才怪。
      

  4.   

    WindowFromPoint,我用它取到确实是发送按钮的句柄,我用spy++比对过
      

  5.   

    oldProc:=Pointer(GetWindowLong(Handle,GWL_WNDPROC));
      

  6.   

    oldProc:=Pointer(GetWindowLong(Handle,GWL_WNDPROC));
    oldWndProc := SetWindowLong(hwndListView, GWL_WNDPROC, Integer(@WindowProc)); 
    两句的作用应该是一样的吧?
    都会得到前窗口过程.
      

  7.   


    说得好,受益非浅!处理 WM_COMMAND 消息的时候,如何分辨是正真来自于“发送”按扭?以ID识别是什么意思 ?我发现用cwp := PCwpStruct(lParam); 而得到的cwp^.HWND句柄不对,取到的是该按扭的父句柄,而不是当前按扭句柄。楼主用WindowFromPoint是取当前鼠标位置的句柄,这样如果是用鼠标单击该按扭而激发WM_COMMAND消息,那么肯定取到的句柄是对的。但这种写法显示很不爽,还不如直接用鼠标钩子。最主要的BUG是如果该按扭不是用鼠标点击的而激发WM_COMMAND 消息,用WindowFromPoint取句柄根本就是错误的。如何取到WM_COMMAND消息时对应的按扭句柄?是不是菜单的单击也会有WM_COMMAND 消息?
      

  8.   

    dandycheung
    我想让你帮我改一下代码,如果代码错的离谱,就帮我写一个贴上来.
    因为,我觉得你在这方面很在行,所以开帖向你请教如果你认为我不钻研,哪我只能说,弄了十多天,已经没心思了.如果你没时间的写代码的话,哪就不麻烦你了.
      

  9.   


    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.