Compatibilité de ShellExecuteEx sur Vista ?? [Résolu]

Signaler
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008
-
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008
-
Bonjour à tous.
Je développe un logiciel sous VisualC++ et j'ai besoin de lancer une page web silencieusement puis la fermer ensuite pour ne pas laisser plein de processus inutile.
j'utilise donc la fonction ShellExecuteEx avec
SHELLEXECUTEINFO ExecInfo;
 memset(&ExecInfo, 0, sizeof(ExecInfo));
 ExecInfo.lpFile = "C:\\Program Files\\Internet Explorer\\iexplore.exe";
 ExecInfo.cbSize = sizeof(ExecInfo);
 ExecInfo.lpVerb = "open";
 ExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; //permet d'obtenir le Handle pour le fermer ensuite
 ExecInfo.nShow = SW_HIDE;   //pour ouvrir silencieusement
 ExecInfo.lpParameters = (LPCTSTR) l_Parametres;  //en parametre l'adresse web à ouvrir
 ExecInfo.lpDirectory = NULL;

// et pour fermer la fenetre:
TerminateProcess(ExecInfo.hProcess,0);

Alors cela fonction très bien sous Windows XP tant dis que sous Vista la fenetre s'ouvre "normalement" et elle ne se ferme pas lors de l'appel de TerminateProcess !

Je cherche donc un moyen que cela fonctionne sous XP et Vista.
Merci d'avance pour vos lumieres

15 réponses

Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
Déjà il faut bannir TeminateProcess, c'est pour les cas d'urgence mais pas en situation normale comme ici, le dossier TEMP des utilisateurs de tes progs te remerciera de ne pas le polluer.
Suffit d'envoyer un WM_CLOSE pour fermer proprement.

FULL exemple avec une vulgaire dialog IDD_APP et un bouton IDCANCEL de fermeture.
En fermant TA dialog, ça fermera le process IE créé par ta dialog.
Testé XP et Vista, prog de 3Ko.

#define _WIN32_WINNT 0x0600
#define _WIN32_IE 0x0700
#include <windows.h>
#include "resource.h"


HWND hie = 0;
char pszcmd[] = ""C:\\Program Files\\Internet Explorer\\iexplore.exe" http://cppfrance.com";


BOOL CALLBACK EnumWindIeProc(HWND hwnd, LPARAM lParam)
{
  char szclass[8];
  if(GetWindowThreadProcessId(hwnd, 0) != (DWORD)lParam) goto defRET;
  if(7 != GetClassName(hwnd, szclass, 8)) goto defRET;
  if(*((DWORD*) szclass) != 'rFEI') goto defRET; // IEFrame
  hie = hwnd;
  return 0;
defRET: return 1; // POUR CONTINUER
}


int __stdcall LanceIE()
{
  PROCESS_INFORMATION pi;
  STARTUPINFO si;
  DWORD *pdw, n;
  pi.dwProcessId = 0;
  pi.dwThreadId = 0;
  pi.hProcess = 0;
  pi.hThread = 0;
  pdw = (DWORD*) &si;
  n = sizeof(STARTUPINFO) / 4;
  do {
    *pdw++ = 0;
  } while(--n);
  si.cb = sizeof(STARTUPINFO);
  si.dwFlags = STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_HIDE;
  if(!CreateProcess(0, pszcmd, 0, 0, 0, 0, 0, 0, &si, &pi)) return 0;
  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
  Sleep(500);
  EnumWindows(EnumWindIeProc, (LPARAM) pi.dwThreadId);
  return (int) hie;
}


BOOL CALLBACK AppDlgProc(HWND hdlg, UINT mssg, WPARAM wParam, LPARAM lParam)
{
  switch(mssg) {
    case WM_INITDIALOG:
      SetClassLongPtr(hdlg, GCLP_HICON, (long)LoadIcon(0, IDI_APPLICATION));
      if(!LanceIE()) goto dlgEND;
      return 1;
    case WM_COMMAND:
      if(wParam != IDCANCEL) break;
      PostMessage(hie, WM_CLOSE, 0, 0);
  dlgEND:
      EndDialog(hdlg, 0);
  }
  return 0;
}


#pragma comment(linker, "/entry:myWinMain")
__declspec(naked) void __stdcall myWinMain()
{
  __asm {
    push    0
    call    dword ptr GetModuleHandle
    push    eax
    push    offset AppDlgProc
    push    0
    push    IDD_APP
    push    eax
    call    dword ptr DialogBoxParam
    push    0
    call    dword ptr ExitProcess
  }
}

ciao...
BruNews, MVP VC++
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
ExecInfo.lpDirectory = NULL;
Ne sert à rien, deja eu memset avant.


ShellExecuteEx ne garantit pas de retourner un hProcess, c'est indiqué dans MSDN. Comme IE a changé depuis XP, tu tombes justement sur ce cas.


Il faut utilmiser CreateProcess.

ciao...
BruNews, MVP VC++
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

Bonjour,

