Создание/завершение потока перехвата

Можно ли подключиться к завершению потока в Windows? IOW, я хотел бы получать уведомления, если поток внутри процесса (не заинтересованный в других процессах и их потоках) завершился (либо нормально, либо, что более важно, принудительно).

В качестве альтернативы можно также подключиться к созданию потока.

Обоснование: у меня есть библиотека, которая управляет некоторой информацией для каждого потока (подумайте об этом как о кэше для каждого потока в масштабе всего процесса для некоторой информации). Когда поток завершается, я должен удалить всю информацию, относящуюся к потоку, из кеша. [Связи с кэшем реализованы с использованием идентификатора потока, который может повторно использоваться для будущих потоков.]

Нет проблем с «нормальным» порядком выполнения, поскольку пользователь библиотеки отключит текущий поток от библиотеки, что очистит состояние. Проблемы начинают появляться, если кто-то убивает поток, владеющий кэшированным ресурсом.


person gabr    schedule 27.09.2010    source источник


Ответы (9)


Вы можете использовать что-то вроде Detours для перехвата API Win32 на уровне API, например TerminateThread.

Однако я не понимаю, зачем вам это нужно. Похоже, вам нужно очистить кеш, связанный с потоком, когда поток умирает, чтобы вы могли повторно использовать этот слот, если появится другой поток с тем же идентификатором. Это правильно?

Если да, то не могли бы вы просто очистить ассоциацию кеша в DllMain при получении события DLL_THREAD_ATTACH? По сути, это уведомление о вашем новом потоке. На данный момент вы знаете, что у вас есть новый поток, так что не безопасно ли очистить существующий связанный кеш?

Другая альтернатива, которая может работать, — это локальное хранилище потока (TLS). Вы можете использовать Win32 API, такие как TlsAlloc/< a href="http://msdn.microsoft.com/en-us/library/ms686818%28VS.85%29.aspx" rel="nofollow">TlsSetValue для хранения информации, относящейся к потоку. Вы также можете определить переменную с __declspec(thread) для попросите компилятор управлять TLS за вас. Таким образом, каждый поток поддерживает свой собственный кеш. Код остается одинаковым для каждого потока, но доступ к данным осуществляется относительно потока.

person Chris Schmich    schedule 27.09.2010
comment
DLL_THREAD_ATTACH будет работать, если рассматриваемая библиотека будет реализована как DLL, но это не так. Перехват TerminateThread возможен, согласитесь. - person gabr; 27.09.2010

Лучший способ — вызвать WaitForSingleObject с HANDLE потока (вызвать OpenThread, используя идентификатор потока, чтобы получить HANDLE).

person Pablo Yabo    schedule 15.11.2010
comment
Действительно очень интересно. Будет ли такое событие срабатывать только при завершении потока или в других ситуациях (какими бы они ни были)? - person rustyx; 10.01.2016

Если ваша программа находится в 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.

РЕДАКТИРОВАТЬ: вызов выполняется в контексте выходящего потока, поэтому вы можете вызвать GetCurrentThreadId(), чтобы получить идентификатор потока.

