下面是一段引发异常进入其他线程的示例代码,它使用SuspendThread停止线程,GetThreadContext读取线程寄存器,改变EIP(指令指针),使用SetThreadContext然后ResumeThread重新启动线程。 它可以正常工作!
UKilThread单元
一个精美打包的可重用单元,提供AbortThread()例程:
unit UKillThread;
interface
uses Classes, Windows, SysUtils;
procedure AbortThread(const Th: TThread);
implementation
type EThreadAbort = class(EAbort);
procedure RaizeThreadAbort;
begin
raise EThreadAbort.Create('Thread was aborted using AbortThread()');
end;
procedure AbortThread(const Th: TThread);
const AlignAt = SizeOf(DWORD);
var Block:array[0..SizeOf(_CONTEXT)+512] of Byte;
ThContext: PContext;
begin
SuspendThread(Th.Handle);
ZeroMemory(@Block, SizeOf(Block));
ThContext := PContext(((Integer(@Block) + AlignAt - 1) div AlignAt) * AlignAt);
ThContext.ContextFlags := CONTEXT_FULL;
if not GetThreadContext(Th.Handle, ThContext^) then
RaiseLastOSError;
ThContext.Eip := Cardinal(@RaizeThreadAbort);
SetThreadContext(Th.Handle, ThContext^);
ResumeThread(Th.Handle);
end;
end.
演示项目
以下是如何使用AbortThread
:
program Project23;
{$APPTYPE CONSOLE}
uses
SysUtils,
Classes,
Windows,
UKillThread;
var Th: TThread;
type
TTestThread = class(TThread)
public
procedure Execute;override;
end;
procedure TTestThread.Execute;
var N: Integer;
begin
try
N := 1;
while not Terminated do
begin
WriteLn(N);
Inc(N);
Sleep(1000);
end;
except on E:Exception do
WriteLn(E.ClassName + ' / ' + E.Message);
end;
end;
begin
Th := TTestThread.Create(False);
WriteLn('Press ENTER to raize exception in Thread');
ReadLn;
AbortThread(Th);
WriteLn('Press ENTER to exit');
ReadLn;
end.
免责声明
在您使用此代码之前,请确保您了解其功能。该代码并不是适当的 Terminate - Terminated
逻辑(即协作式线程关闭)的替代品,但它比使用 TerminateThread()
更好。此代码是基于.NET Thread.Abort() 方法建模而成。我不知道实际的 .NET 方法是如何实现的,但请仍然阅读一下,因为使用此代码可能会遇到类似的潜在问题:
- 该方法实际上并没有终止线程,而是在线程上下文中引发一个
EAbort
派生异常。线程的代码可能会捕获该异常。这很不可能发生,因为 EAbort
异常不应被处理。
- 该方法可能在任何时间停止线程。它可能会在线程处理
finally
部分或设置新异常帧时停止线程。即使您的线程使用适当的 try-finally
块,如果在分配资源给变量之前分配资源后引发异常,可能会导致内存或资源泄漏。
- 如果在线程进入临界区之后立即中断线程,而在通常情况下遵循的
try-finally
之前,则可能会导致死锁。 EnterCriticalSection 的 MSDN 页面 提到:“如果一个线程在拥有临界区所有权时终止,则临界区的状态是未定义的。” 这让我感到意外,我的直觉认为当拥有线程终止时,关键部分应该被“释放”,但显然不是这样。
OnTerminate
事件是否已分配其FatalException
。但是,如果您仍然想要通知主线程,则需要使用一些同步机制。 - user532231