Multithreading 如何终止具有单独消息循环的线程?

Multithreading 如何终止具有单独消息循环的线程?,multithreading,delphi,delphi-2007,message-loop,Multithreading,Delphi,Delphi 2007,Message Loop,我正在为SetWindowsHookExAPI编写一个实用程序单元 要使用它,我希望有这样一个界面: var Thread: TKeyboardHookThread; begin Thread := TKeyboardHookThread.Create(SomeForm.Handle, SomeMessageNumber); try Thread.Resume; SomeForm.ShowModal; finally Thread.Free; // <

我正在为
SetWindowsHookEx
API编写一个实用程序单元

要使用它,我希望有这样一个界面:

var
  Thread: TKeyboardHookThread;
begin
  Thread := TKeyboardHookThread.Create(SomeForm.Handle, SomeMessageNumber);
  try
    Thread.Resume;
    SomeForm.ShowModal;
  finally
    Thread.Free; // <-- Application hangs here
  end;
end;
AFAICS只有在线程中存在消息循环时才会调用钩子过程。问题是我不知道如何正确退出此消息循环

我尝试使用一个属于线程的隐藏
TForm
,但消息循环不处理我发送到该表单的窗口句柄的消息

如何正确执行此操作,以便在线程关闭时终止消息循环

编辑:我现在使用的解决方案看起来像这样(并且工作起来很有魅力):

TKeyboardHookThread=class(TThread)
私有的
类变量
FCreated:布尔型;
FKeyReceiverWindowHandle:HWND;
F消息:红衣主教;
公众的
构造函数创建(AKeyReceiverWindowHandle:HWND;AMessage:Cardinal);
毁灭者毁灭;推翻
程序执行;推翻
结束;
函数HookProc(nCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
变量
S:KBDLLHOOKSTRUCT;
开始
如果nCode<0,则开始
结果:=CallNextHookEx(0,nCode,wParam,lParam)
结束,否则开始
S:=PKBDLLHOOKSTRUCT(lParam)^;
PostMessage(TKeyboardHookThread.fKeyReceiveWindowHandle,TKeyboardHookThread.FMessage,S.vkCode,0);
结果:=CallNextHookEx(0,nCode,wParam,lParam);
结束;
结束;
构造函数TKeyboardHookThread.Create(AKeyReceiverWindowHandle:HWND;
A消息:红衣主教);
开始
如果已创建TKeyboardHookThread.fc,则开始
引发异常。创建(“仅支持一个键盘挂钩”);
结束;
继承的Create('KeyboardHook',True);
FKEYReceiveWindowHandle:=AkeyReceiveWindowHandle;
FMessage:=AMessage;
TKeyboardHookThread.FCreated:=真;
结束;
析构函数TKeyboardHookThread.Destroy;
开始
PostThreadMessage(线程ID,WM_QUIT,0,0);
继承;
结束;
程序TKeyboardHookThread.Execute;
变量
m:tagMSG;
钩子:HHOOK;
开始
hook:=SetWindowsHookEx(WH_KEYBOARD_LL,@HookProc,HInstance,0);
尝试
当GetMessage(m,0,0,0)开始时
翻译信息(m);
发送消息(m);
结束;
最后
解开钩(钩);
结束;
结束;

您需要将WM_QUIT消息发送到该线程的消息队列以退出该线程。如果它从队列中提取的消息是WM_QUIT,则返回false,因此它将在收到该消息时退出循环

为此,使用函数将WM_QUIT消息直接发送到线程的消息队列。例如:

PostThreadMessage(Thread.Handle, WM_QUIT, 0, 0);

消息泵永远不会退出,因此当您释放线程时,它会无限期地阻塞,等待Execute方法完成。从线程调用PostQuitMessage以终止消息泵。如果您希望从主线程调用它,那么您需要向该线程发布一个WM_QUIT


此外,你隐藏的窗口是一场等待发生的灾难。不能在主线程外创建VCL对象。您必须使用原始Win32创建窗口句柄,或者更好地使用DsiAllocateHwnd。

该窗口只是“最后的手段”,我以前尝试过各种方法。我实际上并不需要一个窗口句柄。@JensMühlenhoff-但您的消息循环需要一个窗口句柄
DispatchMessage
将消息发送到窗口过程,而
DSiAllocateHWND
是创建此消息的最佳方法procedure@Serg实际上它不需要一个窗口把手,没有它一切都很好。消息循环的唯一目的是让Windows有机会调用钩子过程。有没有办法在没有消息循环的情况下使用Windows钩子?@JensMühlenhoff我不知道钩子,从来没有使用过。如果没有在线程中创建窗口(句柄),我看不出标准消息循环的用途,因为它所做的是将消息发送到窗口过程(使用窗口句柄查找目标窗口)。@Serg我在这里只是使用一个钩子来解决另一个问题,但这是另一回事。关于消息循环:
  TKeyboardHookThread = class(TThread)
  private
    class var
      FCreated                 : Boolean;
      FKeyReceiverWindowHandle : HWND;
      FMessage                 : Cardinal;
  public
    constructor Create(AKeyReceiverWindowHandle: HWND; AMessage: Cardinal);
    destructor Destroy; override;
    procedure Execute; override;
  end;

function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  S: KBDLLHOOKSTRUCT;
begin
  if nCode < 0 then begin
    Result := CallNextHookEx(0, nCode, wParam, lParam)
  end else begin
    S := PKBDLLHOOKSTRUCT(lParam)^;
    PostMessage(TKeyboardHookThread.FKeyReceiverWindowHandle, TKeyboardHookThread.FMessage, S.vkCode, 0);
    Result := CallNextHookEx(0, nCode, wParam, lParam);
  end;
end;

constructor TKeyboardHookThread.Create(AKeyReceiverWindowHandle: HWND;
  AMessage: Cardinal);
begin
  if TKeyboardHookThread.FCreated then begin
    raise Exception.Create('Only one keyboard hook supported');
  end;
  inherited Create('KeyboardHook', True);
  FKeyReceiverWindowHandle     := AKeyReceiverWindowHandle;
  FMessage                     := AMessage;
  TKeyboardHookThread.FCreated := True;
end;

destructor TKeyboardHookThread.Destroy;
begin
  PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
  inherited;
end;

procedure TKeyboardHookThread.Execute;
var
  m: tagMSG;
  hook: HHOOK;
begin
  hook := SetWindowsHookEx(WH_KEYBOARD_LL, @HookProc, HInstance, 0);
  try
    while GetMessage(m, 0, 0, 0) do begin
      TranslateMessage(m);
      DispatchMessage(m);
    end;
  finally
    UnhookWindowsHookEx(hook);
  end;
end;
PostThreadMessage(Thread.Handle, WM_QUIT, 0, 0);