person mdma    schedule 09.04.2011
comment
Извините, это неправильно. Он вызывается только тогда, когда процесс или поток, который загружает dll, запускается или завершается. Если каждый поток в приложении не загрузит эту DLL, она не будет вызвана (и если DLL может быть загружена каждым потоком, есть лучшие способы получать уведомления об их завершении). - person Ken White; 09.04.2011
comment
Кен, ты неправильно понял. Это стандартный Windows API - метод вызывается, когда какой-либо поток выходит из процесса, в который загружена dll. dll загружается в процесс, а не в поток, поэтому, как только процесс загружает dllMain вызывается для каждого выходящего потока. См. msdn.microsoft.com/en-us/ library/ms682583(v=vs.85).aspx, в частности часть DLL_THREAD_DETATCH. - person mdma; 09.04.2011
comment
@mdma: я так не думаю. См. первое предложение связанной темы (выделено мной): необязательная точка входа в библиотеку динамической компоновки (DLL). Когда система запускает или завершает процесс или поток, она вызывает функцию точки входа для каждой загруженной DLL, используя первый поток процесса. Обратите внимание на первую нить в этом предложении. Я не уверен, как это может быть более ясно, но я был бы готов признать, что был неправ, если бы у вас было что-то, что демонстрирует обратное... - person Ken White; 09.04.2011
comment
Документация менялась с годами. Теперь не так ясно. Хотя это прямо не написано, это означает DLL_PROCESS_ATTACH, который вызывается из первого потока процесса. Согласен, текст неясен, но смысл именно в этом — DLL_THREAD_DETACH вызывается для каждого потока, выходящего из процесса. В противном случае, какая разница между отсоединением потока и процессом? Пожалуйста, попробуйте, если вы мне не верите. Я закодировал много DLL-клеев, которые используют определенные функции. - person mdma; 09.04.2011
comment
@mdma: я не думаю, что это отвечает на вопрос Мэйсона (поскольку он не предоставляет информацию, которую он ищет, а именно ThreadID выходящего потока). Тем не менее, я удаляю свой отрицательный голос, потому что, очень с технической точки зрения, DLLMain действительно вызывается, как вы сказали (но с использованием первого потока, как я уже упоминал в своем комментарии) . Таким образом, очень с технической точки зрения, вы не ошиблись. Тем не менее, не думаю, что это ответ на заданный вопрос :) - просто не заслуживает отрицательного голосования. - person Ken White; 09.04.2011
comment
@ken - я ответил на вопрос. вызов выполняется в контексте существующего потока, поэтому вы можете получить идентификатор потока. многие реализации локальных переменных потока полагаются на эту функцию. - person mdma; 09.04.2011
comment
@mdma: я вижу это. :) Хорошая работа - вы отменили голосование против и сделали голосование за. Впервые я сделал это. - person Ken White; 09.04.2011
comment
Спасибо, хотя я лишь немного изменил свой ответ - большее изменение произошло в вашем понимании. Конечно, недоразумения случаются. Мне удалось заставить вас отменить отрицательный голос только потому, что я потратил время на защиту своего ответа. Вы говорите, что это первый раз, когда вы отменяете голосование - я ценю это - но, возможно, есть другие, за которых вы проголосовали, кто прав, но не стал бы тратить время на защиту своих ответов. Я надеюсь, что в будущем вы будете голосовать против только тогда, когда вы полностью уверены в чем-то и имеете эмпирический опыт, подтверждающий это. Таким образом, SO станет более счастливым местом для всех. - person mdma; 09.04.2011
comment
Помимо частных флеймов, это действительно не ответ, потому что я не работаю внутри DLL. - person Mason Wheeler; 09.04.2011
comment
? «Огненные войны? Я вижу обсуждение ответа. Где пламя? - person Ken White; 09.04.2011
comment
+1 за конструктивное обсуждение и за решение. @ Мейсон, пойми подсказку mdma: Цитата: I've coded many "glue" dlls that use specific feature.. Напишите крошечную DLL, которая перезванивает вам при выходе из потока. - person Cosmin Prund; 09.04.2011

Вы можете использовать событие WMI Win32_ThreadStopTrace для обнаружения завершения любого потока в системе. .

Чтобы начать отслеживать это событие, вы должны написать WQLпредложение, подобное этому

Select * from Win32_ThreadStopTrace Within 1 Where ProcessID=PID_Of_Your_App

проверить этот образец

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;

Теперь, чтобы использовать этот поток в своем приложении, вы должны вызвать его таким образом.

WmiThread:=TWmiEventThread.Create(
  Log,
  '.',
  '',
  '',
  'root\cimv2',
  Format('Select * from Win32_ThreadStopTrace Within 1 Where ProcessID=%d',[GetCurrentProcessId]),1);

