IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Gestion des Processus et des Threads
        Comment créer un nouveau processus ?
        Comment attendre la fin d'exécution d'un processus ?
        comment savoir si le processus est toujours actif ?
        Comment détruire un processus ?
        Comment retrouver l'emplacement d'exécution du processus en cours ?
        Comment travailler sur des objets MFC à partir d'un thread de travail (working thread) ?
        Comment créer un thread de travail ?
        Comment arrêter un thread de travail ?
        Comment créer un programme service ?
        Comment afficher un MessageBox dans un service ?
        Comment récupérer la valeur de retour d'un exe ?
        Comment récupérer la valeur de retour d'un exe déjà ouvert ?
        Comment affecter un Thread à une unité de calcul spécifique (CPU ou Core) ?
        Comment fermer l'application à partir d'un thread de travail ?
        Comment savoir si une application est déjà lancée ?



Comment créer un nouveau processus ?
Mise à jour le 21/05/2006[haut]
auteur : Farscape
En utilisant les fonctions :

ShellExecute() ShellExecuteEx() ou CreateProcess()

Exemples:

ShellExecute() : lancement de word

HINSTANCE nerror;
CString strApp="WINWORD";
nerror=ShellExecute(AfxGetMainWnd()->m_hWnd, NULL, strApp, NULL, NULL, SW_SHOWNORMAL);
if(reinterpret_cast<int>(nerror) <= 32)
{
    CString mess;
    mess.Format("Erreur de lancement de l'application:\n%s Erreur n°:%d",(const char *)strApp,nerror);
    AfxMessageBox(mess,MB_ICONEXCLAMATION);
}
ShellExecuteEx(): lancement de notepad:

SHELLEXECUTEINFO ExecuteInfo;    
memset(&ExecuteInfo, 0, sizeof(ExecuteInfo));
    
ExecuteInfo.cbSize       = sizeof(ExecuteInfo);
ExecuteInfo.fMask        = 0;                
ExecuteInfo.hwnd         = 0;                
ExecuteInfo.lpVerb       = "open";                      
ExecuteInfo.lpFile       = "c:\\windows\\notepad.exe";
ExecuteInfo.lpParameters = "c:\\mydir\\toto.txt";
ExecuteInfo.lpDirectory  = 0;
ExecuteInfo.nShow        = SW_SHOW;
ExecuteInfo.hInstApp     = 0;    
if(ShellExecuteEx(&ExecuteInfo) == FALSE)
{
// erreur
}
CreateProcess(): lancement de notepad.

STARTUPINFO         siStartupInfo;
PROCESS_INFORMATION piProcessInfo;

memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if(CreateProcess(NULL,"c:\\windows\\notepad.exe c:\\mydir\\toto.txt",0,0,FALSE,
                     CREATE_DEFAULT_ERROR_MODE,0,0,                              
                     &siStartupInfo,&piProcessInfo) == FALSE)
{
// erreur
}
ShellExecuteEx() et CreateProcess() sont plus souples pour gérer l'attente du processus ou de sa terminaison.


Comment attendre la fin d'exécution d'un processus ?
auteur : Farscape
Dans le cas de CreateProcess() :

STARTUPINFO         siStartupInfo;
PROCESS_INFORMATION piProcessInfo;

memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if(CreateProcess("c:\\windows\\notepad.exe",
                           "c:\\mydir\\toto.txt",0,0,FALSE,
                           CREATE_DEFAULT_ERROR_MODE,0,0,                              
                           &siStartupInfo,&piProcessInfo) == FALSE)
{
// erreur
}
else
{
   // attente
   WaitForSingleObject(piProcessInfo.hProcess, INFINITE);

   ::CloseHandle(piProcessInfo.hThread);
   ::CloseHandle(piProcessInfo.hProcess);
}
Dans le cas de ShellExecuteEx() :
Exemple attente que l'impression d'un document soit terminée :

   SHELLEXECUTEINFO sei; 
   ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO)); 
   sei.cbSize = sizeof(SHELLEXECUTEINFO ); 

   sei.lpFile = "mondocument.doc"; 
   // attention ce flag est important ici !
   sei.fMask = SEE_MASK_NOCLOSEPROCESS; 
   sei.lpVerb = TEXT("print"); 
   sei.nShow = SW_SHOWNORMAL; 
   if(ShellExecuteEx(&sei)) 
   { 
      WaitForSingleObject(sei.hProcess, INFINITE); 
   }
   ::CloseHandle(sei.hProcess);

comment savoir si le processus est toujours actif ?
auteur : Farscape

DWORD dwExitCode = 0;
GetExitCodeProcess(piProcessInfo.hProcess, &dwExitCode);
if(dwExitCode == STILL_ACTIVE) //process toujours présent ?
{
// toujours la.
}

