[Api Win32] CreateProcess / WaitForInputIdle / PostMessage

CreateProcess / WaitForInputIdle / PostMessage [Api Win32] - Divers - Programmation

Marsh Posté le 20-01-2007 à 11:26:14    

Hello tout le monde !
 
Je fais mes premiers pas dans la manipulation de l'API Windows, et je n'arrive pas à expliquer un résultat que j'obtiens.
 
Le but de la manoeuvre est :
1 - lancer un process via la fonction CreateProcess
2 - attendre que le processus crée soit prêt via la fonction WaitForInputIdle
3 - envoyer un message via PostMessage
 
Ce mécanisme semble correctement fonctionner, mais j'ai l'impression que ça dépend des processus et de la charge de la machine.
Avec le notepad, pas de problème, mais avec d'autres applications, un temps mort de 2 secondes (grouik) est nécessaire.
 
Voilà un petit bout de code fait rapidement en python pour illuster le problème.
Ici, je lance le notepad et j'envoie un F5 pour ajouter la date du jour dans la zone d'édition.
Ça fonctionne sans le sleep, mais pas avec toutes les applications.
 
Une idée ?
 

Code :
  1. import win32api
  2. import win32process
  3. import win32con
  4. import win32event
  5. import win32ui
  6. import time
  7. # Recuperer un pid a partir d'un hwnd
  8. def GetPidFromWindow(hwnd):
  9.      return win32process.GetWindowThreadProcessId(hwnd.GetSafeHwnd())[1]
  10. # On recupere le hwnd a partir de l'id du processus (Microsoft Technet)
  11. def GetWindowHandleFromId(pid):
  12.     """ Retrieve the Window handle (hwnd) from a pid """
  13.     # Grab the first window handle that Window's finds
  14.     tempHwnd = win32ui.FindWindow(None, None)
  15.    
  16.     # Loop until there are no more window handles
  17.     while tempHwnd is not None:
  18.         if tempHwnd.GetParent() is None:
  19.             if pid == GetPidFromWindow(tempHwnd):
  20.                 return tempHwnd
  21.         tempHwnd = tempHwnd.GetWindow(win32con.GW_HWNDNEXT)
  22.     return None
  23. # Creation du process via CreateProcess
  24. appName = None
  25. commandLine = "notepad.exe"
  26. processAttributes = None
  27. threadAttributes = None
  28. inheritHandles = False
  29. creationFlags = win32con.DETACHED_PROCESS
  30. newEnvironnement = None
  31. currentDirectory = None
  32. startupinfo = win32process.STARTUPINFO()
  33. startupinfo.dwFlags = win32process.STARTF_USESHOWWINDOW
  34. startupinfo.wShowWindow = win32con.SW_SHOWMINNOACTIVE
  35. handle, hthread, process_id, thread_id = win32process.CreateProcess(appName,
  36.                                                                     commandLine,
  37.                                                                     processAttributes,
  38.                                                                     threadAttributes,
  39.                                                                     inheritHandles,
  40.                                                                     creationFlags,
  41.                                                                     newEnvironnement,
  42.                                                                     currentDirectory,
  43.                                                                     startupinfo)
  44. # On attend que l'application soit prete
  45. ret = win32event.WaitForInputIdle(handle, win32event.INFINITE)
  46. if ret != 0:
  47.     raise SystemExit("Problem during process wait" )
  48. win32api.CloseHandle(handle)
  49. # On recupere le handle de la fenetre
  50. hwnd = GetWindowHandleFromId(process_id)
  51. # ici, hwnd est le handle sur la fenetre. On lui envoie un message
  52. # time.sleep(5)
  53. hwnd.PostMessage(win32con.WM_COMMAND, 26, 0)


 
edit: Remove trailing spaces mode on ;)


Message édité par Evadream -jbd- le 20-01-2007 à 15:56:05
Reply

Marsh Posté le 20-01-2007 à 11:26:14   

Reply

Marsh Posté le 20-01-2007 à 15:25:47    

drapal, je teste et je regarde dans l'après midi


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 20-01-2007 à 18:18:55    

Merci, c'est très sympa =)

Reply