и в функции обратного вызова

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;
person RRUZ    schedule 09.04.2011
comment
+1 за еще один ответ WMI от RRUZ. Является ли WMI разумным решением в этом случае? Не слишком ли накладные расходы для такой низкоуровневой работы? Примечание. Я использую только название «Инструментарий управления Windows», и оно не похоже на то, что его можно использовать, например, для освобождения переменных потока. - person Cosmin Prund; 09.04.2011
comment
@Cosmin, я знаю, что com-объект не очень часто используется для задач низкого уровня, таких как мониторинг потоков и процессов. с другой стороны, WMI в основном узнает системную информацию, например, об установленном оборудовании, и думает подобным образом, но это лишь малая часть его возможностей. лично я написал много приложений для мониторинга удаленных систем, успешно использующих WMI. с помощью WMI вы можете обнаружить запуск потоков, процессов, аппаратных сбоев, подключения или отключения нового оборудования и т. д. в качестве ответа на ваш вопрос Is WMI a reasonable solution in this case? Да, это так. - person RRUZ; 09.04.2011

program ThreadExitHook;

{$APPTYPE CONSOLE}

uses
  Windows,
  Classes,
  madCodeHook;

type
  TLdrShutdownThread = procedure; stdcall;

var
  LdrShutdownThreadNext : TLdrShutdownThread;

procedure LdrShutdownThreadCallback; stdcall;
begin
  WriteLn('Thread terminating:', GetCurrentThreadId);
  LdrShutdownThreadNext;
end;

begin
  HookAPI('ntdll.dll', 'LdrShutdownThread', @LdrShutdownThreadCallback, @LdrShutdownThreadNext);

  TThread.CreateAnonymousThread(procedure begin
    WriteLn('Hello from Thread');
    Sleep(1000);
  end).Start;

  ReadLn;

  UnhookAPI(@LdrShutdownThreadNext);
end.

Вот версия, которая не зависит ни от какой внешней библиотеки:

program Project7;

{$APPTYPE CONSOLE}

uses
  Windows,
  Classes;

{==============================================================================}
function IsWin9x: Boolean;
asm
  MOV     EAX, FS:[030H]
  TEST    EAX, EAX
  SETS    AL
end;
{------------------------------------------------------------------------------}
function CalcJump(Src, Dest: DWORD): DWORD;
begin
  if (Dest < Src) then begin
    Result := Src - Dest;
    Result := $FFFFFFFF - Result;
    Result := Result - 4;
  end else begin
    Result := Dest - Src;
    Result := Result - 5;
  end;