Comment détruire un processus ?
auteur : Farscape
A l'intérieur du processus lui-même :
En utilisant ExitProcess :

ExitProcess(0); // code d'erreur de sortie 0 .
de l'extérieur il faudra envoyer un WM_QUIT au thread principal de l'application :

PostThreadMessage(piProcessInfo.dwThreadId, WM_QUIT, 0, 0);
Note:Pour la variable piProcessInfo voir la question Comment créer un nouveau processus ?

Un processus créé par CreateProcess():

Le problème c'est que le thread principal de l'application peut ne pas répondre à PostThreadMessage (voir note d'infos plus bas) dans ce cas il faut procéder un peu différemment :

PostThreadMessage(piProcessInfo.dwThreadId, WM_QUIT, 0, 0);
WaitForSingleObject(piProcessInfo.hProcess, 2000);
      
DWORD dwExitCode = 0;
GetExitCodeProcess(piProcessInfo.hProcess, &dwExitCode);
if(dwExitCode == STILL_ACTIVE) //process toujours présent ?
{
  // en dernier recours forcé à la fermeture du process avec un code d'erreur à zéro pour l'exemple
  TerminateProcess(piProcessInfo.hProcess, 0);                                                  }


Comment retrouver l'emplacement d'exécution du processus en cours ?
auteur : Farscape
En utilisant la fonction GetModuleFileName:

WINAPI DWORD GetModuleFileName( 
HMODULE hModule,
LPWSTR lpFilename, 
DWORD nSize);
si hModul est nul la fonction retourne le chemin complet de l'exécutable utilisé pour créer le processus appelant.
Exemple:

char szFilename[255];
GetModuleFileName(NULL,szFilename,sizeof(szFilename));
AfxMessageBox(szFilename);

Comment travailler sur des objets MFC à partir d'un thread de travail (working thread) ?
auteur : Farscape
Explications :
Dans un processus win32 tous les threads travaillent dans le même espace mémoire et peuvent accéder aux variables globales.
En multi-thread les MFC stockent les tables sur les fenêtres et objets GDI localement sur le thread principal grâce au système TLS :thread-local-storage, justement pour éviter des accès intempestifs des différents threads.

Donc bien retenir la règle suivante :
On ne peut pas partager des objets MFC liés aux fenêtres ou d'objets GDI entre plusieurs threads de travail.
On passera le handle de fenêtre en paramètre dans la fonction AfxBeginThread pour établir le lien avec la fenêtre de traitement.

void CSdisamplesView::OnButton1() 
{
// TODO: Add your control notification handler code here
            AfxBeginThread(TheThread,GetSafeHwnd(),THREAD_PRIORITY_NORMAL) ;
}
A partir du thread de travail on enverra des messages à la fenêtre pour réaliser les traitements

UINT TheThread(LPVOID pParam)
{

::PostMessage((HWND)pParam,WM_MY_MSG_PRIVE,0,0) ;

return 0 ;
}
Notes: La fonction thread peut être définie dans une classe si elle est déclarée statique.
Dans le cas d'utilisation conjointe avec des boîtes de dialogue modales, il est préférable de poster (PostMessage) le message plutôt que de l'envoyer (SendMessage), afin d'éviter un problème de réentrance avec la pompe à messages du thread principal.
voir aussi:faq Comment définir un message privé ?

lien : faq Comment définir un message privé ?

Comment créer un thread de travail ?
Mise à jour le 10/09/2005[haut]
auteurs : Farscape, Aurelien.Regat-Barrel
Il y a plusieurs manières de créer un thread de travail (worker thread) :

  • _beginthreadex(),_beginthread() (C run-time library)
  • CreateThread() (Win32 API)
  • AfxBeginThread() (MFC)
Les exemples suivants montrent comment utiliser les différentes fonctions.

Utilisation de _beginthreadex():
                                 
unsigned long _beginthreadex( void *security, 
                                 unsigned stack_size, 
                                 unsigned ( __stdcall *start_address )( void * ),
                                 void *arglist, unsigned initflag,
                                 unsigned *thrdaddr );
Pré-requis:
Include :process.h Système :Win 95, Win NT
Link :Bibliothèque multithread statique Libcmt.lib ou la version dll MSVCRT.LIB.

class CWorkerThread
{
public:
    CWorkerThread()
    {
        m_uiID     = 0;
        m_ulHandle = 0;
    }
    
    bool InitThread()
    {
        m_ulHandle = _beginthreadex(0,
                                    0,
                                    ThreadFunc,
                                    this,
                                    0,
                                    &m_uiID);
        if(!m_ulHandle)
        {
            // Impossible de créer le thread !
            return false;
        }
        return true;              
    }
    
private:
    unsigned int  m_uiID;
    unsigned long m_ulHandle;
    
    static unsigned int __stdcall ThreadFunc(void *pvParam);
};

unsigned int __stdcall CWorkerThread::ThreadFunc(void *pvParam)
{
  CWorkerThread  *pThis=reinterpret_cast< CWorkerThread *>( pvParam) ;
  // Votre code
  //
   return 0;
}
Utilisation de CreateThread():

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
  SIZE_T dwStackSize,                       // initial stack size
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
  LPVOID lpParameter,                       // thread argument
  DWORD dwCreationFlags,                    // creation option
  LPDWORD lpThreadId                        // thread identifier
);
Pré-requis:
Include : Déclarée dans Winbase.h. include Windows.h.
Système:Windows NT/2000/XP: Inclus dans Windows NT 3.1 et plus
Link :Bibliothèque Kernel32.lib.