Marsh Posté le 20-01-2007 à 21:20:06    

ben j'ai testé avec quelques grosses applis (Word, Firefox, FS 2004), et ça fonctionne bien... dans ton cas, quand est-ce que le Sleep() est nécessaire ? avant d'envoyer F5, ou avant de lancer le processus ?


Message édité par Harkonnen le 20-01-2007 à 21:20:29
Reply

Marsh Posté le 20-01-2007 à 21:31:29    

Un grand merci d'avoir pris ton temps pour essayer sur différentes applications. Dans mon cas, je dois faire un sleep juste avant d'envoyer le message.  
 
L'application qui me pose un problème est un logiciel d'imagerie Kodak non disponible au téléchargement :/
 
Si tu as une idée à me suggérer pour trouver ce qui ne va pas... L'idéal sera de pouvoir espionner les messages de l'application dès son démarrage pour voir si elle reçoit ce fameux message, mais je ne sais pas comment procéder.
 
A l'heure actuelle, j'utilise winspector pour espionner ces messages sur un processus déja lancé. Il faudrait que je puisse attacher un espion à un exécutable avant son lancement.
 
Je débute vraiment dans la manipulation de l'api windows, je travaille habituellement dans un environnement Linux/Unix.
 
Encore merci en tout cas !

Message cité 1 fois
Message édité par Evadream -jbd- le 20-01-2007 à 21:32:03
Reply

Marsh Posté le 20-01-2007 à 21:55:36    

Evadream -jbd- a écrit :

Un grand merci d'avoir pris ton temps pour essayer sur différentes applications. Dans mon cas, je dois faire un sleep juste avant d'envoyer le message.  
 
L'application qui me pose un problème est un logiciel d'imagerie Kodak non disponible au téléchargement :/


c'est quoi exactement qui ne fonctionne pas ? ce logiciel ne reçoit pas le message, ou il ne se lance pas sauf si tu Sleep() avant ?

Reply

Marsh Posté le 20-01-2007 à 21:58:26    

J'avais mal compris ta question. Le logiciel se lance, mais ne reçoit pas le message (apparemment, puisque la fonction ne s'exécute pas) à moins que je place ce fameux sleep avant d'envoyer le message.

Reply

Marsh Posté le 20-01-2007 à 21:59:27    

ok, alors essaie plutot d'envoyer le message par PostMessage() au lieu de SendMessage()
 
edit: je voulais dire SendMessage() au lieu de PostMessage() [:ddr555]


Message édité par Harkonnen le 20-01-2007 à 22:01:40
Reply

Marsh Posté le 20-01-2007 à 22:01:25    

C'est ce que je fais malheureusement.  

Code :
  1. hwnd.PostMessage(win32con.WM_COMMAND, 26, 0)

Reply

Marsh Posté le 20-01-2007 à 22:01:58    

je viens d'éditer mon précédent message :whistle:

Reply

Marsh Posté le 20-01-2007 à 22:01:58   

Reply

Marsh Posté le 20-01-2007 à 22:02:46    

Il faudrait que je fasse le test sans passer par les wrappers python afin de voir si le problème ne vient pas d'eux. Ça me semble bizarre cette histoire.

Reply

Marsh Posté le 20-01-2007 à 22:03:05    

et avec SendMessage, ça donne quoi ?

Reply

Marsh Posté le 20-01-2007 à 22:03:45    

Je vais taistai !

Reply

Marsh Posté le 20-01-2007 à 22:04:49    

t'as juste qu'à remplacer PostMessage par SendMessage, les paramètres sont les memes. en remerciant Python au passage d'etre un langage interprété, c'est tellement pratique pour faire des tests

Reply

Marsh Posté le 20-01-2007 à 22:13:44    

Même chose avec SendMessage.  
 
J'ai moi aussi testé avec des applications plus conséquentes que le notepad et ça fonctionne très bien. J'ai plus trop d'idée là.  
 
Je mets un peu de temps désolé, je bosse à distance via vnc, c'est pas terrible.  
 

Reply

Marsh Posté le 20-01-2007 à 22:19:50    

MP

Reply

Marsh Posté le 20-01-2007 à 23:18:30    