end;
{------------------------------------------------------------------------------}
function OpCodeLength(Address: DWORD): DWORD; cdecl; assembler;
const
  O_UNIQUE = 0;
  O_PREFIX = 1;
  O_IMM8 = 2;
  O_IMM16 = 3;
  O_IMM24 = 4;
  O_IMM32 = 5;
  O_IMM48 = 6;
  O_MODRM = 7;
  O_MODRM8 = 8;
  O_MODRM32 = 9;
  O_EXTENDED = 10;
  O_WEIRD = 11;
  O_ERROR = 12;
  asm
    pushad
    cld
    xor edx, edx
    mov esi, Address
    mov ebp, esp
    push    1097F71Ch
    push    0F71C6780h
    push    17389718h
    push    101CB718h
    push    17302C17h
    push    18173017h
    push    0F715F547h
    push    4C103748h
    push    272CE7F7h
    push    0F7AC6087h
    push    1C121C52h
    push    7C10871Ch
    push    201C701Ch
    push    4767602Bh
    push    20211011h
    push    40121625h
    push    82872022h
    push    47201220h
    push    13101419h
    push    18271013h
    push    28858260h
    push    15124045h
    push    5016A0C7h
    push    28191812h
    push    0F2401812h
    push    19154127h
    push    50F0F011h
    mov ecx, 15124710h
    push    ecx
    push    11151247h
    push    10111512h
    push    47101115h
    mov eax, 12472015h
    push    eax
    push    eax
    push    12471A10h
    add cl, 10h
    push    ecx
    sub cl, 20h
    push    ecx
    xor ecx, ecx
    dec ecx
  @@ps:
    inc  ecx
    mov  edi, esp
  @@go:
    lodsb
    mov  bh, al
  @@ft:
    mov  ah, [edi]
    inc  edi
    shr  ah, 4
    sub  al, ah
    jnc  @@ft
    mov al, [edi-1]
    and al, 0Fh
    cmp  al, O_ERROR
    jnz  @@i7
    pop edx
    not edx
  @@i7:
    inc edx
    cmp al, O_UNIQUE
    jz  @@t_exit
    cmp al, O_PREFIX
    jz  @@ps
    add  edi, 51h
    cmp  al, O_EXTENDED
    jz   @@go
    mov edi, [ebp+((1+8)*4)+4]
  @@i6:
    inc  edx
    cmp  al, O_IMM8
    jz   @@t_exit
    cmp  al, O_MODRM
    jz   @@t_modrm
    cmp  al, O_WEIRD
    jz   @@t_weird
  @@i5:
    inc  edx
    cmp  al, O_IMM16
    jz   @@t_exit
    cmp  al, O_MODRM8
    jz   @@t_modrm
  @@i4:
    inc  edx
    cmp  al, O_IMM24
    jz   @@t_exit
  @@i3:
    inc  edx
  @@i2:
    inc  edx
    pushad
    mov  al, 66h
    repnz scasb
    popad
    jnz  @@c32
  @@d2:
    dec  edx
    dec  edx
  @@c32:
    cmp  al, O_MODRM32
    jz   @@t_modrm
    sub  al, O_IMM32
    jz   @@t_imm32
  @@i1:
    inc  edx
  @@t_exit:
    jmp @@ASMEnded
  @@t_modrm:
    lodsb
    mov  ah, al
    shr  al, 7
    jb   @@prmk
    jz   @@prm
    add  dl, 4
    pushad
    mov  al, 67h
    repnz scasb
    popad
    jnz  @@prm
  @@d3:  sub  dl, 3
    dec  al
  @@prmk:jnz  @@t_exit
    inc  edx
    inc  eax
  @@prm:
    and  ah, 00000111b
    pushad
    mov  al, 67h
    repnz scasb
    popad
    jz   @@prm67chk
    cmp  ah, 04h
    jz   @@prmsib
    cmp  ah, 05h
    jnz  @@t_exit
  @@prm5chk:
    dec  al
    jz   @@t_exit
  @@i42: add  dl, 4
    jmp  @@t_exit
  @@prm67chk:
    cmp  ax, 0600h
    jnz  @@t_exit
    inc  edx
    jmp  @@i1
  @@prmsib:
    cmp  al, 00h
    jnz  @@i1
    lodsb
    and  al, 00000111b
    sub  al, 05h
    jnz  @@i1
    inc  edx
    jmp  @@i42
  @@t_weird:
    test byte ptr [esi], 00111000b
    jnz  @@t_modrm
    mov  al, O_MODRM8
    shr  bh, 1
    adc  al, 0
    jmp  @@i5
  @@t_imm32:
    sub  bh, 0A0h
    cmp  bh, 04h
    jae  @@d2
    pushad
    mov  al, 67h
    repnz scasb
    popad
    jnz  @@chk66t
  @@d4:  dec  edx
    dec  edx
  @@chk66t:
    pushad
    mov  al, 66h
    repnz scasb
    popad
    jz   @@i1
    jnz  @@d2
  @@ASMEnded:
    mov esp, ebp
    mov [result+(9*4)], edx
    popad
end;
{------------------------------------------------------------------------------}
function ApiHook(ModName, ApiName: PChar; FuncAddr, HookedApi: Pointer; var MainApi: Pointer): Boolean;
var
  dwCount, Cnt, i, jmp: DWORD;
  P: Pointer;
  hMod, OldP, TMP: Cardinal;