class CWorkerThread
{
public:
    CWorkerThread()
    {
        m_dwID = 0;
        m_hThread    = 0;
    }
    
    ~CWorkerThread()
    {
        CloseHandle(m_hThread);
    }
    
    bool InitThread()
    {
        m_hThread = CreateThread(0,
            0,
            ThreadFunc,
            this,
            0,
            &m_dwID);
        if(!m_hThread)
        {
            // Impossible de créer le thread !
            return false;
        }
        return true;              
    }
    
private:
    DWORD  m_dwID;
    HANDLE m_hThread;
    
    static DWORD WINAPI ThreadFunc(LPVOID pvParam);
};

DWORD WINAPI CWorkerThread::ThreadFunc(LPVOID pvParam)
{
    CWorkerThread  *pThis=reinterpret_cast< CWorkerThread *>( pvParam) ;

    return 0;
}
Utilisation de AfxBeginThread():

CWinThread* AfxBeginThread( 
AFX_THREADPROC pfnThreadProc, 
LPVOID pParam, 
int nPriority = THREAD_PRIORITY_NORMAL, 
UINT nStackSize = 0, 
DWORD dwCreateFlags = 0, 
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

CWinThread* AfxBeginThread( 
CRuntimeClass* pThreadClass, 
int nPriority = THREAD_PRIORITY_NORMAL, 
UINT nStackSize = 0, 
DWORD dwCreateFlags = 0, 
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
Pré-requis:
Include : déclarée dans Afxwin.h
Système: Win 95, Win NT ,WINCE 2.0 et plus.

class CMyDialog : public CDialog
{
public:
    CMyDialog(CWnd* pParent = NULL)
        : CDialog(CMyDialog::IDD, pParent)
    {
        m_pThread = NULL;
    }
    
    bool InitThread()
    {
        m_pThread = AfxBeginThread(ThreadFunc, this);
        if(!m_pThread)
        {
            // Impossible de créer le thread !
            return false;
        }
        return true;              
    }
    
private:
    CWinThread *m_pThread;            
    
    static UINT ThreadFunc(LPVOID pvParam);
};

UINT CMyDialog::ThreadFunc(LPVOID pvParam)
{
    CMyDialog  *pThis=reinterpret_cast< CMyDialog *>( pvParam) ;
    // Votre code
    //
    return 0 ;
}
Autre exemple d'implementation :

class CWorkerThread
{
public:
    CWorkerThread(); // crée le thread et démarre son exécution
    ~CWorkerThread(); // arrêter le thread si ce n'est pas fait

    // demande de s'arrêter au thread
    // si on lui donne un timeout, et que ce dernier expire,
    // la fonction tue le thread et renvoie false
    // renvoie true si le thread s'est terminé proprement
    bool Stop( DWORD dwTimeoutMs = INFINITE );

    // exemple de valeur retournée par le thread
    // son temps d'exécution en ms
    DWORD ExecutionTime;

private:
    // Execute contient votre code à exécuter dans le thread
    UINT Execute();

private:
    volatile bool m_StopThread; // arrêt demandé
    CWinThread * m_Thread; // thread créé

    static UINT WorkerThreadLauncher(LPVOID pvParam);
};

CWorkerThread::CWorkerThread():
    m_StopThread( false ),
    ExecutionTime( 0 )
{
    // depart du thread
    this->m_Thread = AfxBeginThread(WorkerThreadLauncher, this);
}

CWorkerThread::~CWorkerThread()
{
    this->Stop();
}

bool CWorkerThread::Stop( DWORD dwTimeoutMs )
{
    if ( this->m_Thread != NULL )
    {
        // demander de s'arrêter
        this->m_StopThread = true;
        // attendre qu'il soit arrêté pour la durée spécifiée
        if ( ::WaitForSingleObject(
                *this->m_Thread,
                dwTimeoutMs ) != WAIT_OBJECT_0 )
        {
            // échec de l'attente : le thread ne s'est pas terminé
            // on le tue
            ::TerminateThread( *this->m_Thread, 0 );
            this->m_Thread = NULL;
            return false;
        }
        // tout s'est bien passé
        this->m_Thread = NULL;
    }
    return true;
}

// point d'entrée du thread
UINT CWorkerThread::WorkerThreadLauncher(LPVOID pvParam)
{
    CWorkerThread *pThreadInstance =
        reinterpret_cast<CWorkerThread *>(pvParam);
    return pThreadInstance->Execute();
}

// IMPORTANT: si vous appelez des fonctions membres
// ou modifiez des données membres de CWorkerThread
// veillez à que leur utilisation soit thread safe
// car Execute() s'execute dans un autre thread contrairement
// aux autres fonctions qui sont appelées depuis le thread principal
// En particulier, il ne faut surtout pas appeler Stop()
UINT CWorkerThread::Execute()
{
    // exemple de code à exécuter

    DWORD start = ::GetTickCount();
   
    do
    {   
        // code de traitement
        ::Sleep( 1000 ); // simuler une opération de 1 sec
    } while ( this->m_StopThread == false ) ;

    DWORD end = ::GetTickCount();

    // renvoyer un résultat : temps d'exécution
    this->ExecutionTime = end - start;
   
    return 0;
}
utilisation :

    // créer et lancer le thread
    CWorkerThread thread;

    // l'arrêter
    if ( thread.Stop() )
    {
        // récupérer le résultat
        CString s;
        s.Format( "%d ms", thread.ExecutionTime );
        AfxMessageBox( s );
    }
    else
    {
        AfxMessageBox( "Le thread a été tué" );
    } 

Comment arrêter un thread de travail ?
auteur : Farscape
Cette Q/R s'applique sur les threads initiés par la fonction AfxBeginThread donc dans un contexte MFC.
Pour l'arrêt d'un thread plusieurs cas sont à envisager :
Un thread peut s'arrêter tout seul en terminant sa fonction principale.
Le thread comporte une boucle de traitement en continu, et l'action de sortie du thread vient de l'extérieur.

Une réponse pour traiter ce cas serait d'utiliser la fonction TerminateThread. cette fonction à éviter.
Le thread est immédiatement stoppé certes mais elle comporte des inconvénients rédhibitoires :
Notamment, elle ne libère pas la mémoire des variables dynamiques :
Pour fixer les idées le code ci-dessus provoquera une perte de mémoire (memory leaks) .

UINT CWorkerThread::ThreadFunction(LPVOID* pvParam)
{
    CString str ;
    while(true)
    {
          //
    } ;
    return 0 ;
}
Les dlls chargées par le thread ne seront pas libérées !
A éviter donc.

Une technique couramment utilisée pour l'arrêt d'un thread consiste à utiliser des objets de type event :
L'exemple ci-dessus emploie deux events (objet événement) un pour signaler la fin du thread et un deuxième pour attendre que celui-ci soit effectivement terminé.

class CWorkerThread
{
public:
  CWorkerThread();
  ~CWorkerThread();

private:
  HANDLE m_hEndThread;
  HANDLE m_hWaitThread;

  static UINT ThreadFunction(LPVOID* pvParam);
};

CWorkerThread::CWorkerThread()
{
  // Creation des  events
  HANDLE m_EndThread = CreateEvent(0, TRUE, FALSE, 0);
  HANDLE m_WaitThread = CreateEvent(0, TRUE, FALSE, 0);

  // depart du thread
  AfxBeginThread(ThreadFunction, this);
}

CWorkerThread::~CWorkerThread()
{
  // declenche la fin du thread
  ::SetEvent(m_EndThread);

  // attend que le thread soit terminé
  ::WaitForSingleObject(m_WaitThread, INFINITE);

  // fermeture dans handles
  ::CloseHandle(m_EndThread);
  ::CloseHandle(m_WaitThread);
}

UINT CWorkerThread::ThreadFunction(LPVOID* pvParam)
{
  CWorkerThread *pThis = static_cast(pvParam);

  while(true)
  {
    // attente evenement de fin du thread. -> l'objet doit être ?signalé ?
   // WaitForSingleObject  renvoie WAIT_OBJECT_0 si l'objet est signalé.
    if(::WaitForSingleObject(pThis->m_EndThread, 0) == WAIT_OBJECT_0)

    {
      // signale l'objet event d'attente et sort.
      ::SetEvent(pThis->m_WaitThread);
      return 0;
    }

    // Votre code de traitement.
  }
  return 0;
}
Dans certains cas l'emploi de la fonction WaitForMultipleObjects s'avère nécessaire si plusieurs objets sont à contrôler voir pour exemple la classe de communication série dans la faq : Comment travailler avec le port série ?


Comment créer un programme service ?
Mise à jour le 20/05/2006[haut]
auteur : Farscape
Pour créer le service il faudra choisir un projet de type Win 32/Console avec support ou non des MFC .
L'exemple ci-dessus gère le processus d'installation du service.
Un squelette de service permettant l'arrêt la pause et le redémarrage du service.
Et enfin la possibilité de le désinstaller.
Le traitement du service devra être placé dans le thread de travail prévu à cet effet.

Les points clefs du service :
Le service démarre avec la fonction StartServiceCtrlDispatcher
Qui contient une structure SERVICE_TABLE_ENTRY qui comprend le nom du service et sa fonction main principale.
Celle-ci va procéder aux initialisations du service notamment ouvrir un lien avec le SCM : service control manager, le handle retourné permettra d'informer celui-ci des étapes d'initialisations ou des notifications d'arrêt du service.
Le SCM disposera d'un point d'entrée dans le programme pour signaler les événements d'arrêt, de pause ou de redémarrage du service.
Tout cela est initié par la fonction RegisterServiceCtrlHandler.
Toujours dans la fonction main principale le thread de travaille (worker thread) sera créé ainsi que les objets events permettant l'attente de fin de traitement du thread avec la fonction WaitForSingleObject.

pour faciliter les tests en mode debug le programme est executable directement dans l'ide, le thread de travail est créé en dehors de tout contexte de service.
Pour installer le service on appelera le programme avec l'argument install.
Pour supprimer le service on utilisera l'argument delete

le code ci-dessous installera le service TestService ,voir la variable SERVICE_NAME

// TestService.cpp : Defines the entry point for the console application.
//
// Exemple de service windows comprenat l'installation l'execution 
// et la desinstallation du service.
//  
// Farscape le 16/09/2004 : farscape-dev@tiscali.fr
//
#include "stdafx.h"
#include <Winsvc.h>

#include "TestService.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;

using namespace std;

BOOL SendStatusToSCM(DWORD dwCurrentState,
                     DWORD dwWin32ExitCode,
                     DWORD dwServiceSpecificExitCode,
                     DWORD dwCheckPoint,
                     DWORD dwWaitHint);


TCHAR SERVICE_NAME[]= TEXT( "TestService" );

HANDLE hTerminateEvent=NULL;
HANDLE hTerminateThread=NULL;
HANDLE ThreadHandle = NULL;

SERVICE_STATUS_HANDLE ServiceStatusHandle;

BOOL bPauseService=FALSE;
BOOL bRunningService=FALSE;

// ------------------------------------------------------
void ErrorHandler(const char *s,DWORD err)
{
    cout << s << endl;
    cout << "Erreur Numéro: " << err << endl;
    ExitProcess(err);
}
// ------------------------------------------------------
DWORD ServiceThread(LPDWORD param)
{
    while(1)
    {
        if(::WaitForSingleObject(hTerminateThread, 0) == WAIT_OBJECT_0)
        {
            // signale l'objet event d'attente et sort.
            ::SetEvent(hTerminateEvent);
            return 0;
        }
        // placer le code de votre service ici
        _sleep(5000); // juste pour l'exemple.
    }
    return 0;
}
// ------------------------------------------------------
BOOL InitService()
{
    DWORD id=0;
    ThreadHandle = CreateThread(0,0,(LPTHREAD_START_ROUTINE)ServiceThread,NULL,0,&id);
    if(ThreadHandle==0)
    {
        if(ServiceStatusHandle)  SendStatusToSCM(SERVICE_STOPPED,0,0,0,0);
        if(hTerminateEvent) CloseHandle(hTerminateEvent);
        if(hTerminateThread) CloseHandle(hTerminateThread);
        
        ErrorHandler("Erreur sur la mise en place du service",0);
        return FALSE;
    }
    bRunningService=TRUE;
    return TRUE;
}
// ------------------------------------------------------
void ResumeService()
{
    bPauseService = FALSE;
    ResumeThread(ThreadHandle);
}
// ------------------------------------------------------
void PauseService()
{
    bPauseService = TRUE;
    SuspendThread(ThreadHandle);
}
// ------------------------------------------------------
void StopService()
{
    bRunningService=FALSE;
    SetEvent(hTerminateThread);
}
// ------------------------------------------------------
BOOL SendStatusToSCM(DWORD dwCurrentState,
                     DWORD dwWin32ExitCode,
                     DWORD dwServiceSpecificExitCode,
                     DWORD dwCheckPoint,
                     DWORD dwWaitHint)
{
    BOOL bSuccess;
    SERVICE_STATUS ServiceStatus;
    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ServiceStatus.dwCurrentState= dwCurrentState;
    
    ServiceStatus.dwControlsAccepted=0;
    if(dwCurrentState != SERVICE_START_PENDING)        
        ServiceStatus.dwControlsAccepted= SERVICE_ACCEPT_STOP |
        SERVICE_ACCEPT_SHUTDOWN|         
        SERVICE_ACCEPT_PAUSE_CONTINUE;
    
    ServiceStatus.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
    if(!dwServiceSpecificExitCode) ServiceStatus.dwWin32ExitCode=dwWin32ExitCode;
    
    ServiceStatus.dwServiceSpecificExitCode=dwServiceSpecificExitCode;
    
    ServiceStatus.dwCheckPoint = dwCheckPoint;
    ServiceStatus.dwWaitHint = dwWaitHint;
    
    bSuccess = SetServiceStatus(ServiceStatusHandle,&ServiceStatus);
    if(!bSuccess) StopService();
    return bSuccess;
}
// ------------------------------------------------------
void ServiceCtrlHandler(DWORD controlCode)
{
    DWORD currentState= 0;
    BOOL  bSuccess;
    switch(controlCode)
    {
    case SERVICE_CONTROL_STOP:
        currentState= SERVICE_STOP_PENDING;
        bSuccess= SendStatusToSCM(SERVICE_STOP_PENDING,NO_ERROR,0,1,5000);
        StopService();
        return;
        
    case SERVICE_CONTROL_PAUSE:
        if(bRunningService && !bPauseService)
        {
            bSuccess= SendStatusToSCM(SERVICE_PAUSE_PENDING,NO_ERROR,0,1,1000);
            PauseService();
            currentState= SERVICE_PAUSED;
        }
        break;
    case SERVICE_CONTROL_CONTINUE:
        if(bRunningService && bPauseService)
        {
            bSuccess= SendStatusToSCM(SERVICE_CONTINUE_PENDING,NO_ERROR,0,1,1000);
            ResumeService();
            currentState= SERVICE_RUNNING;
        }
        break;
    case SERVICE_CONTROL_INTERROGATE:break;
        
    case SERVICE_CONTROL_SHUTDOWN:return;
        
    default:break;
    }
    SendStatusToSCM(currentState,NO_ERROR,0,0,0);
}
// ------------------------------------------------------
void Terminate(DWORD error)
{
    if(ServiceStatusHandle) SendStatusToSCM(SERVICE_STOPPED,error,0,0,0);
    if(ThreadHandle) CloseHandle(ThreadHandle);
    if(hTerminateEvent) CloseHandle(hTerminateEvent);
    if(hTerminateThread) CloseHandle(hTerminateThread);
}
// ------------------------------------------------------
void AddService()
{
    SC_HANDLE newService,scm;
    CString strPath;
    char szFilename[255];
    
    GetModuleFileName(NULL,szFilename,sizeof(szFilename));
    strPath=szFilename;
    
    scm= OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
    if(!scm) ErrorHandler("Dans OpenScManager",GetLastError());
    newService= CreateService(scm,SERVICE_NAME,
        SERVICE_NAME,
        SERVICE_ALL_ACCESS,
        SERVICE_WIN32_OWN_PROCESS,
        SERVICE_DEMAND_START,
        SERVICE_ERROR_NORMAL,
        strPath,
        0,0,0,0,0);
    if(!newService)ErrorHandler("Dans CreateService",GetLastError());
    else cout << "Service " << SERVICE_NAME << " Installé\n";
}
// ------------------------------------------------------
void DeleteService()
{
    SC_HANDLE newService,scm;
    CString strPath;
    char szFilename[255];
    
    GetModuleFileName(NULL,szFilename,sizeof(szFilename));
    strPath=szFilename;
    
    scm= OpenSCManager(0,0,SC_MANAGER_ALL_ACCESS);
    if(!scm) ErrorHandler("Dans OpenScManager",GetLastError());
    
    newService=OpenService(scm,SERVICE_NAME,SERVICE_ALL_ACCESS| DELETE);
    if(newService)DeleteService(newService);
    if(!newService)ErrorHandler("Dans DeleteService",GetLastError());
    else cout << "Service "<< SERVICE_NAME << " Supprimé\n";
}
// ------------------------------------------------------
void ServiceMain(DWORD argc,LPTSTR *argv)
{
    BOOL bSuccess;
    
    ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME,(LPHANDLER_FUNCTION)ServiceCtrlHandler);
    if(!ServiceStatusHandle)
    {
        Terminate(GetLastError());
        return;
    }
    bSuccess= SendStatusToSCM(SERVICE_START_PENDING,NO_ERROR,0,1,5000);
    if(!bSuccess)
    {
        Terminate(GetLastError());
        return;
    }
    hTerminateEvent = CreateEvent(0,TRUE,FALSE,0);
    if(!hTerminateEvent)
    {
        Terminate(GetLastError());
        return;
    }
    hTerminateThread = CreateEvent(0,TRUE,FALSE,0);
    if(!hTerminateThread)
    {
        Terminate(GetLastError());
        return;
    }
    bSuccess= SendStatusToSCM(SERVICE_START_PENDING,NO_ERROR,0,2,1000);
    if(!bSuccess)
    {
        Terminate(GetLastError());
        return;
    }
    bSuccess = InitService();
    if(!bSuccess)
    {
        Terminate(GetLastError());
        return;
    }
    bSuccess= SendStatusToSCM(SERVICE_RUNNING,NO_ERROR,0,0,0);
    if(!bSuccess)
    {
        Terminate(GetLastError());
        return;
    }
    WaitForSingleObject(hTerminateEvent,INFINITE);
    Terminate(0);
}
//------------------------------------------------------------------------------------------
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    SERVICE_TABLE_ENTRY serviceTable[]=
    {
        {SERVICE_NAME,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
        {NULL , NULL}
    };
    
    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: change error code to suit your needs
        ErrorHandler("Fatal Error: MFC initialization failed",0);
        return 1;
    }
#ifdef _DEBUG
    hTerminateEvent = CreateEvent(0,TRUE,FALSE,0);
    hTerminateThread = CreateEvent(0,TRUE,FALSE,0);
    DWORD id=0;
    ThreadHandle = CreateThread(0,0,(LPTHREAD_START_ROUTINE)ServiceThread,NULL,0,&id);
    WaitForSingleObject(hTerminateEvent,INFINITE);
    ServiceStatusHandle=NULL;
    Terminate(0);
    return 0;
#else
    
    CString strCmdLine;
    if(argc>1) strCmdLine=argv[1];
    strCmdLine.MakeUpper();
    
    if(strCmdLine=="INSTALL")
    {
        AddService();
        return 0;
    }
    if(strCmdLine=="DELETE")
    {
        DeleteService();
        return 0;
    }
    
    BOOL bSuccess;
    bSuccess= StartServiceCtrlDispatcher(serviceTable);
    if(!bSuccess) ErrorHandler("Dans StartServiceCtrlDispatcher",GetLastError());
    return bSuccess;
#endif
}


