Delphi SetWindowsHookEx内螺纹不稳定

Delphi SetWindowsHookEx内螺纹不稳定,delphi,hook,Delphi,Hook,好的,伙计们,我会尽量用一种非常完整的形式来解释我的问题。我正在使用一个DLL注入另一个进程(使用VirtualAllocEx/WriteProcessMemory/CreateRemoteThread注入,但这并不重要),在入口点中运行时,此DLL只有一个功能: procedure EntryPoint(Reason: integer); begin if Reason = DLL_PROCESS_ATTACH then begin MyMainThread.Creat

好的,伙计们,我会尽量用一种非常完整的形式来解释我的问题。我正在使用一个DLL注入另一个进程(使用VirtualAllocEx/WriteProcessMemory/CreateRemoteThread注入,但这并不重要),在入口点中运行时,此DLL只有一个功能:

procedure EntryPoint(Reason: integer);
begin
  if Reason = DLL_PROCESS_ATTACH then
    begin
      MyMainThread.Create;
    end
好的,我所有的工作都是在这个MyMainThread(TThread)中完成的。。。我在MyMainThread中所做的基本上是设置2个计时器,并使用SetWindowsHookEx(WH_keyboard_LL)钩住键盘事件。单独使用时一切正常,这是:或SetWindowsHookEx或2个计时器当我出于某种未知的原因将这两件事结合在一起时,钩子对键盘上键入的几个字符有效(少于10个),计时器只是停止,但MyMain线程不会终止。我在Windows 7/2008上的测试非常完美,但在Windows 2003中运行时问题就开始了。MyMainThread执行如下操作:

procedure MyMainThread.Execute;
begin
  while not Terminated do
    begin
      MyThread:= Self;
      StartKeyboardHook;
      StartUp;
      SetTimer(0, 0, 600000, @MyMainThread.ContactHome);
      SetTimer(0, 0, 40000, @MyMainThread.MapProc);
      CreateMessagePump;
    end;
end;
这两个计时器和“启动”可以通过Indy doing POST/GET请求联系一个php,列出正在运行的进程,以及类似的事情。。。StartKeyboardHook很简单,如下所示:

procedure MyMainThread.StartKeyboardHook;
begin
  if llKeyboardHook = 0 then
    llKeyboardHook:= SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKeyboardHook, HInstance, 0);
end;
正如您所看到的,这个StartKeyboardHook在MyMain线程中,而llKeyboardHook/LowLevelKeyboardHook是全局变量/方法。。。如果我把低水平的KeyboardHook程序放在线程中,就不要使用钩子。我相信问题不在我的低级键盘钩子(代码本身),因为正如我所说的,如果我不设置2个计时器,钩子工作得很好,但是如果你愿意,我可以在这里发布它。正如我所说,钩子是在线程内部启动的,但是回调过程和hhook变量是全局的(也许这就是问题所在)。。。CreateMessagePump(线程执行中的最后一个调用)过程对于计时器和钩子是必需的,因为它是低级钩子,我需要一个消息队列。为什么我会出现这种不稳定性(我的测试只在Win2k3中显示),如果我只放了没有计时器的键盘挂钩,或者只放了没有挂钩的计时器,一切都正常? 信息泵是:

procedure MyMainThread.CreateMessagePump;
var
  AppMsg: TMsg;
begin
  while GetMessage(AppMsg, 0, 0, 0) do
    begin
      TranslateMessage(AppMsg);
      DispatchMessage(AppMsg);
    end;
  //if needed to quit this procedure use PostQuitMessage(0);
end;

首先:不推荐使用全局变量联系线程。。 您正在将LowLevelKeyboardHook用作全局过程。您应该将LowLevelKeyboardHook声明到主线程中: 不幸的是,您不能将回调函数声明为对象(类、对象、线程等),因此您需要将LowLevel KeyboardHook作为类函数,当然它应该是静态函数。(或者您可以使用MakeObjectInstance函数从回调函数创建对象。):

现在您可以设置挂钩:

 llKeyboardHook := SetWindowsHookEx(WH_KEYBOARD_LL,
        @TMyMainThread.LowLevelKeyboardHook, HInstance, 0);
第二:线程的execute方法永远运行,当它每次调用SetTimer时都运行。您应该只调用SetTimer一次

procedure TMyMainThread.Execute;
begin
  while not Terminated do
    begin
    {Set FirstTime to true on TMyMainThread.Create}
      if FirstTime then
        begin
          FirstTime := False;
          SetTimer();
          ...
        end;
    end;
end;

首先:不推荐使用全局变量联系线程。。 您正在将LowLevelKeyboardHook用作全局过程。您应该将LowLevelKeyboardHook声明到主线程中: 不幸的是,您不能将回调函数声明为对象(类、对象、线程等),因此您需要将LowLevel KeyboardHook作为类函数,当然它应该是静态函数。(或者您可以使用MakeObjectInstance函数从回调函数创建对象。):