sur les conseils de BruNews, j'ai utilisé CreateProcess comme suit:

STARTUPINFO si;
 ZeroMemory( &si, sizeof(si) );
 si.cb = sizeof(si);
 si.dwFlags = STARTF_USESHOWWINDOW;   //pour utiliser le wShowWindow
 si.wShowWindow = SW_HIDE;   //pour ouvrir silencieusement



 PROCESS_INFORMATION pi;
 ZeroMemory( &pi, sizeof(pi) );



CreateProcess( NULL,
   ""C:\\Program Files\\Internet Explorer\\iexplore.exe" http://www.cppfrance.com"
,
  NULL,
  NULL,
  TRUE,
  NORMAL_PRIORITY_CLASS,
  NULL,
  NULL,
  &si,
  &pi);



 CString error;      //juste pour voir si une erreur est générée
 error.Format("CreateProcess failed (%d)\n", GetLastError() );
 AfxMessageBox(error);

 TerminateProcess(pi.hProcess,0);
 CloseHandle(pi.hThread);



Alors ces quelques lignes de code fonctionne sous XP (le processus est créé silencieusement puis fermé ensuite, comme je le souhaite) mais ne fonctionne toujours pas sous Vista  (la fenetre s'ouvre normalement et ne se ferme pas)

Si quelqu'un à une solution je l'écoute avec impatience
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

j'ai bien noté pour le TerminateProcess


merci pour le full exemple que je vais tenter d'intégrer à mon projet,
en attendant je vois qu'il faut fermer les CloseHandle(pi.hThread) et
  CloseHandle(pi.hProcess)... ET en plus envoyer un message via PostMessage d'où l'utilité du EnumWindIeProc,
admettons,
mais alors je comprends pas du tout pourquoi il y a de l'assembleur dans void __stdcall myWinMain()
quelques explications la dessus pourraient enrichir ma culture cppienne 
(surtout que je ne vois pas où je vais mettre ce morceau d'assembleur dans mon projet en MFC Dialog Based...)

Je te tiens au courant si j'ai réussi à intégrer tout ca dans mon projet.
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
Ne t'intéresse que le LanceIE().

Le reste c'est pour ceux qui codent sans surcouches interprétatives. Mon exe de 3 Ko peut se refiler à nimporte qui tel quel, il tourne à tout coup alors qu'avec MFC il faut un setup pour poser les runtime, une vraie plaie.

ciao...
BruNews, MVP VC++
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

alors j'ai plusieurs choses qui apparaissent qui m'empeche de compiler:
1)
warning C4005: '_WIN32_IE' : macro redefinition
        C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\commctrl.h(17) : see previous definition of '_WIN32_IE'
ca à la limite ce n'est pas grave, si tu as une soluce pour éviter de l'avoir an warning à chaque fois...
pour info la première définition est #define _WIN32_IE 0x0400

2)
error C2660: 'PostMessageA' : function does not take 4 parameters
là c'est assez ennuyeux, en effet l'assistant me propose:
PostMessage(UINT message, WPARAM wParam, LPARAM lParam)
où j'indique "hie" ??

3)
error C2664: 'EnumWindows' : cannot convert parameter 1 from 'int (struct HWND__ *,long)' to 'int (__stdcall *)(struct HWND__ *,long)'
        None of the functions with this name in scope match the target type
et là l'erreur parle d'elle meme, mais je vois pas comment satisfaire le compilateur ?

Merci encore pour te pencher sur mon problème.
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

ok d'ac pour ces infos, je n'avais pas vu ton post pendant que j'écrivais le miens


mais subsiste quand meme le problème d'envoyer un message à IE ??
mon post avec mes erreurs compilateur reste d'actualité
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

pour l'erreur compilateur 3)
error C2664: 'EnumWindows' : cannot convert parameter 1 from 'int (struct HWND__ *,long)' to 'int (__stdcall *)(struct HWND__ *,long)'
        None of the functions with this name in scope match the target type
 j'ai trouvé le remède,
c'est que par reflexe, la fonctionBOOL CALLBACK EnumWindIeProc(HWND hwnd, LPARAM lParam) ecrite dans le .cpp je l'ai déclaré dans le .h
et il ne faut pas (à priori) le faire pour ce type de fonction, car je n'ai plus cette erreur lorsque je ne la déclare pas dans le .h.

il me reste toujours les 2 autres (voir post de 20h10) que je ne trouve pas de réponse
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
Faut préfixer les appels API par :: pour expliquer au compilo que tu veux appeler la vraie fonction du système, pas celle wrappée par MFC.
Ex:
::PostMessage(hie, WM_CLOSE, 0, 0);
idem pour le reste.

#define _WIN32_WINNT 0x0600
ça tu devrais pouvoir t'en passer ici, sinon faut voir où est situé le
#include <windows.h>
dans stdafx.h peut-être et en ce cas ça se met la ligne au dessus.

ciao...
BruNews, MVP VC++
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

