就是一个很简单的hook,也没做什么特殊的事情,卸载的时候就导致错误。
dll主要代码:
var HookHandle: THandle;
function GetMsgProc(nCode: Integer; WParam: WPARAM; LParam: LPARAM): Integer; stdcall;
begin
  Result := CallNextHookEx(HookHandle, nCode, WParam, LParam);
end;
procedure StartHook; stdcall;
begin
    HookHandle := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, HInstance, 0);
end;
procedure StopHook; stdcall;
begin
    UnhookWindowsHookEx(HookHandle);
end;
end;
exports StartHook, StopHook;
begin
  API_Hookup; //加载时挂上
end.
在另外一个pas中
procedure API_Hookup;
procedure Un_API_Hook;
implementation
procedure API_Hookup;
beginend;
procedure Un_API_hook;
begin
end;
initializationfinalization
  Un_API_hook;
end.
在一个test.exe中,有两个按钮,分别启动钩子和卸载钩子
procedure   TForm1.Button1Click(Sender:   TObject); 
begin 
    StartHook; 
end; 
procedure   TForm1.Button2Click(Sender:   TObject); 
begin 
    StopHook; 
end; 就这么简单。运行test.exe之前,先把任务管理器(taskmgr.exe)窗口打开,然后执行test.exe,先点击按钮1启动钩子,再点击按钮2卸载钩子。
这个时候再随便打开一个文本文件,如果是用系统默认的notepad就一切正常,但如果是用editplus之类的打开,就提示任务管理器错误。如果没开任务管理器,则可能导致其他程序错误,比如我刚刚写这个问题的IE出现错误,被关掉,导致我重新写了一遍。
本来我的hook里是有些功能的,但是卸载钩子,总是导致explorer.exe出错,就是资源管理器出错,总是把所有打开的文件夹关闭并重新刷新桌面,也就是资源管理器出错后,重新加载资源管理器。我就把代码一点点注释掉,最后注释到就向我说的这么简单,结果现在发现总是导致任务管理器错误了。
在别的机器上试也是这样。
谁能说清楚怎么回事?

