Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Services Windows et Threads

23 réponses
Avatar
condo4
Bonjour,

Voici mon probl=E8me :
J'ai une application Windows d=E9velopp=E9 en PowerBuilder (le drame....)
que je souhaite passer en Service, mais pas avec un truc a la srvany
ou autre car j'ai besoin de faire un "arret" propre de mon service, et
en plus, mon service doit en lancer ou arreter d'autre...

Alors pour resoudre ce probl=E8me, j'ai d=E9velopp=E9 une DLL en C qui me
permet d'utiliser les API Win32 pour d=E9clarer une serie de Threads qui
r=E9ponde au norme de Windows NT pour =EAtre vu comme un service (install=
=E9
avec InstSrv du toolkit).

Tout fonctionne =E0 merveille pour une instance de ce service.
Le probl=E8me, cette application dois =EAtre lanc=E9 6 fois, avec un passage
d'argument qui sera different a chaque fois :
MON_SERVICE_SRV1 : mon_exe.exe SRV1
MON_SERVICE_SRV2 : mon_exe.exe SRV2
MON_SERVICE_SRV3 : mon_exe.exe SRV3
MON_SERVICE_SRV4 : mon_exe.exe SRV4
MON_SERVICE_SRV5 : mon_exe.exe SRV5
MON_SERVICE_SRV6 : mon_exe.exe SRV6

Bon, je lance SRV1, =E7a marche... le 2 impecable, le 3 nickel... je me
dis tout marche impecable ;-)
Je lance le 4......... et la rien.....
Je stop le 3, je lance le 4 impecable....
En fait, apr=E8s plusieurs essai, je ne peut en lancer QUE 3 en m=EAme
temps!!!!!
Alors, ma question c'ets pourquoi ?? et comment =E9viter ce probl=E8me!!!

J'ai essay=E9 de copier 6 fois dans des dossiers differents l'exe et la
dll, mais le probl=E8me reste le m=EAme.

Comment peut-t-il y avoir un lien entre mes differents services ?
Je vous donne les source de ma DLL; dites moi si vous voyez un truc
anormal....
Dans PowerBuilder, je lance d=E9s le d=E9but de l'appli un
InitServiceThread
R=E9guli=E8rement (toute les secondes) je test TestServiceThread
Quand l'arret est demand=E9 (STOP_PENDING) j'arrete mon application
powerbuilder, et =E0 la fin de l'arret, j'appel DoneServiceThread
__________________________________________________________
HANDLE hThreadSynchro =3D NULL; // Flag
synchro d'arr=EAt du Thread
HANDLE ThreadHandleSynchro =3D NULL; // Handle du
thread synchro
HANDLE hThreadService =3D NULL; // Flag
Controleur de service
HANDLE ThreadHandleService =3D NULL; // Handle du
thread service
HANDLE hThreadDispatcher =3D NULL;
HANDLE hThreadStop =3D NULL;
SERVICE_STATUS ssStatus; // current status of
the service
SERVICE_STATUS_HANDLE sshStatusHandle;
DWORD dwGlobalErr;

BOOL far pascal TestServiceThread(){
return(hThreadSynchro !=3D (HANDLE)NULL);
}

VOID far pascal DoneServiceThread(){
if (hThreadSynchro !=3D (HANDLE)NULL)
SetEvent(hThreadSynchro);
if (hThreadService !=3D (HANDLE)NULL)
SetEvent(hThreadService);
if (hThreadDispatcher !=3D (HANDLE)NULL)
SetEvent(hThreadDispatcher);
if (hThreadStop !=3D (HANDLE)NULL)
SetEvent(hThreadStop);
}

VOID far pascal InitServiceThread(CHAR far *pszAppName){
hThreadSynchro =3D CreateEvent(NULL, TRUE, FALSE, NULL);
ThreadHandleSynchro =3D (HANDLE)_beginthread(ThreadSynchro, 4096,
pszAppName); // ResetEvent hThreadSynchro
}

VOID ThreadSynchro(CHAR far *pszAppName){
DWORD dwWait;
ThreadHandleService =3D
(HANDLE)_beginthread(ThreadServiceCtrlDispatcher, 4096, pszAppName);
if (hThreadSynchro !=3D (HANDLE)NULL) dwWait =3D
WaitForSingleObject(hThreadSynchro, INFINITE); // wait indefinitely
if (hThreadSynchro !=3D (HANDLE)NULL) CloseHandle(hThreadSynchro);
hThreadSynchro =3D NULL;
return;
}