alors, en mettant le #define en tete de liste dans le stdafx.h
et en mettant l'opérateur de résolution de portée (::) pour le PostMessage (ca j'aurais dû y pensé par moi meme, j'men veux )
et en ne déclarant pas la fonction CALLBACK dans le .h
et bien je n'ai plus d'erreur ni de warning, tout compil bien
et le process ce lance bien (en "caché") et se ferme bien à l'appel de PostMessage... sous XP
MAIS sous Vista, ca ne marche toujours pas !! (la fenetre s'ouvre normalement et ne se ferme pas ensuite)

moi qui pensais voir la fin, et bien nan, IE7 ne veux rien savoir (pour info c'est Vista Business)

alors BruNews, je ne sais pas si tu as encore d'autres pistes...?
car là je sens que tu va en faire une affaire personnelle! nan? lol
Merci d'avance !
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
Non désolé, ça marche sur Vista premium et je ne poserai pas un autre Vista.
Tout ceci indique que c'est dépendant des options et réglages de IE et donc qu'on ne doit pas faire un soft ainsi, je pense que c'est tout ce qu'il faut en conclure. Il est certain qu'un prog n'est absolument pas obligé de lire sa cmdLine au démarrage.

ciao...
BruNews, MVP VC++
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

bon et bien tant pis, je vais continuer à chercher quand meme au cas où,
sinon je vais essayer avec firefox (une version "portable") que je devrai joindre avec mon exe...
bref du bidouillage en somme, mais je vous tiens au courant quand j'aurai une solution

Malgrès tout, Merci BruNews !
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

alors avec firefox, c'est presque pire...


laissons de coté pour l'instant le fait d'ouvrir la page de manière silencieuse


j'ai étudié ton code en détail (fais quelques essais) et j'ai trouvé (en tout cas je pense) pourquoi sous Vista la fenetre ne se referme pas.
tu met: if(GetWindowThreadProcessId(hwnd, 0) != (DWORD)lParam) goto defRET;
afin de "filtrer" les fenetres qui ont étaient créé par mon programme uniquement, sous XP c'est ok, mais avec Vista (et IE7 aussi peut etre) et bien à aucune fenetre n'as comme parent mon programme, c'est pour ca qu'il ne la trouve pas et donc ne peux pas la fermer.
meme en précisant TRUE à bInheritHandles du CreateProcess.
je constate sous vista, via le gestionnaire de tache, que lorsque mon programme lance IE, et bien dans les premiers instant j'ai 2 processus de créés puis un des 2 se ferme pour ne laisser place à un seul processus. c'est ce qui me fait dire, c'est comme si IE7a s'ouvrait pour ouvrir un autre IE7b et IE7a se ferme, ca expliquerait pourquoi le Handle parent est perdu et qu'il ne tient pas compte du paramètre SW_HIDE (que je passe à IE7a), vu que ce n'est plus mon prog qui ouvre IE7 mais c'est IE7 qui l'ouvre à ma place

as-tu une idée? (une question de sécurité? peut etre) (reste la question: pourquoi ca marche chez toi alors?)
désolé pour le monologue, mais il faut bien expliquer...
Messages postés
21042
Date d'inscription
jeudi 23 janvier 2003
Statut
Modérateur
Dernière intervention
21 août 2019
25
Je n'ai qu'1 seul process IE qui se crée sur mon Vista donc pourquoi 2 sur le tien, aucune idée et pas de temps d'investigation à perdre dessus.

http://brunews.com/IEhide.zip
Ferme toute instance de IE et ensuite lance l'exe, sait-on jamais...

ciao...
BruNews, MVP VC++
Messages postés
10
Date d'inscription
samedi 14 avril 2007
Statut
Membre
Dernière intervention
6 février 2008

alors pour clore le sujet (car je sens que cette histoire t'énerve quelque peu)
j'ai essayé l'exe que tu m'as mis en lien (je t'en remercie)
sous XP: le process s'ouvre en caché et le bouton "Quit" ferme bien le process, tout va bien donc
sous Vista: et bien je suis rassuré, je ne suis pas fou, ton exe fait la meme chose que le mien, c'est a dire qu'il lance IE normalement (non caché) et pire encore la fenetre de ton exe ne s'affiche meme pas ! donc je ne peux pas cliquer sur le bouton "quit" puisque ta fenetre n'apparait pas (et n'apparait pas non plus dans les processsus)
pour info: meme avec ton exe j'ai 2 process qui s'ouvre, et un des deux se ferme dans la seconde qui suit (comme mon prog)

A croire donc qu'un vista à l'autre, le résultat n'est pas identique.

NB1: j'y pense, sur mon vista je n'ai jamais fait de mise à jour, ceux sont peut être des bugues qui sont corrigés dans des mises à jour que tu aurait installés...?

NB2: je vais "accepter la réponse" malgres tout de ton "full" code car il est bien et rigoureux meme si pour moi il ne fonctionne pas chez moi pour l'instant !


 


Merci pour tout !