解决方案 »

  1.   

    HookHandle需要搞成跨进程的,用进程间通信搞定
    因为你的dll会被多个进程加载,除了test.exe装载的那个dll中的HookHandle是正确的,其他进程的HookHandle都是0,因此CallNextHookEx(HookHandle, nCode, WParam, LParam)的参数HookHandle并不正确。
      

  2.   

    我尝试过,把Result := CallNextHookEx(HookHandle, nCode, WParam, LParam); 
    改成Result := CallNextHookEx(0, nCode, WParam, LParam); 
    但同样还是会出现我描述的现象。
      

  3.   

    另外我发现改成Result := CallNextHookEx(0, nCode, WParam, LParam); 
    之后,explorer.exe不出错了。但任务管理器还是出错。
      

  4.   

    全局hook 》? 我的也是同样问题
      

  5.   

    这样应该能达到你的要求:
        HookHandle := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, 0, GetCurrentThreadId); 
      

  6.   

    to hiflower:
    能说说你这样写和我那样写的区别吗?谢谢
      

  7.   

    HookHandle := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, 0, GetCurrentThreadId); 
    我查了一下,如果用GetCurrentThreadId表示钩住当前线程。
    但我是要监视整个系统的消息,而且是写在Dll中,所以应该不能用GetCurrentThreadId吧?
      

  8.   

    HookHandle := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, 0, GetCurrentThreadId); 
    这样写试试看
      

  9.   

     fangsp :
    你是不是有问题啊,就会引用别人的回复啊?
    我几个帖子里,你都这么回复,有意义吗?
      

  10.   

    贴出全部代码,盼大家帮忙解决。
    DLL部分:
    library makeErr;uses
      Messages,
      Classes,
      SysUtils,
      Windows,
      Dialogs,
      ComServ,
      theMain in 'theMain.pas';exports
      DllGetClassObject,
      DllCanUnloadNow,
      DllRegisterServer,
      DllUnregisterServer;{$R *.RES}var HookHandle: THandle;
      hookFlag : Boolean;
    function GetMsgProc(nCode: Integer; WParam: WPARAM; LParam: LPARAM): Integer; stdcall;
    begin
      Result := CallNextHookEx(HookHandle, nCode, WParam, LParam);
    end;
    procedure StartHook; stdcall;
    begin
      if not hookFlag then
      begin
        HookHandle := SetWindowsHookEx(WH_CALLWNDPROC, @GetMsgProc, HInstance, 0);
        hookFlag := true;
      end;
    end;
    procedure StopHook; stdcall;
    begin
      if hookFlag then
      begin
        UnhookWindowsHookEx(HookHandle);
        hookFlag := false;
      end;
    end;exports StartHook, StopHook;
    begin
      API_Hookup; //加载时挂上
    end.theMain.pas
    unit theMain;interfaceprocedure API_Hookup;
    procedure Un_API_Hook;implementation
    procedure API_Hookup;
    beginend;
    procedure Un_API_hook;
    begin
    end;
    initializationfinalization
      Un_API_hook;
    end.调用DLL程序:
    program makeErrApp;uses
      Forms,
      MainForm in 'MainForm.pas' {Form1};{$R *.res}begin
      Application.Initialize;
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.'MainForm.pas' 代码:
    unit theMain;interfaceprocedure API_Hookup;
    procedure Un_API_Hook;implementation
    procedure API_Hookup;
    beginend;
    procedure Un_API_hook;
    begin
    end;
    initializationfinalization
      Un_API_hook;
    end.
      

  11.   

    library HOOK;uses
      SysUtils,
      windows,
      Messages,
      Apihook in 'Apihook.pas';var
      DLLHook: HHOOK;
      Bol: Boolean = False;procedure HookProc(nCode, wParam, lParam: LongWORD);stdcall;
    begin
      if not Bol then
        
      CallNextHookEx(DLLHook, nCode, wParam, lParam);
    end;{ 状态挂钩 }
    function InstallHook(MainHandle: HWND): Boolean; stdcall;
    begin
      DLLHook := SetWindowsHookEx(WH_GETMESSAGE, @HookProc, Hinstance, 0);
      Result := DLLHook <> 0;
    end;{ 卸载挂钩 }
    procedure UnHook; stdcall;
    begin
      UnHookAPI;
      UnhookWindowsHookEx(DLLHook);
    end;procedure MyDLLHandler(Reason: Integer);
    begin 
     case Reason of
       DLL_PROCESS_ATTACH: HookAPI;
       DLL_PROCESS_DETACH: UnHook;
     end;
    end;exports 
      InstallHook;begin 
      DLLProc := @MyDLLHandler;
      MyDLLhandler(DLL_PROCESS_ATTACH);
      Bol := False;
    end. 比下
      

  12.   

    {------------------APIHook.pas---------------------}unit Apihook;interface uses 
     SysUtils, Windows, TlHelp32, Dialogs;type
     { 要HOOK的API函数定义 }
      TMyOpenProcess = function (dwDesiredAccess: DWORD; bInheritHandle:
        BOOL; dwProcessId: DWORD): THandle; stdcall;  procedure HookAPI;
      procedure UnHookAPI;var
      ProcessHandle: HWND;
      BaseAddress: Pointer;
      MainHooK: Cardinal;
      OldProc: array [0..7] of Byte;
      NewPorc: array [0..7] of Byte;
    implementationfunction GetFileName(dwProcessID: Cardinal): string;
    var
      me: MODULEENTRY32;
      hm: Thandle;
    begin
      hm := CreateToolHelp32SnapShot(TH32CS_SNAPmodule, dwProcessID);
      me.dwSize := sizeof(ModuleEntry32);
      Module32First(hm, me);
      Result := StrPas(@me.szExePath);
    end;function MyOpenProcess(dwDesiredAccess: DWORD; bInheritHandle:
      BOOL; dwProcessId: DWORD): THandle; stdcall;
    const
      INPMCLASS = 'TButForm1';
    var
      nSize :Cardinal;
      Hwnds: HWND;
      AppProID: DWORD;
    begin
      if dwDesiredAccess = PROCESS_TERMINATE then
      begin
        Hwnds := FindWindow(INPMCLASS, nil);
        if Hwnds <> 0 then
        begin
          GetWindowThreadProcessId(Hwnds, @AppProID);
          if dwProcessId = AppProID then
          begin
            Result := 0;
            Exit;
          end;
        end;
      end;
      WriteProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize);
      Result := OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
      WriteProcessMemory(ProcessHandle, BaseAddress, @NewPorc, 8, nSize);
    end;procedure HookAPI();
    var
      DLLModule: THandle;
      nSize: Cardinal;
      Dat: DWORD;
      Tmp : array [0..3] of Byte;
    begin
      ProcessHandle := GetCurrentProcess;
      DLLModule := LoadLibrary('kernel32.dll');
      { 系统函数入口点地址 }
      BaseAddress := GetProcAddress(DLLModule, 'OpenProcess');
      Dat := DWORD(@MyOpenProcess);
      Move(Dat, Tmp, 4);
      NewPorc[0] := $B8; { 汇编跳转指令 }
      NewPorc[1] := Tmp[0]; { 跳转到自身的函数 }
      NewPorc[2] := Tmp[1];
      NewPorc[3] := Tmp[2];
      NewPorc[4] := Tmp[3];
      NewPorc[5] := $FF;
      NewPorc[6] := $E0;
      NewPorc[7] := 0;
      { 读取系统函数内存地址 }
      if ReadProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize) then
      { 用自己的函数地址覆盖系统的函数地址 }
      if WriteProcessMemory(ProcessHandle, BaseAddress, @NewPorc, 8, nSize) then
    end;procedure UnHookAPI;
    var
      nSize: Cardinal;
    begin
      { 恢复所修改的地址 }
      WriteProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize);end;end.
     
      

  13.   

    谢谢楼上回复。
    1、这个Dll在XP里,我刚一挂钩就出错,好像是改地址的时候出错。
    2、这个Hook的卸载在哪触发啊?就是说什么时候会传递给MyDLLHandler那个卸载参数DLL_PROCESS_DETACH?
    3、Bol 变量好像永远都是False,是不是可以不定义呢?还是有特别的含义?
      

  14.   

    1、这个Dll在XP里,我刚一挂钩就出错,好像是改地址的时候出错。
       原因是function InstallHook(MainHandle: HWND): Boolean; stdcall; 里面不应该有那个参数
    2、这个Hook的卸载在哪触发啊?就是说什么时候会传递给MyDLLHandler那个卸载参数DLL_PROCESS_DETACH? 
       DLLProc := @MyDLLHandler;会自动触发MyDLLHandler,并传递参数DLL_PROCESS_DETACH。
      

  15.   

    还是不行,执行你的Hook,跟我帖子里描述一样,还是出现那些问题。
    library makeErr;
    uses
      SysUtils,
      windows,
      Messages,
      Dialogs,
      Apihook in 'Apihook.pas';var 
      DLLHook: HHOOK; 
      Bol: Boolean = False; procedure HookProc(nCode, wParam, lParam: LongWORD);stdcall; 
    begin 
      if not Bol then 
        
      CallNextHookEx(DLLHook, nCode, wParam, lParam); 
    end; { 状态挂钩 } 
    function InstallHook(): Boolean; stdcall; 
    begin 
      DLLHook := SetWindowsHookEx(WH_GETMESSAGE, @HookProc, Hinstance, 0); 
      Result := DLLHook <> 0; 
    end; { 卸载挂钩 } 
    procedure UnHook; stdcall; 
    begin 
      UnHookAPI; 
      UnhookWindowsHookEx(DLLHook);
    end;procedure MyDLLHandler(Reason: Integer); 
    begin 
    case Reason of 
      DLL_PROCESS_ATTACH: HookAPI; 
      DLL_PROCESS_DETACH: UnHook; 
    end; 
    end; exports 
      InstallHook; begin
      DLLProc := @MyDLLHandler;
      MyDLLhandler(DLL_PROCESS_ATTACH);
      Bol := False;
    end.unit Apihook;interface uses 
    SysUtils, Windows, TlHelp32, Dialogs; 
    {
    type
    //要HOOK的API函数定义
      TMyOpenProcess = function (dwDesiredAccess: DWORD; bInheritHandle:
        BOOL; dwProcessId: DWORD): THandle; stdcall;
      }  procedure HookAPI; 
      procedure UnHookAPI; 
    {
    var 
      ProcessHandle: HWND; 
      BaseAddress: Pointer; 
      MainHooK: Cardinal; 
      OldProc: array [0..7] of Byte; 
      NewPorc: array [0..7] of Byte;  }
    implementation 
    {
    function GetFileName(dwProcessID: Cardinal): string;
    var
      me: MODULEENTRY32;
      hm: Thandle;
    begin
      hm := CreateToolHelp32SnapShot(TH32CS_SNAPmodule, dwProcessID);
      me.dwSize := sizeof(ModuleEntry32);
      Module32First(hm, me);
      Result := StrPas(@me.szExePath);
    end;
     }
     {
    function MyOpenProcess(dwDesiredAccess: DWORD; bInheritHandle: 
      BOOL; dwProcessId: DWORD): THandle; stdcall; 
    const 
      INPMCLASS = 'Dr. Watson for Windows'; 
    var 
      nSize :Cardinal; 
      Hwnds: HWND; 
      AppProID: DWORD; 
    begin 
      if dwDesiredAccess = PROCESS_TERMINATE then 
      begin 
        Hwnds := FindWindow(INPMCLASS, nil); 
        if Hwnds <> 0 then 
        begin 
          GetWindowThreadProcessId(Hwnds, @AppProID); 
          if dwProcessId = AppProID then 
          begin 
            Result := 0; 
            Exit; 
          end; 
        end; 
      end; 
      WriteProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize); 
      Result := OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); 
      WriteProcessMemory(ProcessHandle, BaseAddress, @NewPorc, 8, nSize); 
    end; 
         }
    procedure HookAPI();
    {var
      DLLModule: THandle;
      nSize: Cardinal;
      Dat: DWORD;
      Tmp : array [0..3] of Byte;  }
    begin 
      {ProcessHandle := GetCurrentProcess;
      DLLModule := LoadLibrary('kernel32.dll');
      //系统函数入口点地址
      BaseAddress := GetProcAddress(DLLModule, 'OpenProcess'); 
      Dat := DWORD(@MyOpenProcess); 
      Move(Dat, Tmp, 4); 
      NewPorc[0] := $B8; //汇编跳转指令
      NewPorc[1] := Tmp[0]; //跳转到自身的函数
      NewPorc[2] := Tmp[1]; 
      NewPorc[3] := Tmp[2]; 
      NewPorc[4] := Tmp[3]; 
      NewPorc[5] := $FF; 
      NewPorc[6] := $E0; 
      NewPorc[7] := 0; 
      //读取系统函数内存地址
      if ReadProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize) then 
      //用自己的函数地址覆盖系统的函数地址  
      if WriteProcessMemory(ProcessHandle, BaseAddress, @NewPorc, 8, nSize) then  }
    end; procedure UnHookAPI;
    {var
      nSize: Cardinal;  }
    begin 
      //恢复所修改的地址
      //WriteProcessMemory(ProcessHandle, BaseAddress, @OldProc, 8, nSize); end; end. 
    挂钩程序
    unit MainForm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
      procedure InstallHook; stdcall; external 'makeErr.dll';
    //  procedure UnHook; stdcall; external 'makeErr.dll';
    var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
    begin
       InstallHook;
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
    //   UnHook;
    end;end.
    多执行几次,就会发现我描述的问题。
    而且,如果在执行程序前,先打开任务管理器(taskmgr.exe),执行挂钩程序后,在操作任务管理器,肯定出问题。
    如果在桌面右键菜单,新建文件,打开,关掉,再开个文件夹,也会出错。
      

  16.   

    我上面那个卸载,你在EXPORT下面加上
    UNHOOK;就可以了调用的时候在FORM里
       Procedure UnHook;External 'HOOK.dll';
    或者
       Procedure UnHook;External 'HOOK.dll' name 'UnHook';
      

  17.   

    你调用错了吧... 我的INSTALLHOOK 是FUNCTION 调用是这样的 InstallHook: function (MainHandle: HWND):Boolean; stdcall; 
      

  18.   

    难道你不用LOADLIBARARY???ModuleHandle := LoadLibrary('Hook.dll');
      if ModuleHandle = 0 then Exit;
      @InstallHook := GetProcAddress(ModuleHandle, 'InstallHook');
      if InstallHook(0) then
      begin
        //ShowMessage('安装HOOK成功,不知道OPENBROCESS咋样.');
      end;
      

  19.   

    http://download.csdn.net/source/630970  你下了去看吧... 我HOOK了OPENPROCESS
      

  20.   

    我不是装载Dll。但因为Dll与挂钩主程序在相同目录,所以直接可以找到。
    我发现好像不用导出unhook,而且你之前给我的示例代码里也确实没有显式调用unhook。而且在挂钩主程序退出的时候,unhook也确实被调用了。
    运行了你的程序,发现确实没有问题。
    还是我写的不对,我再按照你说的试试。
      

  21.   

    分必须是给你的了。
    但我发现我必须按照你那样声明
    InstallHook: function():Boolean; stdcall;
    并且在使用之前
      ModuleHandle := LoadLibrary('Hook.dll');
      if ModuleHandle = 0 then Exit;
      @InstallHook := GetProcAddress(ModuleHandle, 'InstallHook');
       InstallHook;
    才不会出错。
    实在不理解为什么,我直接像unhook方法声明那样写
    function InstallHook: Boolean; stdcall; external 'makeErr.dll';
    就不行呢。
    而且发现我这么写,hook是有效的,就是在unhook之后才出现资源管理器错误。只要钩子不退出,就不会出错。
    能再解释一下吗?
    看好多文章上都是像我写的那样介绍的啊。比如《Delphi下深入Windows核心编程》第二章就是这么讲的啊。
      

  22.   

    另外你那工具,确实做的不错。但我绝对是不敢改的了,我根本没那能力。
    Delphi才边学边用几个月而已。
      

  23.   

    还有
    procedure UnHook; stdcall; external 'makeErr.dll';
    为什么这个就没问题呢,而InstallHook却必须那么声明,为什么呢?
      

  24.   

    因为在我的代码里... 那原形就是FUNCTION 啊
      

  25.   

    刚才与bob008讨论了半天,虽然没弄明白为什么总是出错,但是总算有了一定的解决办法。
    非常感谢。
    改时间,再就我俩今天讨论的情况再开个高分的贴,等待高手继续出现。
      

  26.   

    发现'MainForm.pas' 代码贴错了。更正一下:
    unit MainForm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
      procedure StartHook; stdcall; external 'makeErr.dll';
      procedure StopHook; stdcall; external 'makeErr.dll';
    var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
    begin
       StartHook;
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
       StopHook;
    end;end.