Comment afficher un MessageBox dans un service ?
Créé le 22/01/2007[haut]
auteur : Farscape
Un service par définition est silencieux, il ne doit pas avoir d'interaction avec le bureau pour des raisons de sécurité.
Voir la note MSDN Interactive Services à ce sujet.

Dans le cas d'un service non interactif on pourra utiliser un MessageBox pour communiquer sur le bureau, en utilisant l'attribut MB_SERVICE_NOTIFICATION


Comment récupérer la valeur de retour d'un exe ?
auteur : nico-pyright(c)
En utilisant l'API GetExitCodeProcess à partir du handle de process de l'application dont on veut connaitre le retour.
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );
    if( !CreateProcess( NULL, "monExe.exe", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) 
    {
        return 0;
    }
    WaitForSingleObject( pi.hProcess, INFINITE );

    DWORD retour;
    GetExitCodeProcess(pi.hProcess,&retour);

    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

Comment récupérer la valeur de retour d'un exe déjà ouvert ?
auteur : nico-pyright(c)
On utilise la même API GetExitCodeProcess , seulement, l'obtention du handle de process de l'application est légèrement différente.
    HANDLE hSnapShot;
    PROCESSENTRY32 uProcess;
    BOOL r;
    short PID = 0;
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
    uProcess.dwSize = (DWORD) sizeof(PROCESSENTRY32);
    r = Process32First(hSnapShot, &uProcess);
    do 
    {
        if ( strstr(uProcess.szExeFile, "monExe") ) // on compare au nom de l'executable
            PID = (short) uProcess.th32ProcessID;
        r = Process32Next(hSnapShot, &uProcess);
    }
    while ( r );
    CloseHandle(hSnapShot);
    if ( PID == 0)  return false;
    HANDLE hTemp;
    hTemp = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD) PID);
    WaitForSingleObject( hTemp, INFINITE );

    DWORD retour;
    GetExitCodeProcess(hTemp,&retour);

    CloseHandle( hTemp );

