DetailPage-MSS-KB

Base de connaissances

Numéro d'article: 178893 - Dernière mise à jour: jeudi 15 janvier 2004 - Version: 2.1

Ancien nº de publication de cet article : F178893

Sommaire

Résumé

Dans un monde parfait, votre processus pourrait demander à un autre processus, par le biais d'une communication entre processus quelconque, de s'arrêter. Cependant, si vous n'avez pas de maîtrise sur la source de l'application à arrêter, l'arrêt ne pourra pas se dérouler ainsi. Bien qu'il n'existe aucune manière "propre" et garantie d'arrêter une application dans Win32, certaines mesures peuvent vous permettre de vous assurer que cette application utilise la meilleure méthode de nettoyage des ressources.

Plus d'informations

Processus 32 bits (et processus 16 bits sous Windows 95)

Sous Win32, le système d'exploitation promet de nettoyer les ressources détenues par un processus lors de son arrêt. Cela ne signifie toutefois pas que le processus lui-même a eu l'opportunité d'effectuer des vidages finals d'informations sur disque ou d'effectuer des communications finales sur une connexion à distance, ni que les DLL du processus auront l'opportunité d'exécuter leur code PROCESS_DETACH. Pour cette raison, il est généralement préférable d'éviter d'arrêter une application sous Windows 95 et Windows NT.

Si vous devez absolument arrêter un processus, procédez comme suit :
  1. Envoyez un message WM_CLOSE à toutes fenêtres de niveau supérieur détenues par un processus que vous souhaitez arrêter. De nombreuses applications Windows s'arrêtent en réponse à ce message.

    REMARQUE : la réponse d'une application console à WM_CLOSE varie selon qu'elle a installé un gestionnaire de contrôle ou non.

    Utilisez EnumWindows() pour rechercher les handles de vos fenêtres cibles. Dans votre fonction de rappel, vérifiez si l'ID de processus de la fenêtre correspond au processus à arrêter. Pour cela, appelez GetWindowThreadProcessId(). Une fois la correspondance établie, utilisez PostMessage() ou SendMessageTimeout() pour envoyer le message WM_CLOSE à la fenêtre.
  2. Utilisez WaitForSingleObject() pour attendre le handle du processus. Assurez-vous d'attendre avec une valeur de délai d'attente, car il existe de nombreuses situations dans lesquelles WM_CLOSE n'arrêtera pas l'application. Faites en sorte que le délai d'attente soit assez long, avec WaitForSingleObject() ou SendMessageTimeout(), pour que l'utilisateur puisse répondre aux éventuelles boîtes de dialogue créées en réponse au message WM_CLOSE.
  3. Si la valeur de retour est WAIT_OBJECT_0, cela signifie que l'application s'est arrêtée proprement. Si la valeur de retour est WAIT_TIMEOUT, vous devez utiliser TerminateProcess() pour arrêter l'application.

    REMARQUE : si la valeur renvoyée par WaitForSingleObject() est autre que WAIT_OBJECT_0 ou WAIT_TIMEOUT, utilisez GetLastError() pour en déterminer la cause.
