Newsgroup: it.comp.lang.delphi
Titolo: Handle ad un Thread di un processo esterno (W98,D5)

Salve a tutti.
Come posso ottenere un handle ad un thread di un processo esterno, dato
il suo ThreadID ?
Ho provato con DuplicateHandle ma, o non ho capito bene cosa passargli, o 
non è la funzione giusta...
Grazie in anticipo!! Ciao, Davide DG.

 non è la funzione giusta...
DuplicateHandle serve, ma dovrebbe essere chiamata solo internamente al
thread stesso! In poche parole DuplicateHandle dovrebbe creare un Handle
REALE di un oggetto (nel caso specifico, Thread), e non uno PseudoHandle! Mi
spiego meglio: la funzione che può essere interessante è GetCurrentThread,
che restituisce uno PseudoHandle del thread stesso ... ora iniettando un
Hook nel Thread interessato e poi richiamando GetCurrentThread ottieni
questo famoso PseudoHandle, solo che il PHandle è un valore valido solo
all'interno del Thread stesso, quindi se "catturi" questo valore e lo usi
all'esterno la funzione a cui lo passi ti risponde 2 di picche! Leggi però
l'SDK alla voce GetCurrentThread :"A thread can create a "real" handle of
itself that can be used by other threads, or inherited by other
processes, by specifying the pseudohandle as the source handle in a call to
the DuplicateHandle function."
Ecco quindi la mia soluzione "sporca":
1 - Inietti l'Hook nell'indirizzamento del Thread di cui possiedi il TID.
2 - Dalla funzione di Callback (il filtro dell'Hook) richiami
GetCurrentThread per ottenere lo PseudoHandle
3 - Dall'interno della Callback richiami DuplicateHandle secondo quanto
sopra specificato (passandole lo PseudoHandle)
4 - Esporti (mediante una qualsiasi tecnica IPC) l'handle REALE creato da
DuplicateHandle e hai quindi il tuo hThread.
Se le operazioni che devi fare con questo Handle del thread non sono troppo
pesanti c'è una seconda via "sporca" e "semplificata".
1 - Inietti l'Hook nell'indirizzamento del Thread di cui possiedi il TID
2 - Dalla funzione di Callback richiami GetCurrentThread per ottenere
pHandle
3 - Sempre dalla Callback sfrutti lo PseudoHandle ed effettui tutte le
chiamate necessarie (ti ricordo che sei nello spazio di indirizzamento
remoto, per cui lo PseudoHandle è un valore "lecito")
A te la scelta ... non ho trovato (e non credo esista per motivi di
sicurezza) alcuna funzione che ti restituisce un Real-Handle di un oggetto
Kernel di tipo Thread che sia esso esterno o interno al processo!
Neppure uno snapshot dei Thread mediante Tool Help (vedi TThreadEntry32)
fornisce l'handle reale di un Thread, ma sempre il Thread ID.
Fammi (facci) sapere! :)
Ciao! :)
--
- Carlo -
c.b...@libero.it

 > il suo ThreadID ?
[snip miasoluzione]
Su ICP.Win32 ti hanno risposto "OpenThread" (che il mio SDK neppure
documenta ...), ma ti riporto quanto c'è scritto su msdn ...
Requirements
  Windows NT/2000: Requires Windows 2000 or later.
  Windows 95/98: Requires Windows Me.
  Header: Declared in Winbase.h; include Windows.h.
  Library: Use Kernel32.lib.
Per cui ho i miei dubbi che tu la possa usare sotto Windows 98! Il
compilatore non me la riconosce neppure su WinME e segnala un errore su
OpenThread!
Ciao!


 GetCurrentThread per ottenere lo PseudoHandle
Devo ancora mettermi a provare... cmq penso di avere 2 difficoltà
concettuali :
1) Che tipo di hook dovrei considerare?
2) La Call back, dovendo essere "touch and go" (toccata e fuga ;P), può
disattivarsi da sola?
Mi sa che mi rileggerò la tua "guida" agli hook  ;-)
Ciao, mi metto a lavorare !   Davide DG.

 Mi sa che mi rileggerò la tua "guida" agli hook  ;-)