VOID ThreadServiceCtrlDispatcher(CHAR far *pszAppName){
DWORD dwWait;
SERVICE_TABLE_ENTRY DispatchTable[] =3D {{NULL,
(LPSERVICE_MAIN_FUNCTION)ServiceMain},{NULL,NULL}};
DispatchTable->lpServiceName =3D pszAppName;
if (!StartServiceCtrlDispatcher(DispatchTable)) {/
*StopService("Erreur StartServiceCtrlDispatcher.");*/ }
hThreadDispatcher =3D CreateEvent(NULL, TRUE, FALSE, NULL);
if (hThreadDispatcher !=3D (HANDLE)NULL) dwWait =3D
WaitForSingleObject(hThreadDispatcher, INFINITE); // wait indefinitely
if (hThreadDispatcher !=3D (HANDLE)NULL)
CloseHandle(hThreadDispatcher);
hThreadDispatcher =3D (HANDLE)NULL;
return;
}

VOID ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv){
DWORD dwWait;
sshStatusHandle =3D RegisterServiceCtrlHandler(*lpszArgv,
(LPHANDLER_FUNCTION)ServiceCtrl);
if (!sshStatusHandle) goto cleanup;
ssStatus.dwServiceType =3D
SERVICE_WIN32_OWN_PROCESS; // SERVICE_STATUS members that don't
change in example
ssStatus.dwServiceSpecificExitCode =3D 0;
if (!ReportStatusToSCMgr(SERVICE_START_PENDING , NO_ERROR, 1,
3000)) goto cleanup;
hThreadService =3D CreateEvent(NULL, TRUE, FALSE, NULL);
if (hThreadService =3D=3D (HANDLE)NULL) goto cleanup;
if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 2,
3000)) goto cleanup;
if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0, 0)) goto
cleanup;
dwWait =3D WaitForSingleObject(hThreadService, INFINITE); //
wait indefinitely
cleanup:
if (hThreadService !=3D (HANDLE)NULL) CloseHandle(hThreadService);
hThreadService =3D NULL;
if ((HANDLE)sshStatusHandle !=3D (HANDLE)NULL)
(VOID)ReportStatusToSCMgr(SERVICE_STOPPED, dwGlobalErr, 0, 0);
return;
}

VOID ServiceCtrl(DWORD dwCtrlCode){
DWORD dwWait;
DWORD dwState =3D SERVICE_RUNNING;
unsigned short i;
switch(dwCtrlCode) {
case SERVICE_CONTROL_PAUSE: // Pause the service if it is
running. non impl=E9ment=E9
break;
case SERVICE_CONTROL_CONTINUE: // Resume the paused service.
non impl=E9ment=E9
break;
case SERVICE_CONTROL_STOP: {// Stop the service.
dwState =3D SERVICE_STOP_PENDING;
if ((HANDLE)sshStatusHandle !=3D (HANDLE)NULL)
ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 1,
5000); // waithint
hThreadStop =3D CreateEvent(NULL, TRUE, FALSE,
NULL); // no name
if (hThreadSynchro !=3D (HANDLE)NULL)
SetEvent(hThreadSynchro);
for( i=3D2; i<20; i++){
if (hThreadStop !=3D (HANDLE)NULL) dwWait =3D
WaitForSingleObject(hThreadStop, 3000);
if (hThreadStop !=3D (HANDLE)NULL) {
CloseHandle(hThreadStop);
hThreadStop =3D NULL;
break;
}
else
ReportStatusToSCMgr(SERVICE_STOP_PENDING,NO_ERROR,i,5000); // On
informe le SCM qu'on est toujours en train de travailler
}
}
break;

case SERVICE_CONTROL_INTERROGATE:// Update the service status.
break;

default: // invalid control code
break;
}
// send a status response.
if (dwState !=3D SERVICE_STOP_PENDING) if((HANDLE)sshStatusHandle !=3D
(HANDLE)NULL) ReportStatusToSCMgr(dwState, NO_ERROR, 0, 0);
return;
}

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwCheckPoint, DWORD dwWaitHint){
BOOL fResult =3D FALSE;
if ((HANDLE)sshStatusHandle !=3D (HANDLE)NULL){ // Disable control
requests until the service is started.
if (dwCurrentState =3D=3D SERVICE_START_PENDING)
ssStatus.dwControlsAccepted =3D 0;
else ssStatus.dwControlsAccepted =3D SERVICE_ACCEPT_STOP;
ssStatus.dwCurrentState =3D dwCurrentState;
ssStatus.dwWin32ExitCode =3D dwWin32ExitCode;
ssStatus.dwCheckPoint =3D dwCheckPoint;
ssStatus.dwWaitHint =3D dwWaitHint;
if (!(fResult =3D SetServiceStatus(sshStatusHandle, &ssStatus)))
{}
Sleep(100);
}
return fResult;
}
____________________________________________________