现在您可以设置挂钩:

 llKeyboardHook := SetWindowsHookEx(WH_KEYBOARD_LL,
        @TMyMainThread.LowLevelKeyboardHook, HInstance, 0);
第二:线程的execute方法永远运行,当它每次调用SetTimer时都运行。您应该只调用SetTimer一次

procedure TMyMainThread.Execute;
begin
  while not Terminated do
    begin
    {Set FirstTime to true on TMyMainThread.Create}
      if FirstTime then
        begin
          FirstTime := False;
          SetTimer();
          ...
        end;
    end;
end;

首先是一些非常笼统的建议

你问了一个问题,你以前的问题,关于如何组织线程的Execute方法。我当时给你的答案是准确的。你应该注意它

在您就这个主题提出的每个问题上,Sertac和其他人都告诉您Win32回调函数声明不匹配的问题。看来你没有听从建议。您继续使用RTL和@operator提供的中断API声明。在前面的问题中,Sertac向您展示了如何纠正RTL声明的失败。如果无法检查回调是否匹配,则必须让编译器进行检查

您在评论中说您尝试了Sertac的类型安全设置计时器,但它“不起作用”。这是一个错误的诊断。塞塔克的代码工作得很好。您收到的错误来自编译器检查回调声明是否正确。因为它不是,编译器停止了。这是理想的行为。您选择忽略编译器、抑制错误并继续执行中断的回调。正确的回答应该是修复您的回调

对你来说,不断地问问题、接受建议、不听劝告,然后一遍又一遍地问同样的问题是毫无意义的。如果你想取得进展,就必须听从这些建议。为什么还要问你是否愿意那样做


