Ecrire dans une fenetre extérieure

Signaler
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006
-
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006
-
Bonjour,

Apres pas mal de recherche, je ne vois toujours pas comment on peut écrire dans une fenetre extérieure a un programme (et non pas une fenetre fille du programme). J'entend par là ne pas modifier le titre, mais trouver le handle de la première dialogbox et écrire dedans une chaine spécifiée.
J'ai un handle vers la fenêtre dans laquelle je veux écrire, je suppose qu'il me faut récuperer le handle de la premiere textbox et écrire dedans a l'aide de SetWindowsText, ou SendMessage utilisé avec WM_SETTEXT.
Je suppose que je dois me servir de EnumChildWindows, mais:
- je ne comprend pas bien le fonctionnement de cette API, j'ai n'ai pas trouvé d'exemples.
- je ne sais pas comment tester si une fenêtre est de type textbox, je n'ai trouvé aucune API pour ca.

voila, après pas mal de recherches sur MSDN, je ne suis pas beaucoup plus avancé, j'ai pensé pouvoir trouver un peu d'aide sur ce site (génial au passage, je n'ai jamais posté mais je m'en sers souvent).

ps: je précise que je code en C et non pas en C++.
merci d'avance :)

19 réponses

Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
30
Compare buffer rempli par GetClassName avec "EDIT".

ciao...
http://dev.winsysdev.com
BruNews, MVP VC++
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
Bonjour,

Pour trouver le premier Editbox d'une fenêtre il suffit d'utiliser l'API suivante comme ceci:

HWND hwndchild=FindWindowEx(hwndParent,0,"EDIT",0);



Pour un Static on fait:

HWND hwndchild=FindWindowEx(hwndParent,0,"STATIC",0);



Le 2ème paramètre de SetWindowText() (pareil pour WM_SETTEXT)
est un pointeur qui ne sera pas valide dans le process de la fenêtre
cible. Pour résoudre ce problème, je pense qu'il faut allouer de la
mémoire dans le process de cette fenêtre, écrire dans cette zone
mémoire puis utiliser son pointeur dans SetWindowText(). Voici les APIs à utiliser:

GetWindowThreadProcessId() pour récupérer le PID du process.

OpenProcess() pour obtenir le handle du process.

VirtualAllocEx() pour allouer de la mémoire dans le process.

WriteProcessMemory() pour écrire le texte dans la zone allouée.

Utiliser le pointeur renvoyé par VirtualAllocEx() dans SetWindowText().

Ne pas oublier de libérer la zone allouée avec VirtualFreeEx() et le handle du process avec CloseHandle() quand on n'en a plus besoin.
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
30
WM_SETTEXT fonctionne interprocessus, en ce cas c'est le système qui copie d'abord la chaine et la remet dans l'espace mémoire de la fenêtre cible.

ciao...
http://dev.winsysdev.com
BruNews, MVP VC++
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
J'étais obligé d'utiliser la demarche plus haut pour récupérer et
modifier le contenu d'une Listview d'une fenêtre externe à mon
application. Je pensais que c'est pareil pour WM_SETTEXT car je ne l'ai
jamais utilisé interprocess. Merci pour la précision.
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

je viens de tester la fonction suivante, qui apparement échoue.

int SendKey( HANDLE Window, char *string )
{
HWND hwndchild=FindWindowEx(Window,0,"EDIT",0);
SendMessage(hwndchild, WM_SETTEXT, 0, (LPARAM)string);
return 0;
}

je pense que je vais tester la méthode de racpp qui consiste a écrire dans la zone mémoire du processus.
merci pour l'aide.
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
30
Tu as resré la validité du handle obtenu ?

ciao...
http://dev.winsysdev.com
BruNews, MVP VC++
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

mmmh pas bete, effectivement il est invalide :/
je vais tester la méthode avec EnumWindows et GetClassName
( et je vais également arreter de croire les gens sur paroles ! ).

merci
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
char classname[100];

