gab*_*abr 6 windows hook multithreading
是否有可能挂钩到Windows上的线程终止?IOW,如果进程内的线程(对其他进程及其线程不感兴趣)已经终止(通常或者 - 更重要 - 强制),我希望得到通知.
或者,挂钩创建线程也可以.
基本原理:我有一个库,可以在每个线程的基础上管理一些信息(将其视为某些信息的进程范围的每线程缓存).线程终止时,我必须从缓存中删除所有特定于线程的信息.[缓存关联使用线程ID实现,可能会为将来的线程重用.]
"正常"执行顺序没有问题,因为库用户将从库中分离当前线程以清除状态.如果有人杀死拥有缓存资源的线程,问题就会开始出现.
如果您的程序在dll中,则可以设置为处理DllMain方法.当线程或进程开始/结束时调用此方法.
例如,
library MyDLL;
uses
SysUtils, Windows;
procedure DllMain(reason: integer) ;
var
dyingThreadId: Cardinal;
begin
case reason of
DLL_THREAD_DETACH:
begin
dyingThreadId := GetCurrentThreadId();
// handle thread exit with thread id
end;
end;
end;
begin
DllProc := @DllMain;
end.
Run Code Online (Sandbox Code Playgroud)
编辑:调用是在退出线程的上下文中进行的,因此您可以调用GetCurrentThreadId()以获取线程的ID.
您可以使用Win32_ThreadStopTraceWMI事件来检测系统中任何线程的终止.
要开始监控此事件,您必须编写这样的WQL句子
Select * from Win32_ThreadStopTrace Within 1 Where ProcessID=PID_Of_Your_App
Run Code Online (Sandbox Code Playgroud)
检查这个样本
uses
Classes;
type
TProcWmiEventThreadeCallBack = procedure(AObject: OleVariant) of object;
TWmiEventThread = class(TThread)
private
Success : HResult;
FSWbemLocator: OleVariant;
FWMIService : OleVariant;
FEventSource : OleVariant;
FWbemObject : OleVariant;
FCallBack : TProcWmiEventThreadeCallBack;
FWQL : string;
FServer : string;
FUser : string;
FPassword : string;
FNameSpace : string;
TimeoutMs : Integer;
procedure RunCallBack;
public
Constructor Create(CallBack : TProcWmiEventThreadeCallBack;const Server,User,PassWord,NameSpace,WQL:string;iTimeoutMs : Integer); overload;
destructor Destroy; override;
procedure Execute; override;
end;
implementation
uses
SysUtils,
ComObj,
Variants,
ActiveX;
constructor TWmiEventThread.Create(CallBack : TProcWmiEventThreadeCallBack;const Server,User,PassWord,NameSpace,WQL:string;iTimeoutMs : Integer);
begin
inherited Create(False);
FreeOnTerminate := True;
FCallBack := CallBack;
FWQL := WQL;
FServer := Server;
FUser := User;
FPassword := PassWord;
FNameSpace := NameSpace;
TimeoutMs := iTimeoutMs;
end;
destructor TWmiEventThread.Destroy;
begin
FSWbemLocator:=Unassigned;
FWMIService :=Unassigned;
FEventSource :=Unassigned;
FWbemObject :=Unassigned;
inherited;
end;
procedure TWmiEventThread.Execute;
const
wbemErrTimedout = $80043001;
begin
Success := CoInitialize(nil); //CoInitializeEx(nil, COINIT_MULTITHREADED);
try
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer(FServer, FNameSpace, FUser, FPassword);
FEventSource := FWMIService.ExecNotificationQuery(FWQL);
while not Terminated do
begin
try
FWbemObject := FEventSource.NextEvent(TimeoutMs); //set the max time to wait (ms)
except
on E:EOleException do
if EOleException(E).ErrorCode=HRESULT(wbemErrTimedout) then //Check for the timeout exception and ignore if exist
FWbemObject:=Null
else
raise;
end;
if FindVarData(FWbemObject)^.VType <> varNull then
Synchronize(RunCallBack);
FWbemObject:=Unassigned;
end;
finally
case Success of
S_OK, S_FALSE: CoUninitialize;
end;
end;
end;
procedure TWmiEventThread.RunCallBack;
begin
FCallBack(FWbemObject);
end;
Run Code Online (Sandbox Code Playgroud)
现在要在您的应用程序中使用此线程,您必须以这种方式调用它
WmiThread:=TWmiEventThread.Create(
Log,
'.',
'',
'',
'root\cimv2',
Format('Select * from Win32_ThreadStopTrace Within 1 Where ProcessID=%d',[GetCurrentProcessId]),1);
Run Code Online (Sandbox Code Playgroud)
并在回调函数中
procedure TForm1.Log(AObject: OleVariant);
begin
{
The OleVariant parameter has these properties
uint32 ProcessID;
uint8 SECURITY_DESCRIPTOR[];
uint32 ThreadID;
uint64 TIME_CREATED;
}
//do your stuff here
Memo1.Lines.Add(Format('Thread %s terminated ',[AObject.ThreadID]));
end;
Run Code Online (Sandbox Code Playgroud)
您可以使用Detours之类的工具对 Win32 API(例如TerminateThread.
不过,我不明白为什么你需要这样做。听起来您需要在线程终止时清除该线程的关联缓存,以便在另一个具有相同 ID 的线程出现时可以重新使用该插槽。它是否正确?
如果是这样,你不能在DllMain收到DLL_THREAD_ATTACH事件时清除缓存关联吗?这本质上是您的新线程通知。此时,您知道您有一个新线程,那么清除现有的关联缓存不是安全的吗?
另一种可能有效的替代方案是线程本地存储(TLS)。TlsAlloc您可以使用/等 Win32 APITlsSetValue来存储特定于线程的信息。您还可以定义一个变量,__declspec(thread)让编译器为您管理 TLS。这样,每个线程都维护自己的缓存。每个线程的代码保持相同,但数据访问是相对于线程的。
| 归档时间: |
|
| 查看次数: |
7440 次 |
| 最近记录: |