est-il possible de connecter la terminaison de fil sur Windows? Je voudrais être informé si un fil à l'intérieur du processus (non intéressé par d'autres processus et de leurs threads) s'est terminé (normalement ou - plus important - avec force). P>
Alternativement, l'accrochage dans la création de fil ferait également. P>
Justification: J'ai une bibliothèque qui gère des informations sur la base de thread (y penser comme un cache par thread à l'échelle du processus pour certaines informations). Lorsqu'un thread est terminé, je dois supprimer toutes les informations spécifiques à thread du cache. [Les associations de cache sont implémentées à l'aide d'un identifiant de thread qui peut être réutilisé pour les futurs threads.] P>
Il n'y a pas de problème avec l'ordre d'exécution "normal" car l'utilisateur de la bibliothèque détachera le fil actuel de la bibliothèque qui effacera l'état. Les problèmes commencent à apparaître si quelqu'un tue le fil de la ressource mise en cache. p>
9 Réponses :
Vous pouvez utiliser quelque chose comme détours pour accrocher au niveau de l'API de Win32 apis comme Je ne vois pas pourquoi vous devez faire cela, cependant. On dirait que vous avez besoin d'effacer le cache associé du fil lorsque le thread meurt de sorte que vous pouvez réutiliser cette fente si un autre thread avec le même identifiant vient. Est-ce correct? P>
Si tel est le cas, ne pourriez-vous pas simplement effacer l'association de cache dans L'autre alternative qui pourrait fonctionner est thread-local Stockage (TLS). Vous pouvez utiliser Win32 API comme TerminaTéthread code> a>. p>
dllmain code> lorsque vous obtenez le
dll_thread_attach code> événement? C'est essentiellement votre nouvelle notification de fil. À ce stade, vous savez que vous avez un nouveau thread, alors n'est-il pas sûr d'effacer le cache associé existant? P>
tlsalloc code >
/ tlssetvalue code >
pour stocker des informations spécifiques à thread. Vous pouvez également définir une variable avec __ DeclSpec (fil ) code>
pour que le compilateur gère le TLS pour vous. De cette façon, chaque fil maintient son propre cache. Le code reste le même pour chaque thread, mais les accès des données sont par rapport au fil. P>
Dll_thread_attach fonctionnerait si la bibliothèque en question a été mise en œuvre comme une DLL, mais ce n'est pas le cas. La terminaison d'accrochage est une possibilité, convenue.
Chris 'Mention de dll_thread_attach m'a donné une idée ... p>
Fondamentalement, l'association de cache avec un identifiant de thread est une mauvaise chose. Je dois retravailler ma bibliothèque afin qu'un thread établisse initialement une sorte de manipulation puis gérera ensuite des associations à l'aide de cette poignée. p>
boost fournit Boost :: this_thread :: at_ththread_exit () code>
qui vous permet de fournir un code arbitraire à exécuter lorsque le fil actuel sort. Si vous appelez ceci sur chaque thread, alors quand il quitte normalement le code sera exécuté. Si un thread est terminé de force avec TerminaTéthread code>, plus aucun code n'est exécuté sur ce thread, de sorte que les fonctions
at_thread_exit code> ne sont pas appelées. Le seul moyen de gérer de tels cas serait de crochet
TerminaTéthread Code>, bien que cela ne gère pas nécessairement le cas qu'un autre processus termine vos threads. P>
Le seul moyen de de manière fiable forte> Faites ceci est dans une DLL qui croche dll_thread_attach et dll_thread_detach. Voir la discussion précédente ici . P>
Le meilleur moyen est d'appeler WaitforSingObject avec la poignée du fil (appelez OPENHREAD à l'aide de l'ID de thread pour obtenir la poignée). P>
Très intéressant en effet. Un tel événement sera-t-il un incendie sur la terminaison de fil ou dans d'autres situations également (ce qui pourrait être)?
Je suppose que si vous voulez vraiment assez mal, vous pouvez utiliser l'API de débogage (par exemple, Je ne peux pas dire que c'est exactement un moyen simple ou simple de le faire, mais si vous ne pouvez pas venir avec quoi que ce soit d'autre, c'est probablement mieux que rien. P> WaitFordebugevent code>,
ContinueDeBuVENT code>) ,. Vous obtiendrez une sortie_thread_debug_event lorsqu'un fil est sorti. P>
Si votre programme est dans une DLL, vous pouvez configurer pour gérer la méthode DLLMAIN. Ceci est appelé lorsqu'un thread ou un processus commence / se termine / se termine.
Par exemple, P>
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.
Désolé, c'est faux. Il est seulement appelé lorsqu'un processus ou un thread qui charge la DLL i> commence ou terminé. Sauf si chaque fil de l'application charge cette DLL, elle ne sera pas appelée (et si la DLL peut être chargée par chaque thread, il existe de meilleurs moyens de se faire avertir quand ils se terminent).
Ken, tu as mal compris. Ceci est une API Windows standard - la méthode est appelée lorsque tout thread sort du processus que la DLL est chargée. La DLL est chargée dans le processus, non dans le thread, une fois que le processus charge la DLL (par n'importe quel fil), DLLMAIN est appelé à chaque fil de sortie. Voir msdn.microsoft.com/en-us/ Bibliothèque / MS682583 (v = vs.85) .aspx en particulier la partie DLL_Thread_Detatch.
@MDMA: Je ne pense pas. Veuillez vous reporter à la première phrase de votre sujet lié ( emphasis mine b>): "Un point d'entrée optionnel dans une bibliothèque de liaison dynamique (DLL). Lorsque le système démarre ou termine un processus ou un thread, il appelle la Fonction d'entrée-point pour chaque DLL chargée à l'aide du premier fil du processus B>. " Notez le "premier fil" dans cette phrase. Je ne sais pas comment cela peut être plus clair, mais je serais prêt à admettre que j'avais tort si vous avez quelque chose qui démontre le contraire ...
La documentation a changé au fil des ans. Maintenant ce n'est pas si clair. Bien que ce ne soit pas expressément écrit, cela signifie le DLL_PROCESS_ATTACH, qui est appelé à partir du premier fil du processus. Je suis d'accord avec le texte n'est pas clair, mais c'est ce que cela signifie - dll_thread_detach est appelé à chaque fil qui quitte le processus. Sinon, quelle serait la différence entre un détachement de fil et le processus? S'il vous plaît essayez-le si vous ne me croyez pas. J'ai codé de nombreuses dlls "colle" qui utilisent une fonctionnalité spécifique.
@MDMA: Je ne pense pas que cette question répond à la question de Mason (car elle ne fournit pas l'information qu'il recherche, à savoir le threadid du fil de sortie). Cependant, je retire mon vote en désaccord parce que très i> techniquement parler, dllmain fait i> est appelé comme vous l'avez dit (mais en utilisant le premier fil, comme je l'ai mentionné dans mon commentaire) . Par conséquent, très i> techniquement parler, vous n'étiez pas faux. Néanmoins, ne pensez pas que c'est une réponse à la question posée, cependant :) - ne méritait tout simplement pas un vote au bas.
@ken - J'ai répondu à la question. L'appel est effectué dans le contexte du thread de sortie afin que vous puissiez obtenir l'ID de thread. De nombreuses implémentations de variables locales de fil s'appuient sur cette fonctionnalité.
@MDMA: Je vois ça. :) Nice Travail - Vous avez inversé un vote en panne et avez fait un vote en place. Première fois que j'ai jamais fait que i>.
Merci, bien que je n'ai changé que ma réponse légèrement - le changement plus important était de votre compréhension, bien sûr, des malentendus se produisent. Je n'ai réussi que de vous amener à inverser le bowvote parce que j'ai passé le temps de défendre ma réponse. Vous dites que c'est la première fois que vous avez inversé un vote - je l'apprécie - mais peut-être que d'autres que vous avez évité, vous avez corrigé, mais ne prendrait pas le temps de défendre leurs réponses. J'espère que, à l'avenir, vous ne baisserez que lorsque vous êtes totalement sûr de quelque chose et que vous avez une expérience empirique pour la corroborer. De cette façon, alors sera une place plus heureuse pour tous.
Flamewars privé de côté, ce n'est vraiment pas une réponse parce que je ne cours pas à l'intérieur d'une DLL.
? 'Flamewars "? Je vois une discussion sur une réponse. Où est la flamme?
+1 pour la discussion constructive et pour la solution. @ Mason, prenez l'indice de MDMA: Quote: J'ai codé de nombreuses dlls "colle" qui utilisent une fonctionnalité spécifique. Code>. Écrivez une petite dll qui vous rappelle quand un fil sort.
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.
Semble très gentil. Toute façon de le faire sans la bibliothèque exclusive?
Oui, c'est un peu plus compliqué, mais faisable. Fondamentalement, vous devez écraser le premier couple d'octets de LDRSHUTTODTODHADAD avec une instruction de saut à votre propre code, puis faites ce que vous voulez faire et enfin faire quelles que soient les instructions que vous avez écrasées et retournez dans l'original LDRSHUTTODTHADAD après l'instruction de saut. Le problème est que vous ne pouvez pas simplement couper des instructions en deux, vous devez donc démonter le code d'origine de LDRSHUTTODODADAD pour vous assurer de copier un certain nombre d'instructions complètes. Il commence la même chose sur toutes les versions du système d'exploitation que cela devient plus facile ...
Une recherche rapide trouve ceci: sites.google.com/site/delphibasics/home/ Delphibasicssnippets / ... Après un coup d'œil rapide au code, cela va faire plus que ce dont vous avez besoin (il est écrit de pouvoir injecter des API de DLL et de crochet dans d'autres processus) afin que vous puissiez pouvoir extraire le quantité minimale de code pour celui-ci que vous devez simplement accrocher l'API dans votre propre processus.
Il semble que Jeff Atwood lui-même a fusionné la question de Mason avec Gabr sur. Une étape que je dois dire que je suis fortement en désaccord parce que les deux questions ne sont vraiment pas identiques du tout.
@Thorstenengler est-il possible de gérer rtluserthreadstart, baséhreadinitthunk code>? Pour commencer, initialisation?
Vous pouvez utiliser le win32_threadstoptrace code>
événement WMI Pour détecter la résiliation de tout thread dans le système.
Pour commencer à surveiller cet événement, vous devez écrire une phrase chèque Cet exemple P> maintenant Pour utiliser ce fil dans votre application, vous devez l'appeler de cette manière p> et dans le Fonction de rappel p> wql code> comme ceci p>
+1 pour encore une autre réponse WMI de Rruz. WMI est-il une solution raisonnable dans ce cas? Les frais généraux ne sont-ils pas trop grains pour un travail aussi bas? Remarque: Je ne vais passer que par le nom, l'instrumentation de gestion de Windows, et elle ne semble pas être utilisée comme utilisée pour libérer des variables de fil, par exemple.
@Cosmin, je sais qui n'est pas très courant d'utilisation un objet COM avec des tâches de faible niveau, telles que la surveillance des threads et du processus. Dans une autre main, le WMI code> est principalement connu en récupérant des informations système telles que le matériel installé et réfléchit comme ça, mais ce n'est qu'une petite partie de son pouvoir. Personnellement, j'ai écrit de nombreuses applications pour surveiller les systèmes distants avec succès à l'aide de l'OMM. Avec le
WMI code> Vous pouvez détecter le début des threads, le processus, les pannes matérielles, le nouveau matériel connecté ou déconnecté, etc. En résumé comme réponse à votre question
est une solution raisonnable dans ce cas? code>, oui c'est.