关于这里的细节,我看到两个主要问题:

  • 线程循环从不终止。使用我在上一个问题中给你的循环。与其只是复制它,不如试着理解它是如何工作的,以及为什么它是正确的

  • 计时器回调函数与所需签名不匹配。它们不能是实例方法(或类方法)。它们应该是在单元范围内声明的函数。它们必须是stdcall。参数列表必须匹配。由于您发现很难满足这些要求,因此最好使用前面问题中的Sertac代码,并让编译器强制执行类型安全


  • 首先是一些非常笼统的建议

    你问了一个问题,你以前的问题,关于如何组织线程的Execute方法。我当时给你的答案是准确的。你应该注意它

    在您就这个主题提出的每个问题上,Sertac和其他人都告诉您Win32回调函数声明不匹配的问题。看来你没有听从建议。您继续使用损坏的API d
    procedure TForm3.FTimerMethod(_hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    begin
      { Note : you can not access function params correctly }
      { & you should use Form3.ListBox2.Items to access the ListBox
        instead of ListBox2.Items .
      }
      {
        this Code will not work :
        ListBox2.Items.add(IntToStr(_hwnd)); !!!
    
      }
      Form3.ListBox2.Items.add(IntToStr(_hwnd));
    end;
    
    unit uTimer;
    {uTimer Unit by S.Mahdi}
    interface
    
    uses Windows;
    
    type
    
      TTimerStruct = record
        _hwnd: HWND;
        uMsg: UINT;
        idEvent: UINT_PTR;
        dwTime: DWORD;
    
      end;
    
    type
      TTimerMethod = procedure(var Message: TTimerStruct) of object;
    
    function MakeTimerObjectInstance(const AMethod: TTimerMethod): Pointer;
    procedure FreeTimerObjectInstance(ObjectInstance: Pointer);
    
    implementation
    
    type
      PObjectInstance = ^TObjectInstance;
    
      TObjectInstance = packed record
        Code: Byte;
        Offset: Integer;
        case Integer of
          0:
            (Next: PObjectInstance);
          1:
            (FMethod: TMethod);
      end;
    
    const
    {$IF Defined(CPUX86)}
      CodeBytes = 2;
    {$ELSEIF Defined(CPUX64)}
      CodeBytes = 8;
    {$ENDIF CPU}
      InstanceCount = (4096 - SizeOf(Pointer) * 2 - CodeBytes)
        div SizeOf(TObjectInstance) - 1;
    
    type
      PInstanceBlock = ^TInstanceBlock;
    
      TInstanceBlock = packed record
        Next: PInstanceBlock;
        Code: array [1 .. CodeBytes] of Byte;
        WndProcPtr: Pointer;
        Instances: array [0 .. InstanceCount] of TObjectInstance;
      end;
    
    var
      InstBlockList: PInstanceBlock;
      InstFreeList: PObjectInstance;
    
    function CalcJmpOffset(Src, Dest: Pointer): Longint;
    begin
      Result := IntPtr(Dest) - (IntPtr(Src) + 5);
    end;
    
    procedure StdTimerProc(_hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    var
      TimerStruct: TTimerStruct;
    {$IF Defined(CPUX86)}
      { In    ECX = Address of method pointer }
      asm
        PUSH EBX
        PUSH EDX
        MOV EBX,_hwnd
        XOR EDX,EDX
        LEA EDX,TimerStruct
        MOV [EDX].TTimerStruct._hwnd,EBX;
        MOV EBX,uMsg
        MOV [EDX].TTimerStruct.uMsg,EBX;
        MOV EBX,idEvent
        MOV [EDX].TTimerStruct.idEvent,EBX;
        MOV EBX,dwTime
        MOV [EDX].TTimerStruct.dwTime,EBX;
        PUSH EDX
        MOV     EAX,[ECX].Longint[4]
        CALL    [ECX].Pointer
        POP EDX
        POP EBX
        (* XOR     EAX,EAX
        PUSH    EAX
        PUSH    dwTime
        PUSH    idEvent
        PUSH    uMsg
        PUSH    _hwnd
        MOV     EDX,ESP
        MOV     EAX,[ECX].Longint[4]
        CALL    [ECX].Pointer
        ADD     ESP,16
        POP     EAX *)
    end;
    {$ELSEIF Defined(CPUX64)}
      { In    R11 = Address of method pointer }
      asm
        .PARAMS 1
        MOV TimerStruct._hwnd,_hwnd;
        MOV TimerStruct.uMsg,uMsg;
        MOV TimerStruct.idEvent,idEvent;
        MOV TimerStruct.dwTime,dwTime;
        LEA RDX,TimerStruct
        PUSH RCX
        PUSH R11
        MOV     RCX,[R11].TMethod.Data
        CALL    [R11].TMethod.Code
        POP R11
        POP RCX
    end;
    {$ENDIF CPUX64}
    
    function MakeTimerObjectInstance(const AMethod: TTimerMethod): Pointer;
    const
      BlockCode: array [1 .. CodeBytes] of Byte = (
    {$IF Defined(CPUX86)}
        $59, { POP ECX }
        $E9); { JMP StdTimerProc }
    {$ELSEIF Defined(CPUX64)}
        $41, $5B, { POP R11 }
        $FF, $25, $00, $00, $00, $00)
      ; { JMP [RIP+0] }
    {$ENDIF}
      PageSize = 4096;
    var
      Block: PInstanceBlock;
      Instance: PObjectInstance;
    begin
      if InstFreeList = nil then
        begin
          Block := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
          Block^.Next := InstBlockList;
          Move(BlockCode, Block^.Code, SizeOf(BlockCode));
    {$IF Defined(CPUX86)}
          Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],
            @StdTimerProc));
    {$ELSEIF Defined(CPUX64)}
          Block^.WndProcPtr := @StdTimerProc;
    {$ENDIF}
          Instance := @Block^.Instances;
          repeat
            Instance^.Code := $E8; { CALL NEAR PTR Offset }
            Instance^.Offset := CalcJmpOffset(Instance, @Block^.Code);
            Instance^.Next := InstFreeList;
            InstFreeList := Instance;
            Inc(PByte(Instance), SizeOf(TObjectInstance));
          until IntPtr(Instance) - IntPtr(Block) >= SizeOf(TInstanceBlock);
          InstBlockList := Block;
        end;
      Result := InstFreeList;
      Instance := InstFreeList;
      InstFreeList := Instance^.Next;
      Instance^.FMethod := TMethod(AMethod);
    end;
    
    procedure FreeTimerObjectInstance(ObjectInstance: Pointer);
    begin
      if ObjectInstance <> nil then
        begin
          PObjectInstance(ObjectInstance)^.Next := InstFreeList;
          InstFreeList := ObjectInstance;
        end;
    end;
    
    end.
    
    unit uMain;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
      uTimer;
    
    type
      TForm3 = class(TForm)
        Button1: TButton;
        ListBox1: TListBox;
        Button2: TButton;
        ListBox2: TListBox;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
        IDTimer, IDTimer2: DWORD;
        FObj: Pointer;
        procedure FTimerMethod(_hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
          dwTime: DWORD); stdcall;
    
        procedure FTimerMethod2(var Message: TTimerStruct);
      public
        { Public declarations }
      end;
    
    var
      Form3: TForm3;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm3.Button2Click(Sender: TObject);
    begin
      KillTimer(Handle, IDTimer);
      FreeTimerObjectInstance(FObj);
    end;
    
    procedure TForm3.FTimerMethod(_hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    begin
      { Note : you can not access function params correctly }
      { & you should use Form3.ListBox2.Items to access the ListBox
        instead of ListBox2.Items .
      }
      {
        this Code will not work :
        ListBox2.Items.add(IntToStr(_hwnd)); !!!
    
      }
      Form3.ListBox2.Items.add(IntToStr(_hwnd));
    end;
    
    procedure TForm3.FTimerMethod2(var Message: TTimerStruct);
    begin
      ListBox1.Items.add(IntToStr(Message._hwnd));
    end;
    
    procedure TForm3.Button1Click(Sender: TObject);
    begin
      ReportMemoryLeaksOnShutdown := True;
      FObj := MakeTimerObjectInstance(FTimerMethod2);
      IDTimer := SetTimer(Handle, 0, 1000, FObj);
      IDTimer2 := SetTimer(Handle, 1, 1000, @TForm3.FTimerMethod);
    
    end;
    
    end.