E menomale che era un segreto! ;)
Cmq guarda che cosa ti ho preparato ... Ecco un'applicazione che ottiene lo
PseudoHandle di un Thread dato il suo Thread ID. Quella che segue è la DLL.
-----
library hThreadFromTid;
uses
  SysUtils,
  ClipBrd,
  Windows;
{$R *.RES}
var
hHook : THandle;
OnlyOnce : Boolean = True;
WM_NOTIFICA : Cardinal;
Function HookProc(Code:Integer; WPar : Wparam; LPar :Lparam) : Integer;
Stdcall;
var hThread : Thandle;
Begin
{ Tutto quello che verrà creato all'interno di questa porzione di codice
  risulterà di "proprietà" del thread, ecco perchè la Form contasecondi
  che viene creata non viene distrutta assieme al processo che ha caricato
la DLL }
  If OnlyOnce Then begin
       OnlyOnce := False;
       WM_NOTIFICA:=RegisterWindowMessage('MyMessage');
       hThread := GetCurrentThread();
       ClipBoard.Clear;
       ClipBoard.SetTextBuf(PChar(IntToStr(hThread)));
       PostMessage(HWND_BROADCAST,WM_NOTIFICA,0,0);
  end;
  Result := CallNextHookEx(hHook,Code,WPar,LPar);
  { Richiamo la CallNextHookEx per proseguire nella catena di Hooks }
end;
{ Procedura di caricamento dell'Hook. Questa, come si può notare, ha un
parametro
  che è il Thread Id del Thread nel quale si desidera iniettare il codice.
Il
  Thread Id di un particolare Thread è ricavabile mediante la funzione
  GetWindowThreadProcessId }
Procedure Load (TID : dWord); stdcall;
Begin
   hHook:=SetWindowsHookEx(WH_GETMESSAGE,@HookProc,hInstance,TID);
