挂钩线程创建/终止

gab*_*abr 6 windows hook multithreading

是否有可能挂钩到Windows上的线程终止?IOW,如果进程内的线程(对其他进程及其线程不感兴趣)已经终止(通常或者 - 更重要 - 强制),我希望得到通知.

或者,挂钩创建线程也可以.

基本原理:我有一个库,可以在每个线程的基础上管理一些信息(将其视为某些信息的进程范围的每线程缓存).线程终止时,我必须从缓存中删除所有特定于线程的信息.[缓存关联使用线程ID实现,可能会为将来的线程重用.]

"正常"执行顺序没有问题,因为库用户将从库中分离当前线程以清除状态.如果有人杀死拥有缓存资源的线程,问题就会开始出现.

Pab*_*abo 6

最好的方法是使用线程的HANDLE调用WaitForSingleObject(使用线程id调用OpenThread来获取HANDLE).


mdm*_*dma 5

如果您的程序在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.


RRU*_*RUZ 5

您可以使用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)


Chr*_*ich 4

您可以使用Detours之类的工具对 Win32 API(例如TerminateThread.

不过,我不明白为什么你需要这样做。听起来您需要在线程终止时清除该线程的关联缓存,以便在另一个具有相同 ID 的线程出现时可以重新使用该插槽。它是否正确?

如果是这样,你不能在DllMain收到DLL_THREAD_ATTACH事件时清除缓存关联吗?这本质上是您的新线程通知。此时,您知道您有一个新线程,那么清除现有的关联缓存不是安全的吗?

另一种可能有效的替代方案是线程本地存储(TLS)。TlsAlloc您可以使用/等 Win32 APITlsSetValue来存储特定于线程的信息。您还可以定义一个变量,__declspec(thread)让编译器为您管理 TLS。这样,每个线程都维护自己的缓存。每个线程的代码保持相同,但数据访问是相对于线程的。