Des fois, je devrais réfléchir.
 
L'application doit initialiser des périphériques et dont j'ai besoin pour que ma commande s'effectue bien. Ça doit mettre un peu de temps. L'application est utilisable avant que cette initialisation se fasse, et donc je suis revenu de mon WaitForInputIdle et je poste le message dans le vent.  
 
Je suis quasiment certain qu'il s'agit de ça. L'application reçoit surement un message particulier lors de l'initialisation de ces périphériques, il faudrait que je le trouve. Une autre solution serait de tester si la commande qui m'intéresse n'est pas "grisée" dans les menus de l'application, mais je ne sais si c'est possible. Si tu as une piste, même grossière ;)
 
Hop !

Message cité 1 fois
Message édité par Evadream -jbd- le 20-01-2007 à 23:18:42
Reply

Marsh Posté le 20-01-2007 à 23:37:54    

Evadream -jbd- a écrit :

Des fois, je devrais réfléchir.
 
L'application doit initialiser des périphériques et dont j'ai besoin pour que ma commande s'effectue bien. Ça doit mettre un peu de temps. L'application est utilisable avant que cette initialisation se fasse, et donc je suis revenu de mon WaitForInputIdle et je poste le message dans le vent.  
 
Je suis quasiment certain qu'il s'agit de ça. L'application reçoit surement un message particulier lors de l'initialisation de ces périphériques, il faudrait que je le trouve. Une autre solution serait de tester si la commande qui m'intéresse n'est pas "grisée" dans les menus de l'application, mais je ne sais si c'est possible. Si tu as une piste, même grossière ;)
 
Hop !


Non, je ne crois pas que ce soit ça, car PostMessage() est asynchrone. Elle poste le message dans la file d'attente, et rend la main au module appelant immédiatement, laissant la WndProc de l'application récupérer le message et le traiter. Ton application a donc le temps d'initialiser ses périphériques avant de récupérer le message dans la pompe à messages.
Ce que tu peux faire pour débugger : lance ton application Kodak, utilise OllyDbg (debugger gratuit), et place un point d'arrêt sur le message WM_INIT. Regarde ensuite ce qui est effectué à l'initialisation...


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 20-01-2007 à 23:40:55    

Et moi qui était très fier de mon analyze =) Je vais essayer ce que tu me suggères !

Reply

Marsh Posté le 20-01-2007 à 23:41:56    

par contre, connaissance de l'assembleur obligatoire :D

Reply

Marsh Posté le 21-01-2007 à 00:04:51    

Je craque, via vnc c'est stressant. Je n'ai que des notions en assembleur, ça risque d'être violent. L'application a l'air super bien faite par contre. Je vais voir ce que je peux faire une fois devant la machine.
 
Il n'y aurait pas moyen de lancer une application dans un espion à messages  comme spy++ ou winspector ?
 
Merci une nouvelle fois pour tes conseils !

Reply

Marsh Posté le 21-01-2007 à 00:20:39    

Harkonnen a écrit :

Non, je ne crois pas que ce soit ça, car PostMessage() est asynchrone. Elle poste le message dans la file d'attente, et rend la main au module appelant immédiatement, laissant la WndProc de l'application récupérer le message et le traiter. Ton application a donc le temps d'initialiser ses périphériques avant de récupérer le message dans la pompe à messages.


 
Peut-on imaginer que la WndProc récupère le message, le traite, mais que justement, le traitement ne fait rien puisque la fameuse fonctionnalité n'est pas encore activée ? Auquel cas, il faudrait pouvoir récupérer une éventuelle erreur j'imagine.


Message édité par Evadream -jbd- le 21-01-2007 à 00:29:12
Reply

Marsh Posté le 21-01-2007 à 01:39:48    

Autre question : y'a t'il un moyen de connaître le code de retour d'une commande ? Par exemple, j'envoie un F5 au notepad et je veux savoir si l'action liée à cette commande s'est bien déroulée.  
 
Si j'ai bien compris, comme PostMessage est asynchrone, la valeur de retour nous renseigne uniquement sur le fait que le message a été correctement posté.
 
Bonne nuit =)

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed