我在網上找到一段代碼,可以實現全局的hooking。它分兩個部分: DLL 和 主程序 hook.exe 原來的代碼是用於keyboard hooking的,但我修改了它,使得可以接收其它message。下面是代碼// DLL 部分
library HookDll;
uses
  SysUtils,
  Classes,windows, messages;var CurrentHook: HHook;function GlobalKeyBoardHook(code: integer; wParam: word; lParam: longword): longword; stdcall;
begin
    if code<0 then
    begin
       GlobalKeyBoardHook:=CallNextHookEx(CurrentHook,code,wParam,lparam); //then return the value from it.
       Exit;
    end;    // 這裡我們判斷message是否WM_INPUTLANGCHANGE,若是則代表輸入法已改變    
    if (PMSG(wParam).message = WM_INPUTLANGCHANGE) then
    begin
      MessageBox(0, 'global input changed', '', 0)
    end;
    CallNextHookEx(CurrentHook,code,wParam,lparam);
    GlobalKeyBoardHook:=0;
    Exit;
end;procedure SetHookHandle(HookHandle: HHook); stdcall;
begin
    CurrentHook:=HookHandle;
    KeyArrayPtr:=0;
end;exports GlobalKeyBoardHook index 1,
        SetHookHandle index 2;
beginend.然後是主程序,包含 local.pas 及 global.pas// local.pasunit Local;interfaceuses windows,sysutils, messages;function SetupLocalHook: boolean;
function RemoveLocalHook: boolean;
function KeyBoardHook(code: integer; wParam: word; lParam: longword): longword; stdcall;var CurrentHook: HHook;
    HookInstalled: boolean;
    
implementationfunction SetupLocalHook: boolean;
begin
  CurrentHook:=setwindowshookex(WH_GETMESSAGE,@KeyBoardHook,0,GetCurrentThreadID());
  if CurrentHook<>0  then SetupLocalHook:=true else SetupLocalHook:=false;
end;function RemoveLocalHook: boolean;
begin
    RemoveLocalHook:=UnhookWindowsHookEx(CurrentHook);
end;function KeyBoardHook(code: integer; wParam: word; lParam: longword): longword; stdcall;
begin
    if code<0 then
    begin
       KeyBoardHook := CallNextHookEx(CurrentHook,code,wParam,lparam);
       Exit;
    end;
    // 對於 local hooking (就是衹hook 在本application上),也是檢測是否收到WM_INPUTLANGCHANGE
    if (PMSG(lParam).message=WM_INPUTLANGCHANGE) then
      MessageBox(0, 'LANG CHANGED', '', 0);
    CallNextHookEx(CurrentHook,code,wParam,lparam);
    KeyBoardHook:=0;
    Exit;
end;end.
// global.pasunit Global;interfaceuses windows,Local;type TSetHookHandle = procedure(HookHandle: HHook); stdcall;var LibLoaded: boolean;
    LibHandle: HInst;
    HookProcAdd: pointer;
    GHookInstalled: boolean;
    SetHookHandle: TSetHookHandle;function LoadHookProc: boolean;
function SetupGlobalHook: boolean;implementationfunction LoadHookProc: boolean;
begin
    LibHandle:=LoadLibrary('hookdll.dll');
    if LibHandle=0 then begin
       LoadHookProc:=false;
       exit;
    end;
    HookProcAdd:=GetProcAddress(LibHandle,'GlobalKeyBoardHook');
    @SetHookHandle:=GetProcAddress(LibHandle,'SetHookHandle');
    if (HookProcAdd=nil)or(@SetHookHandle=nil) then begin //if loading fails, unload library, exit and return false
       FreeLibrary(LibHandle);
       LoadHookProc:=false;
       exit;
    end;
    LoadHookProc:=true;
end;
function SetupGlobalHook: boolean;
begin
     SetupGlobalHook:=false;
     if LibLoaded=false then LibLoaded:=LoadHookProc;
     if LibLoaded=false then exit;
     // 這裡,我們將hook設成可以接收MESSAGE的類型
     CurrentHook:=setwindowshookex(WH_GETMESSAGE,HookProcAdd,LibHandle,0);
     SetHookHandle(CurrentHook);
     if CurrentHook<>0  then SetupGlobalHook:=true;
end;end.
// 主程序unit MainFormUnit;interfaceuses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Local, global,
  StdCtrls;type
  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  MainForm: TMainForm;implementation{$R *.DFM}procedure TMainForm.FormCreate(Sender: TObject);
begin
  // 建立全局hook
  GHookInstalled:=SetupGlobalHook;
end;procedure TMainForm.FormDestroy(Sender: TObject);
begin
   RemoveLocalHook;
end;end.
實驗結果表明,如果截取 WM_INPUTLANGCHANGE 是不成功的,但如果換成 WM_INPUTLANGCHANGEREQUEST 則可以,但是 WM_INPUTLANGCHANGEREQUEST 是在輸入法轉換前發出的,我要的是輸入法成功轉換後的消息,請問怎樣做才可以?thanks

解决方案 »

  1.   


    不對吧,WM_IME_COMPOSITION 是當輸入法編碼狀態改變時而發出的吧,如果衹是用ALT+SHIFT改變keyboard layout,會觸發這個消息?我試了一下,好像不行哦
      

  2.   

    你截获的消息应该是没错的,不过这句错了if (PMSG(lParam).message=WM_INPUTLANGCHANGE) then
    改为if (wParam=WM_INPUTLANGCHANGE) then接收消息类型的是wParam不是lParam,其实消息就是一个整型的数字,所以也不用强制类型转换直接用就行了
      

  3.   


    但我用的是 WH_GETMESSAGE hook 哦,在 WINAPI的說明中,它的callback function 是這樣的LRESULT CALLBACK GetMsgProc(    int code, // hook code
        WPARAM wParam, // removal flag
        LPARAM lParam  // address of structure with message
       );wParam 不對應相應的 message,消息結構在 lParam 中。當然我也實驗過,用 if (wParam=WM_INPUTLANGCHANGE)then ...是不成的。真奇怪,我的代碼可以截到 WM_INPUTLANGCHANGEREQUEST 就是截不了 WM_INPUTLANGCHANGE
      

  4.   

    我重新看了一下书,你的做法应该是对的,本来我想试一下你的代码的,不过你贴出来的代码不全.我自己做了个测试,WM_INPUTLANGCHANGEREQUEST,WM_INPUTLANGCHANGE这两个消息在切换输入法的时候是一起触发的,先产生INPUTLANGCHANGEREQUEST,再产生WM_INPUTLANGCHANGE,这个MSDN里面也有说明的.
    When the DefWindowProc function receives the WM_INPUTLANGCHANGEREQUEST message, it activates the new input locale and notifies the application of the change by sending the WM_INPUTLANGCHANGE message. unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        Edit1: TEdit;
        procedure FormCreate(Sender: TObject);
      private
        OldWndProc: TWndMethod;
      protected
        procedure NewWndProc(var Message: TMessage);
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.NewWndProc(var Message: TMessage);
    begin
      if Message.Msg = WM_INPUTLANGCHANGEREQUEST then
      begin
        ShowMessage('WM_INPUTLANGCHANGEREQUEST');
      end;
      if Message.Msg = WM_INPUTLANGCHANGE then
      begin
        ShowMessage('WM_INPUTLANGCHANGE');
      end;
      OldWndProc(Message);
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      OldWndProc := Edit1.WindowProc;
      Edit1.WindowProc := NewWndProc;
    end;end.
      

  5.   

    謝謝你的回复。是的,你這樣寫的沒問題,我也測試過,但是,你這樣是本地消息監控而矣,它衹能截取當前 application 的消息。而我的問題是全域消息監控,要截取系統中所有窗體的WM_INPUTLANGCHANGE消息,即是不管系統上哪個窗體切換了輸入法,我都想把它截取出來,所以我才寫成DLL的形式。另外,我上面貼的內代碼己經齊全的,應該可以編譯的,我想把文件上傳的,可惜這裡無法貼附件。
      

  6.   

    今天才看到这个问题
    用WH_CALLWNDPROC 钩子就可以。