How can I terminate a thread that has a seperate message loop?
I am writing a utility unit for the SetWindowsHookEx API.
To use it, I'd like to have an interface like this:
var
Thread: TKeyboardHookThread;
begin
Thread := TKeyboardHookThread.Create(SomeForm.Handle, SomeMessageNumber);
try
Thread.Resume;
SomeForm.ShowModal;
finally
Thread.Free; // <-- Application hangs here
end;
end;
In my current implementation of TKeyboardHookThread I am unable to make the thread exit correctly.
The code is:
TKeyboardHookThread = class(TThread)
private
class var
FCreated : Boolean;
FKeyReceiverWindowHandle : HWND;
FMessage : Cardinal;
FHiddenWindow : TForm;
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 < then begin
Result := CallNextHookEx(, nCode, wParam, lParam)
end else begin
S := PKBDLLHOOKSTRUCT(lParam)^;
PostMessage(TKeyboardHookThread.FKeyReceiverWindowHandle, TKeyboardHookThread.FMessage, S.vkCode, );
Result := CallNextHookEx(, 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
PostMessage(FHiddenWindow.Handle, WM_QUIT, , );
inherited;
end; procedure TKeyboardHookThread.Execute;
var
m: tagMSG;
hook: HHOOK;
begin
hook := SetWindowsHookEx(WH_KEYBOARD_LL, @HookProc, HInstance, );
try
FHiddenWindow := TForm.Create(nil);
try
while GetMessage(m, , , ) do begin
TranslateMessage(m);
DispatchMessage(m);
end;
finally
FHiddenWindow.Free;
end;
finally
UnhookWindowsHookEx(hook);
end;
end;
AFAICS the hook procedure only gets called when there is a message loop in the thread.
The problem is I don't know how to correctly exit this message loop.
I tried to do this using a hidden TForm that belongs to the thread,
but the message loop doesn't process messages I'm sending to the window handle of that form.
How to do this right, so that the message loop gets terminated on thread shutdown?
Edit: The solution I'm now using looks like this (and works like a charm):
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 < then begin
Result := CallNextHookEx(, nCode, wParam, lParam)
end else begin
S := PKBDLLHOOKSTRUCT(lParam)^;
PostMessage(TKeyboardHookThread.FKeyReceiverWindowHandle, TKeyboardHookThread.FMessage, S.vkCode, );
Result := CallNextHookEx(, 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, , );
inherited;
end; procedure TKeyboardHookThread.Execute;
var
m: tagMSG;
hook: HHOOK;
begin
hook := SetWindowsHookEx(WH_KEYBOARD_LL, @HookProc, HInstance, );
try
while GetMessage(m, , , ) do begin
TranslateMessage(m);
DispatchMessage(m);
end;
finally
UnhookWindowsHookEx(hook);
end;
end;
You need to send the WM_QUIT message to that thread's message queue to exit the thread.
GetMessage returns false if the message it pulls from the queue is WM_QUIT, so it will exit the loop on receiving that message.
To do this, use the PostThreadMessage function to send the WM_QUIT message directly to the thread's message queue.
For example:
PostThreadMessage(Thread.Handle, WM_QUIT, , );
The message pump never exits and so when you free the thread
it blocks indefinitely waiting for the Execute method to finish.
Call PostQuitMessage, from the thread, to terminate the message pump.
If you wish to invoke this from the main thread then you will need to
post a WM_QUIT to the thread.
Also, your hidden window is a disaster waiting to happen.
You can't create a VCL object outside the main thread.
You will have to create a window handle using raw Win32,
or even better, use DsiAllocateHwnd.
http://www.techques.com/question/1-10451535/How-to-exit-a-thread's-message-loop?
It turns out that it's better for everyone that you don't use a windowless message queue.
A lot of things can be unintentionally, and subtly, broken if you don't have a window for messages to be dispatched to.
Instead allocate hidden window (e.g. using Delphi's thread-unsafe AllocateHwnd)
and post messages to it using plain old PostMessage:
procedure TMyThread.Execute;
var
msg: TMsg;
begin
Fhwnd := AllocateHwnd(WindowProc);
if Fhwnd = then Exit;
try
while Longint(GetMessage(msg, , , )) > do // will block until a message arrives on the queue.
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
finally
DeallocateHwnd(Fhwnd);
Fhwnd := ;
end;
end;
Where we can have a plain old window procedure to handle the messages:
WM_TerminateYourself = WM_APP + ; procedure TMyThread.WindowProc(var msg: TMessage);
begin
case msg.Msg of
WM_ReadyATractorBeam: ReadyTractorBeam;
WM_TerminateYourself: PostQuitMessage(0);
else
msg.Result := DefWindowProc(Fhwnd, msg.msg, msg.wParam, msg.lParam);
end;
end;
and when you want the thread to finish, you tell it:
procedure TMyThread.Terminate;
begin
PostMessage(Fhwnd, WM_TerminateYourself, , );
end; PostThreadMessage( FThreadId, WM_QUIT, 0, 0 );
Using PostThreadMessage is not necessarily incorrect. Raymond's article that you linked to says:
PostQuitMessage(0)
Because the system tries not to inject a WM_QUIT message at a "bad time";
instead it waits for things to "settle down" before generating the WM_QUIT message,
thereby reducing the chances that the program might be in the middle of a multi-step
procedure triggered by a sequence of posted messages.
If the concerns outlined here do not apply to your message queue,
then call PostThreadMessage with WM_QUIT and knock yourself out.
Otherwise you'll need to create a special signal, i.e. a user-defined message,
that allows you to call PostQuitMessage from the thread.
How can I terminate a thread that has a seperate message loop?的更多相关文章
- Thread message loop for a thread with a hidden window? Make AllocateHwnd safe
Thread message loop for a thread with a hidden window? I have a Delphi 6 application that has a thre ...
- The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
好久没有冒泡了,最近在新环境上搭建应用时,启动报错: INFO: Illegal access: this web application instance has been stopped alre ...
- Jmeter:运行报:Error occurred starting thread group :线程组, error message:Invalid duration 0 set in Thread Group:线程组, see log file for more details
最近在用jmeter做压测,上周五压测的脚本,今天早晨结束后. 点击同样的脚本,运行就报Error occurred starting thread group :线程组, error message ...
- Await, and UI, and deadlocks! Oh my!
It’s been awesome seeing the level of interest developers have had for the Async CTP and how much us ...
- WaitForMultipleObject与MsgWaitForMultipleObjects用法
http://blog.csdn.net/byxdaz/article/details/5638680 用户模式的线程同步机制效率高,如果需要考虑线程同步问题,应该首先考虑用户模式的线程同步方法. 但 ...
- Android Guts: Intro to Loopers and Handlers
One of the reasons I love Android API is because it contains so many useful little things. Many of t ...
- Correct thread terminate and destroy
http://www.techques.com/question/1-3788743/Correct-thread-destroy Hello At my form I create TFrame a ...
- 多线程爬坑之路-Thread和Runable源码解析
多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...
- Worker Thread
http://www.codeproject.com/Articles/552/Using-Worker-Threads Introduction Worker threads are an eleg ...
随机推荐
- linux echo命令的-n、-e两个参数
echo -n 不换行输出 $echo -n "123" $echo "456" 最终输出 123456 而不是 123 456 echo -e 处理特殊字符 ...
- ubuntu下 apt-get install 下载的文件存放的目录
apt-get把下载的deb包全部放在/var/cache/apt/archives下面,该目录下的文件可以删除.当然用 sudo apt-get clean 命令也可以,这个命令只会删除缓存起来的d ...
- JSTL笔记(胖先生版)
今天系统的学习了一下jstl,来记录一下. 在学习jstl以前,先要引两个jar包,然后再加入标签: <%@ taglib prefix="c" uri="http ...
- ubuntu切换root
sudo passwd root这个命令是给root用户设定密码.然后su root切换到root用户.
- Supervisor行为分析和实践
1.简介 Erlang要编写高容错性.稳定性的系统,supervisor就是用来解决这一问题的核心思想.通过建立一颗监控树,来组织进程之间的关系,通过确定重启策略.子进程说明书等参数信息来确定 ...
- 用physdiskwrite在VMware虚拟机上安装m0n0wall【转】
在一台PC机上安装m0n0wall,相信大家都有经验.一般采用两种方法:1.在一台Windows XP或Windows 2000的PC上,下载physdiskwrite软件和m0n0wall映像文件( ...
- linux grub
grub 是目前使用最广泛的linux引导装载程序,旧的lilo这个引导装载程序很少见了,grub优点: 支持较多哦的文件系统,可以使用grub主程序在文件系统中查找内核文件名 启动的时候,可以自行编 ...
- Zookeeper Hello World
1.Zookeeper的安装使用 在官网上下载zk的安装包(http://labs.renren.com/apache-mirror/zookeeper/),解压后cd到zk的目录下. 单机版安装方法 ...
- 单源最短路径-Dijkstra算法
1.算法标签 贪心 2.算法描述 具体的算法描述网上有好多,我觉得莫过于直接wiki,只说明一些我之前比较迷惑的. 对于Dijkstra算法,最重要的是维护以下几个数据结构: 顶点集合S : 表示已经 ...
- flex之组件简单应用
1. 将父窗体的内容传递给子窗体: 2.将子窗体内容传递给父窗体:即用户名和密码,t1是父窗体文本框id.