Cette méthode procure à l'application les meilleures chances de s'arrêter proprement (si l'on met de côté la communication entre processus et l'intervention de l'utilisateur).

La question 16 bits (sous Windows NT)

Les étapes précédentes fonctionnent avec les applications 16 bits sous Windows 95 ; sous Windows NT, en revanche, les applications 16 bits fonctionnent très différemment.

Sous Windows NT, toutes les applications 16 bits s'exécutent dans une machine DOS virtuelle (VDM, Virtual DOS Machine). Cette VDM s'exécute en tant que processus Win32 (NTVDM) sous Windows NT. Le processus NTVDM possède un ID de processus. Vous pouvez obtenir un handle de ce processus par le biais de OpenProcess(), tout comme pour tout autre processus Win32. Cependant, aucune des applications 16 bits exécutées dans la VDM ne possède d'ID de processus ; vous ne pouvez donc pas obtenir de handle de processus à partir de OpenProcess() pour ces applications. En revanche, chaque application 16 bits dans une VDM possède un handle de tâche et un thread d'exécution 32 bits. Vous pouvez déterminer les ID de handle et de thread en appelant la fonction VDMEnumTaskWOWEx(). Pour plus d'informations, consultez l'article suivant dans la Base de connaissances Microsoft :
175030  (http://support.microsoft.com/kb/175030/ ) PROCÉDURE : Énumerer des applications dans Win32
La première (et la plus simple) option qui se présente à vous lors de l'arrêt d'une application 16 bits sous Windows NT consiste à arrêter l'ensemble du processus NTVDM. Pour cela, procédez comme décrit ci-dessus. Il vous suffit de connaître l'ID de processus du processus NTVDM (pour la procédure à suivre, voir l'article 175030  (http://support.microsoft.com/kb/175030/ ) de la Base de connaissances cité plus haut). L'inconvénient de cette méthode est qu'elle ferme toutes les applications 16 bits en cours d'exécution dans cette VDM. Si cela n'est pas votre objectif, vous devez procéder d'une autre manière.

Si vous souhaitez arrêter une seule application 16 bits dans un processus NTVDM, procédez comme suit :
  1. Envoyez un message WM_CLOSE à toutes les fenêtres de niveau supérieur détenues par le processus et ayant le même ID de thread propriétaire que la tâche 16 bits à arrêter. Le moyen le plus efficace consiste à utiliser EnumWindows(). Dans votre fonction de rappel, vérifiez si l'ID de processus et l'ID de thread de la fenêtre correspondent au processus 16 bits à arrêter. N'oubliez pas que l'ID de processus sera celui du processus NTVDM dans lequel s'exécute l'application 16 bits.
  2. Bien que vous ayez un ID de thread, vous n'avez aucun moyen d'attendre l'arrêt du processus 16 bits. En conséquence, vous devez attendre pendant un laps de temps arbitraire (pour autoriser un arrêt propre), puis essayer d'arrêter tout de même l'application. Si celle-ci s'est déjà arrêtée, alors rien ne sera fait. Si l'application ne s'est pas encore arrêtée, elle le sera.
  3. Arrêtez l'application à l'aide d'une fonction nommée VDMTerminateTaskWOW(), qui se trouve dans Vdmdbg.dll. Cette fonction prend l'ID de processus de la VDM et le numéro de la tâche 16 bits.
Cette méthode vous permet d'arrêter une application 16 bits dans une VDM sous Windows NT. Néanmoins, Windows 16 bits n'est pas très performant lorsqu'il s'agit de nettoyer les ressources d'une tâche arrêtée, tout comme le WOWEcex exécuté dans la VDM. Si vous souhaitez arrêter une application 16 bits Windows NT de la manière la plus propre possible, vous devez envisager l'arrêt de l'ensemble du processus VDM. REMARQUE : si vous démarrez une application 16 bits que vous envisagez d'arrêter ultérieurement, utilisez CREATE_SEPARATE_WOW_VDM avec CreateProcess().

Exemple de code

L'exemple de code implémente les techniques décrites ci-dessus pour les applications 16 bits et 32 bits à l'aide des deux fonctions suivantes : TerminateApp() et Terminate16App(). TerminateApp() prend un ID de processus 32 bits et un délai d'attente (en millisecondes). Terminate16App(). Ces deux fonctions utilisent une liaison explicite vers des fonctions DLL, de sorte qu'elles seront compatibles au niveau binaire sur Windows NT et Windows 95.
   //******************
   //En-tête
   //******************

   #include <windows.h>

   #define TA_FAILED 0
   #define TA_SUCCESS_CLEAN 1
   #define TA_SUCCESS_KILL 2
   #define TA_SUCCESS_16 3

   DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) ;
   DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
                        WORD w16Task, DWORD dwTimeout );

   //******************
   //Source
   //******************

   #include "TermApp.h"
   #include <vdmdbg.h>

   typedef struct
   {
      DWORD   dwID ;
      DWORD   dwThread ;
   } TERMINFO ;

   // Déclare les fonctions de rappel Enum.
   BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) ;

   BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam ) ;

   /*----------------------------------------------------------------
   DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )

   Objet :
      Arrêter un processus 32 bits (ou un processus 16 bits sous Windows 95)

   Paramètres :
      dwPID
         ID du processus à arrêter.

      dwTimeout
         Durée d'attente en millisecondes avant l'arrêt du processus.

   Valeur de retour :
      TA_FAILED - Si l'arrêt a échoué.
      TA_SUCCESS_CLEAN - Si le processus a été arrêté avec WM_CLOSE.
      TA_SUCCESS_KILL - Si le processus a été arrêté avec
         TerminateProcess().
      REMARQUE :  voir l'en-tête de ces définitions.
   ----------------------------------------------------------------*/ 
   DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )
   {
      HANDLE   hProc ;
      DWORD   dwRet ;

      // Si vous ne pouvez pas ouvrir le processus avec des droits PROCESS_TERMINATE,
      // abandonnez tout de suite.
      hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE,
         dwPID);

      if(hProc == NULL)
      {
         return TA_FAILED ;
      }

      // TerminateAppEnum() envoie WM_CLOSE à toutes les fenêtre dont le PID
      // correspond à celui de votre processus.
      EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ;

      // Attend le handle. En cas de signal, parfait ! En cas d'expiration du délai d'attente,
      // vous le terminez.
      if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0)
         dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
      else
         dwRet = TA_SUCCESS_CLEAN ;

      CloseHandle(hProc) ;

      return dwRet ;
   }

   /*----------------------------------------------------------------
   DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
                        WORD w16Task, DWORD dwTimeout )

   Objet :
      Arrêt d'une application Win16.

   Paramètres :
      dwPID
         ID de processus du NTVDM dans lequel s'exécute l'application
         16 bits.

      dwThread
         ID du thread d'exécution de l'application
         16 bits.

      w16Task
         Handle de tâche 16 bits de l'application.

      dwTimeout
         Durée d'attente en millisecondes avant l'arrêt de la tâche.

   Valeur de retour :
      En cas de réussite, renvoie TA_SUCCESS_16
      En cas d'échec, renvoie TA_FAILED.
      REMARQUE :  ces valeurs sont définies dans l'en-tête de cette
      fonction.

   REMARQUE :
      vous pouvez obtenir les ID de tâche et de thread Win16 à l'aide de la fonction
      VDMEnumTaskWOW() ou VDMEnumTaskWOWEx().
   ----------------------------------------------------------------*/ 
   DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
                        WORD w16Task, DWORD dwTimeout )
   {
      HINSTANCE      hInstLib ;
      TERMINFO      info ;

      // Vous appellerez les fonctions par l'intermédiaire de liaisons explicites,
      // de sorte que ce code sera compatible au niveau binaire sur
      // les plateformes Win32.
      BOOL (WINAPI *lpfVDMTerminateTaskWOW)(DWORD dwProcessId,
         WORD htask) ;

      hInstLib = LoadLibraryA( "VDMDBG.DLL" ) ;
      if( hInstLib == NULL )
         return TA_FAILED ;

      // Obtient les adresses de procédures.
      lpfVDMTerminateTaskWOW = (BOOL (WINAPI *)(DWORD, WORD ))
         GetProcAddress( hInstLib, "VDMTerminateTaskWOW" ) ;

      if( lpfVDMTerminateTaskWOW == NULL )
      {
         FreeLibrary( hInstLib ) ;
         return TA_FAILED ;
      }

      // Envoie un message WM_CLOSE à toutes les fenêtres dont l'ID et le thread
      // correspondent.
      info.dwID = dwPID ;
      info.dwThread = dwThread ;
      EnumWindows((WNDENUMPROC)Terminate16AppEnum, (LPARAM) &info) ;

      // Attend.
      Sleep( dwTimeout ) ;

      // Puis arrête.
      lpfVDMTerminateTaskWOW(dwPID, w16Task) ;

      FreeLibrary( hInstLib ) ;
      return TA_SUCCESS_16 ;
   }

   BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam )
   {
      DWORD dwID ;

      GetWindowThreadProcessId(hwnd, &dwID) ;

      if(dwID == (DWORD)lParam)
      {
         PostMessage(hwnd, WM_CLOSE, 0, 0) ;
      }

      return TRUE ;
   }

   BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam )
   {
      DWORD      dwID ;
      DWORD      dwThread ;
      TERMINFO   *termInfo ;

      termInfo = (TERMINFO *)lParam ;

      dwThread = GetWindowThreadProcessId(hwnd, &dwID) ;

      if(dwID == termInfo->dwID && termInfo->dwThread == dwThread )
      {
         PostMessage(hwnd, WM_CLOSE, 0, 0) ;
      }

      return TRUE ;
   }

Les informations contenues dans cet article s'appliquent au(x) produit(s) suivant(s):
  • Microsoft Win32 Application Programming Interface sur le système suivant
    • Microsoft Windows 95
    • Microsoft Windows NT 4.0
    • Microsoft Windows 2000 Standard Edition
    • Système d'exploitation : Microsoft Windows XP
    • Microsoft Windows XP Home Edition
    • Microsoft Windows XP Media Center Edition
    • Microsoft Windows XP Professional
    • Microsoft Windows XP Tablet PC Edition
Mots-clés : 
kbhowto kbthread kbkernbase kbgrpdskernbase KB178893
L'INFORMATION CONTENUE DANS CE DOCUMENT EST FOURNIE PAR MICROSOFT SANS GARANTIE D'AUCUNE SORTE, EXPLICITE OU IMPLICITE. L'UTILISATEUR ASSUME LE RISQUE DE L'UTILISATION DU CONTENU DE CE DOCUMENT. CE DOCUMENT NE PEUT ETRE REVENDU OU CEDE EN ECHANGE D'UN QUELCONQUE PROFIT.
Partager
Options de support supplémentaire
Forums du support Microsoft Community
Nous contacter directement
Trouver un partenaire Microsoft Certified Partner
Microsoft Store