GetClassName( hwndChild, classname, sizeof(classname));
if(!strcmp(classname,"EDIT")) {
SendMessage(hwndChild, WM_SETTEXT, 0, lParam);
}

return TRUE;
}

int SendKey( HANDLE Window, char *string )
{
if(!EnumChildWindows(Window, EnumChildProc, (LPARAM) string)) {
printf ("EnumChildWindows error: %d \n",GetLastError());
return 1;
}
return 0;
}

Bon, apres avoir testé ca, ca ne marche pas et je ne comprend pas trop ou est l'erreur, j'ai repris l'exemple donné sur msdn...
De plus, avec ce code j'enverrais le message donné a toutes les boites de dialogue de la fenetre parent, non ?
Comment faire pour ne l'envoyer qu'a une seule dialog box ?
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
30
Regarde donc avec SPY++ le classname du controle cible, on ne sait jamais...
Suffirait de retourner 0 au 1er EDIT trouvé pour stopper enum, faut voir si te convient.
Autre idée, vérifie sur plusieurs instances si le ctrlID ne varie pas, si oui alors fais la recherche sur l'ID dans l'enum.

ciao...
http://dev.winsysdev.com
BruNews, MVP VC++
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
Cybernus > Il est vrai qu'il ne faut pas croire les gens sur parole.
Le problème c'est que tu n'as pas su bien utiliser FindWindowEx(). Il
faut lui tranmettre un HWND parent valide et un nom de classe valide.
J'ai toujours utilisé cette API sans problème, sinon je ne t'aurais pas
proposé cette solution. Il est de loin préférable à EnumChildWindows()
avec sa procédure callback.

Moralité : Avant d'appeler une API il faut toujours s'assurer que les paramètres qu'on lui passe sont valides.

:)

C'est surement là le problème.

Bonne chance.
Messages postés
21041
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
30
Il est clair qu'on ne fait jamais un copier coller sans vérifier les fonctions dans MSDN si on ne les connait pas.
Je confirme que FindWindowEx fonctionne parfaitement.

ciao...
http://dev.winsysdev.com
BruNews, MVP VC++
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

Pourtant, je suis sur et certain de mon handle Window (avec un simple
SetWindowText, j'arrive a changer le titre de la fenetre, c'est la
preuve qu'il s'agit du bon Handle non ? )

La seule solution que je vois, c'est que l'appel a FindWindowEx
échoue... avec pourtant un bon handle (j'ai testé sur une fenetre du
bloc note, je ne sais pas si elle contient une window de classename
"EDIT" ).

Je ne comprend pas trop bien spy++, mais apparement je ne trouve aucune window qui a une fenetre fille qui a "EDIT" comme nom de classe :/

racpp >> je te prie de m'excuser, en relisant mon message je me suis rendu compte qu'il pouvait etre un peu vexant.
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
Cybernus >> Je ne me suis pas vexé, j'ai juste trouvé que tu as
tiré une conclusion de manière un peu hative. Si FindWindowEx() échoue,
ça veut dire qu'elle n'a pas trouvé ce qu'on lui a demandé. Tu es sûr
qu'il s'agit d'un EDIT? Et que cette fenêtre contient bien un EDIT?

Spy++ est un outils de Visual Studio permettant de trouver tous les
paramères d'une fenêtre affichée à l'écran et tous ses controles child.
Ca te permettra de reconnaitre la classe de ton controle. Il existe des
exemples de codes immitant Spy++ sur cppfrance.
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

Apres avoir regardé attentivement les classes des fenetres filles du bloc-note et fait quelques essais, j'ai trouvé que la classe "Scintilla" correspond a la textbox ou l'on écrit.
la fonction suivante permet d'écrire donc sur le bloc note

int SendKey( HANDLE Window, char *string )
{
HWND hwndchild=FindWindowEx(Window,0,"Scintilla",0);
if(!hwndchild) {
printf ("handle invalide :( \n");
return 1;
}
SendMessage(hwndchild, WM_SETTEXT, 0, (LPARAM)string);
return 0;
}

