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 ...
随机推荐
- jquery easyui datagrid字段绑定问题
表字段为G_XIAN,生成PO对象时为private String GXian; datagrid字段必须写为gXian,否则数据无法正确绑定. 总结:不管VO对象中字段名称首字母是否大写,在data ...
- 百度地图Api详解之地图标注
标注概述 标注(Marker)是用来表示一个点位置的可见元素,每个标注自身都包含地理信息.比如你在西单商场位置添加了一个标注,不论地图移动.缩放,标注都会跟随一起移动,保证其始终指向正确的地理位置. ...
- C# GDI+学习笔记1
前言 本文是学习C# GDI+系列的第一篇文章,简单的介绍了GDI+的一些基本绘图内容,比较粗糙.但本文主要是让大家简单的回顾一下GDI+的基本概念.本篇文章的参考代码请在此下载 . GDIPTes ...
- HDU5800 To My Girlfriend 背包计数dp
分析:首先定义状态dp[i][j][s1][s2]代表前i个物品中,选若干个物品,总价值为j 其中s1个物品时必选,s2物品必不选的方案数 那么转移的时候可以考虑,第i个物品是可选可可不选的 dp[i ...
- IOS-简单计时器的使用
1. 使用NSTimer *_timer --游戏计时器 2. NSDate *_startTime -- 开始时间 3. 初始化计时器 _timer = [NSTimer scheduledTime ...
- 静态成员变量.xml
pre{ line-height:1; color:#1e1e1e; background-color:#f0f0f0; font-size:16px;}.sysFunc{color:#627cf6; ...
- 分段统计与Oracle的分析函数、逻辑判断等知识点的综合运用
重点部分:TOTAL层 项目要求: 统计每个巡检员(USER_ID)当前月的签到率及查询相关字段 签到率公式:以巡检员为单位, (当月至今天为止签到的所有点/该月巡检点的总个数)=(b.Point/a ...
- 把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend
//把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend //dp[i][j]:把第i个数转成第j小的数,最小花费 //此题与po ...
- 15+ 易响应的CSS框架快速开启你的敏捷网站项目
由 于移动互联用户的快速增加,现在数量已经超出10亿,几乎可以肯定的是你的网站每天都会有移动用户访问.如果你组织计划创建一个对移动用户友好的浏览体 验,有多个方面需要考虑.响应快速的网站设计似乎现在很 ...
- WPF 简介
简介 一. WPF产生的背景 因为人们的生活水平不断提前,审美观也随着提升,而软件的应用发展水平目前无法赶上大家的审美观和使用要求:比如:像电影中的软件能够方便的使用,而且有动态的效果同时附加形象 ...