Comment affecter un Thread à une unité de calcul spécifique (CPU ou Core) ?
Créé le 20/05/2006[haut]
auteur : matazz
Toutes les méthodes suivantes sont en static dans une classes COutils
Tout d'abord il faut récupérer le nombre de CPU de l'ordinateur :

//**************************************************************************
//GetCPUCount
//**************************************************************************
int COutils::GetCPUCount()
{
SYSTEM_INFO SysInfos;

::GetSystemInfo(&SysInfos);

return SysInfos.dwNumberOfProcessors;
}
Ensuite il faut autoriser le processus de votre application à tourner sur plusieurs CPU (la valeur maximale de CPU est 32) :

//**************************************************************************
//Modifie le process Courant afin de pouvoir tourner sur plusieurs CPU
//int nCPU : nombre de CPU estimé (incluant les dual Core) Max = 32
//**************************************************************************
bool COutils::SetMultiCPUCapability(int nCPU, bool Verbose)
{
    boolSuccess= true;
    intNbCPU= COutils::GetCPUCount();
    
    if (nCPU > NbCPU)
    {
        CString Message;
        Message.Format("COutils::SetMultiCPUCapability -> Erreur : \
            Demande de fonctionnement sur %d CPU pour une machine à %d CPU\r\n", nCPU, NbCPU);
#ifdef _DEBUG
        TRACE (Message);
#endif
        if (Verbose)
        {AfxMessageBox(Message,  MB_OK); }
        Success = false;
    }
    else
    {
        DWORDProcessAffinity = 0xFFFFFFFF;
        HANDLEProcess= ::GetCurrentProcess();
        ProcessAffinity= ProcessAffinity >> (32 -nCPU);
        //Possibilité de tourner sur les CPU 1, 2, 3 ou 4 -> jusqu'a 32
        //CPU  4321
        //0x1 -> 0001
        //0x3 -> 0011
        //0x7 -> 0111
        //0xF -> 1111
        
        if ( !::SetProcessAffinityMask(Process, ProcessAffinity) )
        {
            Success = false;
            CString Message;
            //mettre un message erreur
            //....
#ifdef _DEBUG
            TRACE (Message);
#endif
            if (Verbose)
            {AfxMessageBox(Message,  MB_OK); }
        }
    }
    
    return Success;
}
Enfin , il suffit d'affecter le thread au CPU que l'on désire par :

