IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ VC++ et MFCConsultez toutes les FAQ

Nombre d'auteurs : 20, nombre de questions : 545, dernière mise à jour : 5 avril 2013  Ajouter une question

 

Cette faq a été réalisée pour répondre aux questions les plus fréquement posées sur le forum Développement Visual C++

Je tiens à souligner que cette faq ne garantit en aucun cas que les informations qu'elle contient sont correctes ; Les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou si vous souhaitez devenir redacteur, lisez ceci.

Sur ce, je vous souhaite une bonne lecture. Farscape

SommaireGestion des Processus et des Threads (15)
précédent sommaire suivant
 

En utilisant les fonctions :

ShellExecute() ShellExecuteEx() ou CreateProcess()

Exemples:

ShellExecute() : lancement de word

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
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:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  
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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
  
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.

Mis à jour le 21 mai 2006 farscape

Dans le cas de CreateProcess() :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  
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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
   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);

Mis à jour le 5 avril 2013 farscape

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
DWORD dwExitCode = 0; 
GetExitCodeProcess(piProcessInfo.hProcess, &dwExitCode); 
if(dwExitCode == STILL_ACTIVE) //process toujours présent ? 
{ 
// toujours la. 
}

Mis à jour le 5 avril 2013 farscape

A l'intérieur du processus lui-même :
En utilisant ExitProcess :

Code c++ : Sélectionner tout
1
2
  
ExitProcess(0); // code d'erreur de sortie 0 .
de l'extérieur il faudra envoyer un WM_QUIT au thread principal de l'application :

Code c++ : Sélectionner tout
1
2
  
PostThreadMessage(piProcessInfo.dwThreadId, WM_QUIT, 0, 0);
Noteour 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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
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);                                                  }
Article de référence sur MSDN: HOWTO: Terminate an Application "Cleanly" in Win32

Mis à jour le 5 avril 2013 farscape

En utilisant la fonction GetModuleFileName:

Code c++ : Sélectionner tout
1
2
3
4
5
  
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:

Code c++ : Sélectionner tout
1
2
3
4
  
char szFilename[255]; 
GetModuleFileName(NULL,szFilename,sizeof(szFilename)); 
AfxMessageBox(szFilename);

Mis à jour le 5 avril 2013 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 multithread 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.

Code C++ : Sélectionner tout
1
2
3
4
5
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

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
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.

Mis à jour le 5 avril 2013 farscape

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():

Code c++ : Sélectionner tout
1
2
3
4
5
6
  
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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  
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():

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
  
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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  
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():

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  
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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  
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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  
    // 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é" ); 
    }

Mis à jour le 10 septembre 2005 Aurelien.Regat-Barrel 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).

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
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é.

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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 ?

Mis à jour le 5 avril 2013 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

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// 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 
}

L'application de test : TestService.zip

Mis à jour le 20 mai 2006 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.

Mis à jour le 22 janvier 2007 farscape

En utilisant l'API GetExitCodeProcess à partir du handle de process de l'application dont on veut connaitre le retour.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    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 );

Mis à jour le 5 avril 2013 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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    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 );

Mis à jour le 5 avril 2013 nico-pyright(c)

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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
  
//************************************************************************** 
//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) :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
//************************************************************************** 
//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 n° 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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  
//************************************************************************** 
//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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
  
//************************************************************************** 
//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...

Mis à jour le 20 mai 2006 matazz

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
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 : Comment travailler sur des objets MFC à partir d'un thread de travail (working thread) ?

Mis à jour le 20 mai 2006 farscape

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

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 fera :

Code C++ : Sélectionner tout
1
2
3
4
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.

Mis à jour le 20 mai 2006 nico-pyright(c)

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

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 © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.