cs_nitrique
Messages postés344Date d'inscriptionjeudi 1 mai 2003StatutMembreDernière intervention 4 avril 2011
-
13 avril 2006 à 09:53
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 2022
-
18 avril 2006 à 15:00
Bonjour à tous,
Je liste tous les processus actifs dans une liste (de la même manière que le gestionnaire des taches).
Ensuite, à partir du ProcessID de l'application choisie, j'aimerais récuperer tous les handles de ce processus (un handle correspond à un composant de l'application choisie, je me trompe ?),
pour me permettre de sélectionner un champs texte de cette application.
Je le fait de cette manière:
tabHandles: array [0..100] of integer; // Un HWND est un integer...
procedure TfrmMain.ListerHandles(pid: Cardinal);
var
handles: TList;
i: Integer;
findWindowsStruct: TFindWindowsStruct;
begin
handles:= TList.Create;
Try
findWindowsStruct.ProcessID:= pid; // GetCurrentProcessID
findWindowsStruct.HandleList:= handles;
EnumWindows(@EnumWindowsProc, Integer(@findWindowsStruct));
cbThreads.Clear;
for i:= 0 to handles.Count - 1 do begin
cbThreads.Items.Add('Handle: '+IntToStr(Integer(handles[i])));
tabHandles[i] := Integer(handles[i]);
end;
cbThreads.ItemIndex := 0;
Finally
handles.Free;
end;
end;
Mais quand je teste les handles reçus avec un
PostMessage(tabHandles[cbThreads.ItemIndex]),WM_CHAR,sCar,0); // Simulation du copier
Rien ne se passe.
Par exemple, Si je fait:
myHandle := GetTopwindow(GetforegroundWindow); // Ca marche mais que pour notePad
PostMessage(myHandle,WM_CHAR,sCar,0);
Et que le focus est sur le notepad par exemple, handle est égal à 263014.
Mais si j'utilise la méthode de listage pour le notepad, il ne me retourne qu'un enregistrement qui est différent de celui de la solution ci-dessus (459614).
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 14 avril 2006 à 00:22
Bon alors regarde j'ai fait un bout de code, cette approche un peut différente mais c'est la seule qui me permet de faire l'inventaire des composants (plus où moins, ne fonctionne pas avec toutes les applications) et d'en selectionner un pour lui envoyer du texte :
Function EnumWindowsProc(hWnd: HWND; lParam: lParam): Bool; stdcall;
Const aStr = '%s %d';
Var FrmMain : TFrmMain;
HandleList : TStringList;
Implementation
{$R *.DFM}
Uses TlHelp32;
Var tabProcessus : Array[0..100] Of Cardinal;
// Contient le ProcessId de la première comboBox
tabHandles : Array[0..100] Of integer;
// Contient les handles de la deuxième comboBox
Function EnumWindowsProc(hWnd: HWND; lParam: lParam): Bool;
Var processID : DWORD;
aName : Array[0..255] Of Char;
Begin GetWindowThreadProcessID(hwnd, @processID);
If (hWnd <> FindWindow('Shell_TrayWnd', Nil)) And (hWnd <> FindWindow('Progman', Nil)) Then If IsWindowVisible(hwnd) Then Begin GetWindowText(hwnd, aName, SizeOf(aName));
HandleList.Add(Format(aStr, [aName, HWnd]));
TabProcessus[HandleList.Count - 1] := hwnd;
Result := True;
End ;
End;
Procedure GetProcessList;
Begin HandleList.Clear;
EnumWindows(@EnumWindowsProc, 0);
End;
Procedure TFrmMain.ListerChilds(pid: Cardinal);
Var aHandle : THandle;
aName : Array[0..255] Of Char;
Begin cbThreads.Clear;
aHandle : = 0;
Repeat aHandle := FindWindowEx(pid, aHandle, Nil , Nil);
If aHandle <> 0 Then Begin GetClassName(aHandle, aName, SizeOf(aName));
cbThreads.Items.Add(Format(aStr, [aName, aHandle]));
tabHandles[cbThreads.Items.Count - 1] : = aHandle;
End;
Until aHandle = 0;
End ;
Procedure TfrmMain.cbProcessusChange(Sender: TObject);
Begin ListerChilds(TabProcessus[cbProcessus.ItemIndex]);
End;
Procedure TFrmMain.cbThreadsClick(Sender: TObject);
Var S : String;
Begin S : = '1234567890';
SendMessage(tabHandles[cbThreads.ItemIndex], WM_SETTEXT, 0, Longint(s));
//On Change le Texte
End;
Procedure TFrmMain.FormCreate(Sender: TObject);
Begin HandleList := TStringList.Create;
End;
Procedure TFrmMain.FormClose(Sender: TObject; Var Action: TCloseAction);
Begin HandleList.Free;
End;
Procedure TFrmMain.FormShow(Sender: TObject);
Begin btn_Refresh.Click;
End;
Procedure TFrmMain.btn_RefreshClick(Sender: TObject);
Begin GetProcessList;
cbProcessus.Items.Clear;
cbProcessus.Items.AddStrings(HandleList);
End;
End.
Le TEdit n'est la que pour tester et pour avoir FrmMain dans la liste il faut cliquer sur Refresh
Tester ok sur : NotePad, Calculatrice et sur lui même.
il faut bien sur affiner mais je pense que c'est la marche à suivre
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 13 avril 2006 à 12:18
Salut,
Ce bout de code envoie du texte à la calculatrice
procedure TForm1.Button1Click(Sender: TObject);
Var s : String;
H_WinCmd : THandle;
begin S := '123456789'; // Texte à envoyer
H_WinCmd := FindWindowEx(FindWindow(Nil, 'calculatrice'), 0,'Edit', nil); // on trouve le handle de l'edit
SendMessage(H_WinCmd,WM_SETTEXT,0,Longint(s)); //On Change le Texte
end;
cs_nitrique
Messages postés344Date d'inscriptionjeudi 1 mai 2003StatutMembreDernière intervention 4 avril 20111 13 avril 2006 à 12:28
Bonjour cirec,
C'est cool, je mettrais le ProcessId dans le premier paramètre mais seulement le 3ème paramètre m'est inconnu, je veux essayer avec tous les handles du processus.
N'y a t-il pas un équivalent qui attends un HWND plutot qu'un pchar en paramètre ?
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 13 avril 2006 à 13:33
Le handle que tu récupère ainsi c'est celui de la fenêtre et non celui d'un composant.
Pour pouvoir envoyer du texte au NotePad il faut trouver le handle de l'Edit qui se trouve sur la fiche
Voici une variante qui change le texte du Notepad
procedure TForm1.Button1Click(Sender: TObject);
Var s : String;
H_WinCmd : THandle;
begin S := '123456789'; // Texte à envoyer
H_WinCmd := FindWindowEx(FindWindow('NotePad', Nil), 0,'edit', nil); // on trouve le handle de l'edit
SendMessage(H_WinCmd,WM_SETTEXT,0,Longint(s)); //On Change le Texte
end;
Pour énumerer les fenêtres enfants il existe :
EnumChildWindows
Essaye peut être avec mais il faudra quand même faire la différence entre un bouton et un edit pour envoyer le texte au bon composant
@+
Cirec
Vous n’avez pas trouvé la réponse que vous recherchez ?
cs_nitrique
Messages postés344Date d'inscriptionjeudi 1 mai 2003StatutMembreDernière intervention 4 avril 20111 13 avril 2006 à 13:40
Très bien, je vais essayer.
Mais dans ce cas, comment ça se fait que ma fonction retourne plusieurs handles pour certains processus (word, ...) ?
De plus, dans le cas d'une application qui a plusieurs champs textes, j'aimerais choisir !
cs_nitrique
Messages postés344Date d'inscriptionjeudi 1 mai 2003StatutMembreDernière intervention 4 avril 20111 13 avril 2006 à 14:28
Aucun pb, les voici:
type
PFindWindowsStruct = ^TFindWindowsStruct;
TFindWindowsStruct = record
ProcessID: DWORD;
HandleList: TList;
var
tabProcessus: array [1..100] of Cardinal; // Contient le ProcessId de la première comboBox
tabHandles: array [0..100] of integer; // Contient les handles de la deuxième comboBox
function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): boolean; stdcall;
var
dwProcessId: DWORD;
begin
if lParam <> 0 then
begin
GetWindowThreadProcessId(hwnd, dwProcessId);
with PFindWindowsStruct(lParam)^ do
if dwProcessID = ProcessID then
HandleList.Add(Pointer(hwnd));
result:= true;
end
else
result:= false;
end;
procedure TfrmMain.ListerProcessus(); // Lister les processus actifs du systeme
var
SnapShot: Cardinal;
ProcessEntry: TProcessEntry32;
i: integer;
begin
cbProcessus.Clear;
SnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); // Création de "l'instantané"
If SnapShot = 0 then Exit; // En cas d'erreur on sort
ProcessEntry.dwSize := SizeOf(ProcessEntry);
If Process32First(SnapShot, ProcessEntry) then begin // Lecture du premier processus de la liste
cbProcessus.Items.Add(ProcessEntry.szExeFile);
cbProcessus.ItemIndex := 0;
i := 0;
while Process32Next(SnapShot, ProcessEntry) do begin // Tant qu'il reste un processus, ajouter son nom à la liste
cbProcessus.Items.Add(ProcessEntry.szExeFile+' ('+IntToStr(ProcessEntry.th32ProcessID)+')');
i := i+1;
tabProcessus[i] := ProcessEntry.th32ProcessID; // Garde le ProcessId pour la correspondance
end;
end;
CloseHandle(SnapShot); // Libération de l'instantané
end;
procedure TfrmMain.ListerHandles(pid: Cardinal);
var
handles: TList;
i: Integer;
findWindowsStruct: TFindWindowsStruct;
begin
handles:= TList.Create;
Try
findWindowsStruct.ProcessID:= pid; // GetCurrentProcessID
findWindowsStruct.HandleList:= handles;
EnumWindows(@EnumWindowsProc, Integer(@findWindowsStruct));
cbThreads.Clear;
for i:= 0 to handles.Count - 1 do begin
cbThreads.Items.Add(IntToStr(pid)+' '+IntToStr(Integer(handles[i])));
tabHandles[i] := Integer(handles[i]);
end;
cbThreads.ItemIndex := 0;
Finally
handles.Free;
end;
end;
procedure TfrmMain.cbProcessusChange(Sender: TObject);
begin
ListerHandles(tabProcessus[cbProcessus.ItemIndex]);
end;
postMessage(tabHandles[cbThreads.ItemIndex],WM_PASTE,0,0); // Dans mon évenement
Il faut juste 2 combobox (cbProcessus et cbThreads)
Le principe est simple, quand tu changer cbProcessus qui contient le nom de tous les processus, j'aurais aimé que ça affiche tous les handles correspondant aux composants de ce processus dans la 2ème lookup.
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 14 avril 2006 à 11:30
La source fonctionne tel quel.
le problème c'est que je n'ai pas réussi à récuperer les handles des composants d'une fenêtre avec ta fonction.
par contre celle-ci fonctionne très bien (chez moi) et si j'enlève IsWindowVisible ça ne plante pas mais j'ai toutes les fenêtres du système ?
ps : après avoir cliquer sur Refresh tu choisis un processus qui t'interesse tu cliques dessus et si il y en a (des composants) il met les handles dans la liste