Je viens d'essayer et ca marche.
Par contre ca ne me permet pas d'écrire "sur la premiere textbox de n'importe quel fenetre".
n'existe il pas un moyen de "tester" si la fenetre est une textbox, indépendamment du nom de la classe ?
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
C'est justement le nom de la classe qui permet de savoir si c'est un EDIT ou non.

Beaucoup de programmes utilisent des Edits personnalisés, d'où ces
étranges noms de classes. Ca ne les empêche pas de recevoir des
WM_SETTEXT. Je pense que tu dois utiliser FindWindowEx() dans une
boucle pour envoyer un texte à tous les controles child et voir le
résultat. Ca te permettra de reconnaitre ton textbox parmi eux.
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

et y a t'il un moyen de savoir si le message a bien été pris en compte ?
histoire d'arreter la fonction une fois que le texte a été écrit :)
Messages postés
1910
Date d'inscription
vendredi 18 juin 2004
Statut
Modérateur
Dernière intervention
14 novembre 2014
13
Il suffit de regarder le textbox pour vérifier le changement de texte. ça te permet juste de l'identifier.
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

je remonte le message pour dire qu'après essai:

int SendKey( HANDLE Window, char *string )
{

HWND hwndChild = FindWindowEx(Window,0,NULL,0);
do
{
SendMessage(hwndChild, WM_SETTEXT, 0, (LPARAM)string);
hwndChild = FindWindowEx(Window, hwndChild, NULL, 0);
// on execute la boucle tant qu'il existe une autre fenetre enfant
} while(hwndChild);

return 0;
}

je n'arrive toujours a écrire que sur le bloc note.
j'en ai déduis:
- soit la boite de dialogue qui m'interesse est une fenetre fille de la fenetre fille
- soit (je ne sais pas si c'est possible) les boites de dialogues sont des fenetres completement a part

si je trouve autre chose, je posterais la source ici
Messages postés
12
Date d'inscription
mardi 3 juin 2003
Statut
Membre
Dernière intervention
8 mars 2006

voila, je poste pour dire que j'ai adopté une autre méthode, qui consiste a placer la fenetre ciblée au premier plan, puis a écrire dessus en simulant une pression des touches.
Peu satisfaisant en théorie, d'apres les dire de BruNews ( j'ai d'ailleurs emprunté sa fonction de simulation ), en pratique je trouve cela relativement satisfaisant.

void __stdcall SimuleKey(TCHAR *psz)
{
// fonction reprise de BruNews
// ajout de la touche entrée
INPUT npt; npt.ki.dwExtraInfo npt.ki.time 0;
npt.type = INPUT_KEYBOARD;
npt.ki.wVk = 0;
while(*psz) {
if(psz[0]=='|') {
// je me sers du char '|' pour simuler la pression de la touche entrée
keybd_event(VK_RETURN , 0, 0, 0);
keybd_event(VK_RETURN , 0, KEYEVENTF_KEYUP, 0);
}
else {
#ifdef UNICODE
npt.ki.wScan = (WORD) *psz;
#else
npt.ki.wScan = (WORD) (BYTE) *psz;
#endif
npt.ki.dwFlags = KEYEVENTF_UNICODE;
SendInput(1, &npt, sizeof(INPUT));
npt.ki.dwFlags = KEYEVENTF_UNICODE| KEYEVENTF_KEYUP;
SendInput(1, &npt, sizeof(INPUT));
}
psz++;
}
}


int SendKey( HANDLE Window, char *string )
{
if(!IsWindowEnabled(Window)) SendMessage(Window, WM_ENABLE, 0, 0);
// au cas ou la fenetre est minimizée, on la restaure
ShowWindow(Window, SW_SHOW);

// on amene la fenetre au premier plan
SetForegroundWindow( Window );

printf("sending key: %s\n",string);
SimuleKey( string );

return 0;
}