end;
{ Procedura di rimozione dell'Hook dalla memoria }
Procedure Remove; stdcall;
Begin
   UnHookWindowsHookEx(hHook);
end;
Exports
Load,Remove;
end.
-----
Ora un'esempio di applicazione: piazza in una form 2 button e 1 edit.
Io ho trovato lo PseudoHandle di un Thread di Explorer ... tu dai in pasto
alla funzione Load il TID che già possiedi.
-----
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ClipBrd;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
     Procedure MessageHandler (var MSG : TMSG; var Handled : boolean);
    { Public declarations }
  end;
Procedure Load (TID : dWord); stdcall; External 'hThreadFromTid.DLL';
Procedure Remove; stdcall; External 'hThreadFromTid.DLL';
var
  Form1: TForm1;
  WM_NOTIFICA : Cardinal;
implementation
{$R *.DFM}
Procedure TForm1.MessageHandler(var MSG : TMSG; var Handled : boolean);
Begin
  if (Msg.message = WM_NOTIFICA) then begin
     Edit1.Clear;
     Edit1.text := 'PseudoHandle: ' + ClipBoard.AsText;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
Var TID : dWord;
    hExplorer : THandle;
begin
  hExplorer:=FindWindow('Progman','Program Manager');
  { Ho Trovato l'handle di una delle finestre di Explorer }
  If hExplorer<>0 Then Begin
     TID := GetWindowThreadProcessId(hExplorer,nil);
     { Ho trovato il Thread ID correlato }
     Load(TID);
     PostMessage(hExplorer,WM_MOUSEMOVE,0,0);
     { Sollecito il caricamento del Thread aggiungendo un messaggio
"innocuo" alla coda dei messaggi }
  end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  Remove;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  WM_NOTIFICA:=RegisterWindowMessage('MyMessage');
  Application.OnMessage := MessageHandler;
end;
end.
-----
Appena ho tempo provo a creare un Handle Reale mediante DuplicateHandle ...
dammi il tempo di vedere come funziona DuplicateHandle e il gioco dovrebbe
essere fatto.
Ci sentiamo!


Ciao Davide,
ci ho perso un po' di tempo ma ora sembra funzionare ... mentre lo
PseudoHandle è un valore molto alto l'Handle reale è un valore molto basso.
Fammi sapere!
DLL:
-----
library hThreadFromTid;
uses
  Dialogs,
  SysUtils,
  ClipBrd,
  Windows;
{$R *.RES}
var
hHook : THandle;
OnlyOnce : Boolean = True;
WM_NOTIFICA : Cardinal;
Function HookProc(Code:Integer; WPar : Wparam; LPar :Lparam) : Integer;
Stdcall;
var hThread, hNewThread, hRemoteProcess : THandle;
    PID : dWord;
Begin
  If OnlyOnce Then begin
       OnlyOnce := False;
       ClipBoard.Clear;
       WM_NOTIFICA:=RegisterWindowMessage('MyMessage');
       hThread := GetCurrentThread();
       GetWindowThreadProcessId(FindWindow('TForm1','TheFormText'),@PID);
       hRemoteProcess:=OpenProcess(PROCESS_DUP_HANDLE,TRUE,PID);
       if
DuplicateHandle(OpenProcess(PROCESS_DUP_HANDLE,True,GetCurrentProcessId),hTh
read,hRemoteProcess,@(hNewThread),0,True,DUPLICATE_SAME_ACCESS) <> False
then
          ClipBoard.SetTextBuf(PChar(IntToStr(hNewThread)))
       else
          ClipBoard.SetTextBuf('Errore');
       PostMessage(HWND_BROADCAST,WM_NOTIFICA,0,0);
  end;
  Result := CallNextHookEx(hHook,Code,WPar,LPar);
end;
Procedure Load (TID : dWord); stdcall;
Begin
   hHook:=SetWindowsHookEx(WH_GETMESSAGE,@HookProc,hInstance,TID);
end;
Procedure Remove; stdcall;
Begin
   UnHookWindowsHookEx(hHook);
end;
Exports
Load,Remove;
end.
-----
Programma. ... sinceramente non credo di aver fatto modifiche rispetto al
codice del messaggio precedente, però per sicurezza te lo riposto.
Dai alla Form la Caption "TheFormText", è importante.
-----
unit Unit1;
interface




implementation
{$R *.DFM}

     Edit1.text := 'Handle Reale: ' + ClipBoard.AsText;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
Var TID : dWord;
    hExplorer : THandle;
begin
  hExplorer:=FindWindow('Progman','Program Manager');
  If hExplorer<>0 Then Begin
     TID := GetWindowThreadProcessId(hExplorer,nil);
     Load(TID);
     PostMessage(hExplorer,WM_MOUSEMOVE,0,0);
  end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
  Remove;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  WM_NOTIFICA:=RegisterWindowMessage('MyMessage');
  Application.OnMessage := MessageHandler;
end;
end.
-----
Ciao! :)


 Fammi sapere!
Ciao!
Intanto ti ringrazio per la velocità con cui mi hai risposto!!!
Ero arrivato, leggendo la tua segretissima guida (;-PPP), più o meno allo
stesso risultato e ... tutto felice
e contento, ho provato la routine nel caso reale.
Ora, siccome le cose sono sempre più complicate del previsto... Il thread di
cui devo ottenere un handle (che
non è "reale", ma semplicemente "valido" nello spazio di indirizzamento
della mia applicazione), NON CREA
FINESTRE.... e, sebbene siano le 2.10 e la mia mente sia molto
annebbiata.... mi pare che ci sia un problema.
Io mi preoccupavo per il _tipo_ di hook proprio per questo : un thread che
non ha creato finestre
(in generale :
The system creates a thread's message queue when the thread makes its first
call to one of the Win32 USER or GDI functions.), non ha una messagequeue...
ed effettivamente, facendo delle prove all'interno di un processo
multithreaded
con alcuni thread senza finestre e altri con... quelli "finestrati"
rispondono correttamente... gli altri invece no. :(
Ora, leggendo i Remarks  della PostThreadMessage, forse si potrebbe forzare
a crearne una, di message queue... ma dovrei provare.
Va beh... domani provo :)
Ciao e grazie ancora moltissimo per l'aiuto!!
Ciao, Davide DG