| 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)
{
}
|
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)
{
}
|
ShellExecuteEx() et CreateProcess() sont plus souples pour gérer l'attente du processus ou de sa terminaison.
|
| 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)
{
}
else
{
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 " ;
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = TEXT (" print " );
sei.nShow = SW_SHOWNORMAL;
if (ShellExecuteEx (& sei))
{
WaitForSingleObject (sei.hProcess, INFINITE);
}
:: CloseHandle (sei.hProcess);
|
|
| auteur : Farscape |
DWORD dwExitCode = 0 ;
GetExitCodeProcess (piProcessInfo.hProcess, & dwExitCode);
if (dwExitCode = = STILL_ACTIVE)
{
}
|
|
| auteur : Farscape | A l'intérieur du processus lui-même :
En utilisant ExitProcess :
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)
{
TerminateProcess (piProcessInfo.hProcess, 0 ); }
|
|
| 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);
|
|
| 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 ()
{
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: Comment définir un message privé ?
|
lien : Comment définir un message privé ?
|
| 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)
{
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) ;
return 0 ;
}
|
Utilisation de CreateThread():
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
|
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)
{
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)
{
return false ;
}
return true ;
}
private :
CWinThread * m_pThread;
static UINT ThreadFunc (LPVOID pvParam);
} ;
UINT CMyDialog:: ThreadFunc (LPVOID pvParam)
{
CMyDialog * pThis= reinterpret_cast < CMyDialog * > ( pvParam) ;
return 0 ;
}
|
Autre exemple d'implementation :
class CWorkerThread
{
public :
CWorkerThread ();
~ CWorkerThread ();
bool Stop ( DWORD dwTimeoutMs = INFINITE );
DWORD ExecutionTime;
private :
UINT Execute ();
private :
volatile bool m_StopThread;
CWinThread * m_Thread;
static UINT WorkerThreadLauncher (LPVOID pvParam);
} ;
CWorkerThread:: CWorkerThread ():
m_StopThread ( false ),
ExecutionTime ( 0 )
{
this - > m_Thread = AfxBeginThread (WorkerThreadLauncher, this );
}
CWorkerThread:: ~ CWorkerThread ()
{
this - > Stop ();
}
bool CWorkerThread:: Stop ( DWORD dwTimeoutMs )
{
if ( this - > m_Thread ! = NULL )
{
this - > m_StopThread = true ;
if ( :: WaitForSingleObject (
* this - > m_Thread,
dwTimeoutMs ) ! = WAIT_OBJECT_0 )
{
:: TerminateThread ( * this - > m_Thread, 0 );
this - > m_Thread = NULL ;
return false ;
}
this - > m_Thread = NULL ;
}
return true ;
}
UINT CWorkerThread:: WorkerThreadLauncher (LPVOID pvParam)
{
CWorkerThread * pThreadInstance =
reinterpret_cast < CWorkerThread * > (pvParam);
return pThreadInstance- > Execute ();
}
UINT CWorkerThread:: Execute ()
{
DWORD start = :: GetTickCount ();
do
{
:: Sleep ( 1000 );
} while ( this - > m_StopThread = = false ) ;
DWORD end = :: GetTickCount ();
this - > ExecutionTime = end - start;
return 0 ;
}
|
utilisation :
CWorkerThread thread;
if ( thread.Stop () )
{
CString s;
s.Format ( " %d ms " , thread.ExecutionTime );
AfxMessageBox ( s );
}
else
{
AfxMessageBox ( " Le thread a été tué " );
}
|
|
| 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 ()
{
HANDLE m_EndThread = CreateEvent (0 , TRUE, FALSE, 0 );
HANDLE m_WaitThread = CreateEvent (0 , TRUE, FALSE, 0 );
AfxBeginThread (ThreadFunction, this );
}
CWorkerThread:: ~ CWorkerThread ()
{
:: SetEvent (m_EndThread);
:: WaitForSingleObject (m_WaitThread, INFINITE);
:: CloseHandle (m_EndThread);
:: CloseHandle (m_WaitThread);
}
UINT CWorkerThread:: ThreadFunction (LPVOID* pvParam)
{
CWorkerThread * pThis = static_cast (pvParam);
while (true )
{
if (:: WaitForSingleObject (pThis- > m_EndThread, 0 ) = = WAIT_OBJECT_0)
{
:: SetEvent (pThis- > m_WaitThread);
return 0 ;
}
}
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 ?
|
| 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
# include "stdafx.h"
# include <Winsvc.h>
# include "TestService.h"
# ifdef _DEBUG
# define new DEBUG_NEW
# undef THIS_FILE
static char THIS_FILE[] = __FILE__;
# endif
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)
{
:: SetEvent (hTerminateEvent);
return 0 ;
}
_sleep (5000 );
}
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 }
} ;
if (! AfxWinInit (:: GetModuleHandle (NULL ), NULL , :: GetCommandLine (), 0 ))
{
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
}
|
|
| 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
|
| 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 );
|
|
| 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 " ) )
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 );
|
|
| 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 :
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) :
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);
if ( ! :: SetProcessAffinityMask (Process, ProcessAffinity) )
{
Success = false ;
CString Message;
# 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 :
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 :
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...
|
| auteur : Farscape |
DWORD WINAPI CWorkerThread:: ThreadFunc (LPVOID pvParam)
{
CWorkerThread * pThis= reinterpret_cast < CWorkerThread * > ( pvParam) ;
:: PostMessage (AfxGetMainWnd ()- > m_hWnd,WM_CLOSE,0 ,0 );
return 0 ;
}
|
|
| 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 " ))
else
|
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.
|
|