//**************************************************************************
//SetThreadOnCPU
//**************************************************************************
bool COutils::SetThreadOnCPU(HANDLE hThread, int CPUNumber)
{
    bool Success = true;
    
    if (CPUNumber <= COutils::GetCPUCount() )
    {
        DWORD ThreadAffinity = 0x00000001;
        
        if (CPUNumber != 1 )
        { ThreadAffinity =  ThreadAffinity << (CPUNumber - 1); }
        
        if ( !::SetThreadAffinityMask (hThread, ThreadAffinity) )
        {Success = false;}
    }
    else
    {Success = false;}
    return Success;
}
ou par :

//**************************************************************************
//SetThreadOnCPU
//**************************************************************************
bool COutils::SetThreadOnCPU (CWinThread *Thread, int CPUNumber)
{
return COutils::SetThreadOnCPU(Thread->m_hThread, CPUNumber);
}
Dans le même genre, il y a SetThreadIdealProcessor qui permet d'affecter une préférence, mais Windows gardera le choix d'affecter ou non le thread au CPU si il est trop occupé...
Attention car si vous affectez 15 Thread au CPU n°1, Windows le fera et votre application sera encore plus lente, donc à utiliser en connaissance de cause...


Comment fermer l'application à partir d'un thread de travail ?
Créé le 20/05/2006[haut]
auteur : Farscape