begin
  Result := False;
  if IsWin9x then
    Exit;
  P := FuncAddr;
  if P = nil then begin
    hMod := GetModuleHandle(ModName);
    if hMod = 0 then
      hMod := LoadLibrary(ModName);
    P := GetProcAddress(hMod, ApiName);
  end;
  if (P = nil) or (HookedApi = nil) then
    Exit;
  if not VirtualProtect(P, $40, PAGE_EXECUTE_READWRITE, @OldP) then
    Exit;
  if ((Byte(P^) = $68) and (DWORD(Pointer(DWORD(P) + 1)^) = DWORD(HookedApi))) then
    Exit;
  MainApi := VirtualAlloc(nil, $1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  if MainApi = nil then
    Exit;
  Cnt := 0;
  for dwCount := 0 to $3F do begin
    Inc(Cnt, OpCodeLength(DWORD(P) + Cnt));
    for i := 0 to Cnt - 1 do
      PByte(MainApi)[i] := PByte(P)[i];
    if Cnt > 5 then
      Break;
  end;
  PByte(MainApi)[Cnt] := $68;
  DWORD(Pointer(DWORD(MainApi) + Cnt + 1)^) := DWORD(P) + Cnt;
  PByte(MainApi)[Cnt + 5] := $C3;
  PByte(MainApi)[Cnt + 6] := $99;
  if (OpCodeLength(DWORD(MainApi)) = 5) and
    ((Byte(MainApi^) = $E8) or (Byte(MainApi^) = $E9)) then
  begin
    jmp := DWORD(P) + DWORD(Pointer(DWORD(MainApi) + 1)^) + 5;
    DWORD(Pointer(DWORD(MainApi) + 1)^) := CalcJump(DWORD(MainApi), jmp);
  end;
  PByte(P)[0] := $68;
  DWORD(Pointer(DWORD(P) + 1)^) := DWORD(HookedApi);
  PByte(P)[5] := $C3;
  VirtualProtect(P, $40, OldP, @TMP);
  Result := True;
end;
{------------------------------------------------------------------------------}
function ApiUnHook(ModName, ApiName: PChar; FuncAddr, HookedApi: Pointer; var MainApi: Pointer): Boolean;
var
  dwCount, Cnt, i, jmp: DWORD;
  P: Pointer;
  hMod, OldP, TMP: Cardinal;
begin
  Result := False;
  if IsWin9x then
    Exit;
  P := FuncAddr;
  if P = nil then begin
    hMod := GetModuleHandle(ModName);
    P := GetProcAddress(hMod, ApiName);
  end;
  if (P = nil) or (MainApi = nil) or (HookedApi = nil) then
    Exit;
  if not VirtualProtect(P, $40, PAGE_EXECUTE_READWRITE, @OldP) then
    Exit;
  if ((Byte(P^) <> $68) or (DWORD(Pointer(DWORD(P) + 1)^) <> DWORD(HookedApi))) then
    Exit;
  Cnt := 0;
  for dwCount := 0 to $3F do begin
    Inc(Cnt, OpCodeLength(DWORD(MainApi) + Cnt));
    if (Byte(Pointer(DWORD(MainApi) + Cnt)^) = $C3) and
      (Byte(Pointer(DWORD(MainApi) + Cnt + 1)^) = $99) then
      Break;
    for i := 0 to Cnt - 1 do
      PByte(P)[i] := PByte(MainApi)[i];
  end;
  if (OpCodeLength(DWORD(P)) = 5) and ((Byte(P^) = $E8) or (byte(P^) = $E9)) then begin
    jmp := DWORD(MainApi) + DWORD(Pointer(DWORD(MainApi) + 1)^) + 5;
    DWORD(Pointer(DWORD(P) + 1)^) := CalcJump(DWORD(P), jmp);
  end;
  VirtualProtect(P, $40, OldP, @TMP);
  VirtualFree(MainApi, 0, MEM_RELEASE);
  Result := True;
end;
{==============================================================================}

type
  TLdrShutdownThread = procedure; stdcall;

var
  LdrShutdownThreadNext : TLdrShutdownThread;

procedure LdrShutdownThreadCallback; stdcall;
begin
  WriteLn('Thread terminating:', GetCurrentThreadId);
  LdrShutdownThreadNext;
end;

begin
  ApiHook('ntdll.dll', 'LdrShutdownThread', nil, @LdrShutdownThreadCallback, @LdrShutdownThreadNext);

  TThread.CreateAnonymousThread(procedure begin
    WriteLn('Hello from Thread');
    Sleep(1000);
    WriteLn('Waking up');
  end).Start;

  ReadLn;

  ApiUnHook('ntdll.dll', 'LdrShutdownThread', nil, @LdrShutdownThreadCallback, @LdrShutdownThreadNext);

  TThread.CreateAnonymousThread(procedure begin
    WriteLn('Hello from Thread');
    Sleep(1000);
    WriteLn('Waking up');
  end).Start;

  ReadLn;
end.
person Thorsten Engler    schedule 09.04.2011
comment
Выглядит очень красиво. Есть ли способ сделать это без проприетарной библиотеки? - person Mason Wheeler; 09.04.2011
comment
Да, это немного сложнее, но выполнимо. По сути, вам нужно перезаписать первые пару байтов LdrShutdownThread с помощью инструкции перехода к вашему собственному коду, затем сделать все, что вы хотите сделать, и, наконец, сделать то, что сделали инструкции, которые вы перезаписали, и вернуться в исходный LdrShutdownThread после инструкции перехода. Проблема в том, что вы не можете просто сократить любые инструкции пополам, поэтому вам нужно разобрать исходный код LdrShutdownThread, чтобы убедиться, что вы скопировали несколько полных инструкций. Он запускается одинаково на всех версиях ОС, дальше становится проще... - person Thorsten Engler; 09.04.2011
comment
быстрый поиск находит это: sites.google.com/site/delphibasics/home/ delphibasicssnippets/ после беглого взгляда на код, это будет делать больше, чем вам нужно (он написан, чтобы иметь возможность внедрять dll и перехватывать API в другие процессы), поэтому вы сможете извлечь минимальное количество кода для этого вам нужно просто подключить API к вашему собственному процессу. - person Thorsten Engler; 09.04.2011
comment
Что ж, кажется, сам Джефф Этвуд объединил вопрос Мэйсона с вопросом Габра. Шаг, с которым я должен сказать, что я категорически не согласен, потому что эти два вопроса на самом деле совсем не одинаковы. - person Thorsten Engler; 09.04.2011
comment
@ThorstenEngler Можно ли подключить RtlUserThreadStart, BaseThreadInitThunk? Для запуска, инициализации? - person John Lewis; 09.01.2015

Упоминание Крисом DLL_THREAD_ATTACH навело меня на мысль...

По сути, связывание кеша с идентификатором потока — это плохо. Мне нужно переработать мою библиотеку, чтобы поток сначала устанавливал какой-то дескриптор, а затем управлял ассоциациями, используя этот дескриптор.

person gabr    schedule 27.09.2010

Я думаю, если вы действительно хотите этого достаточно сильно, вы можете использовать API отладки (например, WaitForDebugEvent, ContinueDebugEvent), . Вы получите EXIT_THREAD_DEBUG_EVENT при завершении потока.

Я не могу сказать, что это прямолинейный или чистый способ сделать это, но если вы не можете придумать ничего другого, это, вероятно, лучше, чем ничего.

person Jerry Coffin    schedule 09.04.2011


Единственный способ надежно сделать это — использовать DLL, которая перехватывает DLL_THREAD_ATTACH и DLL_THREAD_DETACH. См. предыдущее обсуждение здесь.

person Steve Townsend    schedule 27.09.2010