10 réponses

1 2 3
Avatar
condo4
Complement d'info :
Le démarage du 4 service plante sur le sleep(100) de
ReportStatusToSCMgr.
J'ai mis des log sur cette fonction de cette façon :

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwCheckPoint, DWORD dwWaitHint){
BOOL fResult = FALSE;
fprintf(logFile,"tReportStatusToSCMgr(%i,%i,%i,%i)
n",dwCurrentState,dwWin32ExitCode,dwCheckPoint,dwWaitHint);
fflush(logFile);
if ((HANDLE)sshStatusHandle != (HANDLE)NULL){ // Disable control
requests until the service is started.
fprintf(logFile,"tReportStatusToSCMgr sshStatusHandle !=
(HANDLE)NULLn");
fflush(logFile);
if (dwCurrentState == SERVICE_START_PENDING)
ssStatus.dwControlsAccepted = 0;
else ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
ssStatus.dwCurrentState = dwCurrentState;
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
ssStatus.dwCheckPoint = dwCheckPoint;
ssStatus.dwWaitHint = dwWaitHint;
if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))
{
fprintf(logFile,"tReportStatusToSCMgr : ERROR
SetServiceStatusn");
fflush(logFile);
}
else{
fprintf(logFile,"tReportStatusToSCMgr : SetServiceStatus
OKn");
fflush(logFile);
}
Sleep(100);
}
else{
fprintf(logFile,"tReportStatusToSCMgr sshStatusHandle ==
(HANDLE)NULLn");
fflush(logFile);
}
fprintf(logFile,"ReportStatusToSCMgr(%i,%i,%i,%i) return %i
n",dwCurrentState,dwWin32ExitCode,dwCheckPoint,dwWaitHint,fResult);
fflush(logFile);
return fResult;
}



Voici le resultat lorsque tout fonctionne :
ThreadServiceCtrlDispatcher(MON_SERVICE_SRV3)
ServiceMain(1,MON_SERVICE_SRV3)
ServiceMain state 1
ReportStatusToSCMgr(2,0,1,3000)
ReportStatusToSCMgr sshStatusHandle != (HANDLE)NULL
ReportStatusToSCMgr : SetServiceStatus OK
Et lors du problème sur le 4eme service :
ThreadServiceCtrlDispatcher(MON_SERVICE_SRV4)
ServiceMain(1,MON_SERVICE_SRV4)
ServiceMain state 1
ReportStatusToSCMgr(2,0,1,3000)
ReportStatusToSCMgr sshStatusHandle != (HANDLE)NULL
ReportStatusToSCMgr : SetServiceStatus OK

Conclusion, tout indentique!!!

Donc, a croire que Windows n'a pas vu que je m'était mis en
SERVICE_START_PENDING et que lorsque je me met en Sleep, le service
manager me kill violament.....
Ce qui est vraiment bizzard, c'est que les autres fois, ça marche bien
(les 3 autres démarrages...)

A vos idée / suggestion ?? ;-)

@+Fab
Avatar
condo4
Ben encore une précision qui est encore plus étrange...

A la base, mon application ouvrait une fenetre (assez simple
d'ailleur).
En mode service, forcement, elle ne s'ouvre pas...
Sauf si j'active l'option Autoriser à interagir avec le bureau...
Je n'avais pas plus testé avec cette option activé, mais voila que
j'essai....
Et la, miracle, j'en lance 4 sans problème........

Alors qu'elle peut etre la difference mis a part que la fenetre est
visible ?
Avatar
Patrick Philippot
condo4 wrote:
Alors qu'elle peut etre la difference mis a part que la fenetre est
visible ?



Un indice de plus pointant vers un problème de synchronisation de threads
dans le process ou en cross-process (instances multiples de votre exe).
Revoyez votre code en vérifiant bien que tous les threads sont bien
coordonnés, notamment quand ils accèdent aux ressources fournies par le
Service Manager.

Juste pour être sûr: je suppose que quand vous appelez
RegisterServiceCtrlHandler vous n'utilisez pas le même nom à chaque fois?

Je pense que construire un service avec PowerBuilder doit présenter quelques
difficultés techniques qui ne sont pas aussi simple que ça à surmonter.
Sinon, il n'y aurait pas des produits commerciaux sur le marché pour faire
ce travail. Comme PBservice par exemple: http://www.ecrane.com/ .

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
condo4
> > Alors qu'elle peut etre la difference mis a part que la fenetre est
> visible ?
Un indice de plus pointant vers un problème de synchronisation de threa ds
dans le process ou en cross-process (instances multiples de votre exe).
Revoyez votre code en vérifiant bien que tous les threads sont bien
coordonnés, notamment quand ils accèdent aux ressources fournies par le
Service Manager.


En effet, à un autre moment, (apres 7 ou 8 lancements de l'appli),
parfois le démarage va un peu plus loin (dans ma DLL, j'ai ajouté
plein d'écriture dans un fichier de log pour tracer mon problème).
Pourtant je n'ai que 3 threads... et je ne vois pas les problème qui
pourrait se passer...



Juste pour être sûr: je suppose que quand vous appelez
RegisterServiceCtrlHandler vous n'utilisez pas le même nom à chaque f ois?


Non non, chaque instance poséde son nom propre (qui est par exemple
MON_SERVICE_SRV3 dans l'exemple donné ci-dessus)
En faite lorsque j'appel InitServiceThread depuis PB, je lui donne en
paramêtre cette chaine de caractère qui est contruite à partir d'un
fichier de paramettrage .ini


Je pense que construire un service avec PowerBuilder doit présenter que lques
difficultés techniques qui ne sont pas aussi simple que ça à surmon ter.
Sinon, il n'y aurait pas des produits commerciaux sur le marché pour fa ire
ce travail. Comme PBservice par exemple:http://www.ecrane.com/.


Mise à part le problème actuel, ma méthode à l'air de fonctionner t rès
bien, pourtant mon application est quand même assez complexe
(dépendance avec d'autre service, acces à 2 bases de données, écrit ure
de fichier de log, acces à la base de registre....), et tout
fonctionne.... jusqu'a 3 instances.... ;-)
Je pense qu'il ne me reste plus beaucoup de problème à résoudre...
Je vais regarder du coté de la synchro des thread... si vous avez
d'autre idée...
Avatar
condo4
J'ai toujours pas trouvé le problème...
A première vue, mes threads sont bien synchronisé.
De plus, le problème se pose vraiment au 4ème service !!
En effet, 3 sans pb, 4eme pb; si je coupe le N°3, je peux lancer le 4
sans pb, mais apres, le 3 refuse de démarrer....

Il dois vraiment y avoir un point commun entre mes appli, mais je ne
voit pas du tout le quel...

Avez vous des idées ?
Avatar
Patrick Philippot
condo4 wrote:
Avez vous des idées ?



Désolé, j'étais en déplacement.

A l'expérience, cela m'indique toujours un problème de synchro (un sémaphore
utilisé dans le code? - ce comportement ressemble à un sémaphore initialisé
avec un compte de 3). Mais sans voir le code, je ne vais pas pouvoir aller
plus loin.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
condo4
> A l'expérience, cela m'indique toujours un problème de synchro (un s émaphore
utilisé dans le code? - ce comportement ressemble à un sémaphore in itialisé
avec un compte de 3). Mais sans voir le code, je ne vais pas pouvoir aller
plus loin.



Bonjour,

J'ai mis le code dans mon premier post.
si vous voulez je peux vous envoyer le code de la DLL complet par mail
(ecrivez moi sur c4 AT saint-pal.com).
J'ai vraiment du mal a comprendre le problème.
En faite, l'application NORMAL (non service) fonctionne très bien
quelque soit le nombre lancé.
En service, seulement 3.

Pourtant, je ne vois pas de liens entre chaque appli...
Pour moi, chaque appli lance ces propre thread indépendant.... mais
comme je ne connait pas trop ces mécanisme,je ne m'avance pas trop sur
ce sujet....
En faite il me semble que je n'ai que 3 threads :
ThreadSynchro, ThreadServiceCtlDispatcher et ServiceMain,
Et chaqu'un de c'est thread ne fait que attendre un Event....

Peut etre que mon mécanisme est mauvais, mais je pense que je suis à
mes limites en connaissance a ce niveau.

En plus, ce problème deviens vraiment génant pour mon
dévellopement.... je ne trouve pas vraiment d'autre solution, donc, je
suis vraiment a la recherche ne serai-ce que de piste pour avancer ;-)

Merci d'avance pour toute réponse
Avatar
Patrick Philippot
> Pourtant, je ne vois pas de liens entre chaque appli...
Pour moi, chaque appli lance ces propre thread indépendant.... mais
comme je ne connait pas trop ces mécanisme,je ne m'avance pas trop sur
ce sujet....



Si ce n'est pas l'appli elle-même qui a un problème de synchro, cela peut
être le Service Control Manager qui a des limitations (peut-être à cause du
fait que vous lancez le même exe avec des arguments différents).

Test: lancez 4 instances de votre exe en renommant chaque instance de
manière à ce que le SCM pense qu'il s'agit de services différents. Si le
problème n'apparaît pas, c'est que le SCM a une limitation sur le nombre
d'instances du service (ça me rappelle d'ailleurs quelque chose ça,
brutalement). Si le test fonctionne, c'est que le SCM a un problème avec des
instances multiples d'un même exe. J'essaie de vérifier ça.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
condo4
> Si ce n'est pas l'appli elle-même qui a un problème de synchro, cela peut
être le Service Control Manager qui a des limitations (peut-être à cause du
fait que vous lancez le même exe avec des arguments différents).

Test: lancez 4 instances de votre exe en renommant chaque instance de
manière à ce que le SCM pense qu'il s'agit de services différents. Si le
problème n'apparaît pas, c'est que le SCM a une limitation sur le nom bre
d'instances du service (ça me rappelle d'ailleurs quelque chose ça,
brutalement). Si le test fonctionne, c'est que le SCM a un problème ave c des
instances multiples d'un même exe. J'essaie de vérifier ça.



Merci, je vais faire ce test dans la journée, je vous tiens au courant
du résultat.
Je vais même essayer en créant 4 DLL avec des noms different aussi....
au cas ou...
C'est vrai que je verrai bien un problème dans ce genre aussi.

-----------------------------
Fabien PRORIOL
Avatar
Patrick Philippot
condo4 wrote:
Merci, je vais faire ce test dans la journée, je vous tiens au courant
du résultat.
Je vais même essayer en créant 4 DLL avec des noms different aussi....
au cas ou...
C'est vrai que je verrai bien un problème dans ce genre aussi.



Bonjour,

J'ai lu votre code et je me pose quelques questions. Rappel:

"
If StartServiceCtrlDispatcher succeeds, it connects the calling thread to
the service control manager ****and does not return until all running
services in the process have terminated****. The service control manager
uses this connection to send control and service start requests to the main
thread of the service process. The main thread acts as a dispatcher by
invoking the appropriate
"

Ce qui veut dire que dans votre fonction ThreadServiceCtrlDispatcher,
l'event hThreadDispatcher n'est pas créé tant que le service n'est pas
terminé. Ce qui pose un problème pour la routine DoneServiceThread qui fait
le SetEvent sur cet événement au moment où la décision est prise d'arrêter
le service (appel à DoneServiceThread si j'ai bien compris). Mais comme
l'objet n'est pas encore créé puisque dans ThreadServiceCtrlDispatcher, nous
ne sommes pas encore revenu de l'appel à StartServiceCtrlDispatcher, nous
voilà face au problème de la poule et de l'oeuf. Je peux faire une erreur de
lecture mais il me semble que quelque chose ne va pas.

Au minimum, il me semble que la ligne

hThreadDispatcher = CreateEvent(NULL, TRUE, FALSE, NULL);

devrait se trouver avant l'appel à StartServiceCtrlDispatcher.

Comme je vous le disais précédemment, je ne suis pas sûr que la solution à
votre problème soit aussi simple que cela. Sinon personne ne se serait
donner la peine de sortir un produit commercial pour le résoudre. Mais je
n'y ai pas passé suffisamment de temps pour être formel sur ce point.

Par ailleurs, à quoi sert le Sleep (100) dans la fonction
ReportStatusToSCMgr.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
1 2 3