DWORD WINAPI CWorkerThread::ThreadFunc(LPVOID pvParam)
{
    CWorkerThread  *pThis=reinterpret_cast< CWorkerThread *>( pvParam) ;
  // ici le traitement du thread 
  // ......................

   // sortie du thread
   ::PostMessage(AfxGetMainWnd()->m_hWnd,WM_CLOSE,0,0); 
    return 0;
}
Il suffira d'envoyer un PostMessage à la MainFrame en utilisant le handle de fenêtre.
Voir explications dans ce post: faq Comment travailler sur des objets MFC à partir d'un thread de travail (working thread) ?


Comment savoir si une application est déjà lancée ?
Créé le 20/05/2006[haut]
auteur : nico-pyright(c)
si on connait le nom du processus, on peut par exemple regarder dans la liste de processus pour savoir s'il est lancé ou pas.
on peut utiliser cette fonction
bool isProcessRunning(char *processName)
{
    HANDLE hSnapShot;
    PROCESSENTRY32 uProcess;
    BOOL r;
    short PID = 0;

    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
    uProcess.dwSize = (DWORD) sizeof(PROCESSENTRY32);

    r = Process32First(hSnapShot, &uProcess);
    do 
    {
        if ( strstr(uProcess.szExeFile, processName) )
            PID = (short) uProcess.th32ProcessID;

        r = Process32Next(hSnapShot, &uProcess);
    } while ( r );
    CloseHandle(hSnapShot);
    return (PID != 0);
}
par exemple, pour savoir si Firefox est lancé, on ferra :
if (isProcessRunning("firefox.exe"))
// lancé
else
// non lancé
Remarque : on utilise ici la fonction strstr pour rechercher une chaîne dans une autre, en tenant compte de la casse. On pourrait très bien envisager de changer cette fonction pour rechercher en ignorant la casse ou bien pour avoir un nom exact, etc ..



Consultez les autres F.A.Q.


Valid XHTML 1.0 TransitionalValid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2004 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.