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
> 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 ma in
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 qu i 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 ThreadServiceCtrlDispatche r, nous
ne sommes pas encore revenu de l'appel à StartServiceCtrlDispatcher, no us
voilà face au problème de la poule et de l'oeuf. Je peux faire une er reur 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.



Oui en effet, j'avais pas remarqué; enfin, ce problème devrait
aparaitre a la fermeture du service non?
Donc, ce n'est pas celui la qui doit poser problème pour le lancement
je pense...


Comme je vous le disais précédemment, je ne suis pas sûr que la sol ution à
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.



Oui, surement, mais ce qui est dommage, j'ai l'impression d'être très
proche du résultat que je recherche...
Il reste ce problème...

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


La franchement je ne saurais vous répondre, je suis partis du code
d'un collegue que je n'ai pas encore vu; il dois etre la la semaine
prochaine, j'espere pouvoir lui demander si il a la moindre idée du
problème.


Sinon, j'ai un peu avancé dans mon analyse :
- Il y avait des Mutex de créé dans mon application qui ne servait
plus en service (il servait à eviter que l'appli était lancé 2 fois
avec les même parametres) je les ai donc enlevé.

- J'ai aussi instrumenté la DLL pour avoir des trace dans un fichier.

A première vu, que je soit en service 1, 2, 3 qui marche ou en 4 qui
marche pas, j'ai les même trace...

En revanche, les traces de mes application PB diffère, j'ai essayer de
comprendre, et j'ai l'impression que la création de certain objets
dynamics ont échoué.

Ma question, n'y aurait-il pas une limitation de l'espace mèmoire
aloué par le Windows Manager Service ?
Je pensais qu'ils tournaient dans des espaces mémoire different, mais
ce pourrait-t-il qu'ils partage un espace commun ?
Limité à une certaine taille ?


Merci de votre aide en tout cas

Fabien PRORIOL
Avatar
Patrick Philippot
condo4 wrote:
Oui en effet, j'avais pas remarqué; enfin, ce problème devrait
aparaitre a la fermeture du service non?



Oui. Mais cela signifie que les problèmes de synchro ne sont pas aussi
"bétonnés" que vous le pensiez :-) .

A première vu, que je soit en service 1, 2, 3 qui marche ou en 4 qui
marche pas, j'ai les même trace...



Vous pourriez utilser WinObj et Process Explorer pour vérifier quels objets
kernels sont créés (events) et voir le nombre de threads activés par
process. Cela donnerait sûrement quelques informations
(http://www.microsoft.com/technet/sysinternals/default.mspx).

Ma question, n'y aurait-il pas une limitation de l'espace mèmoire
aloué par le Windows Manager Service ?



Service Control Manager?

Je pensais qu'ils tournaient dans des espaces mémoire different, mais
ce pourrait-t-il qu'ils partage un espace commun ?
Limité à une certaine taille ?



Non. Les services sont des processus comme les autres.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
condo4
> > Oui en effet, j'avais pas remarqué; enfin, ce problème devrait
> aparaitre a la fermeture du service non?
Oui. Mais cela signifie que les problèmes de synchro ne sont pas aussi
"bétonnés" que vous le pensiez :-) .


Certe c'est vrai :-)

> A première vu, que je soit en service 1, 2, 3 qui marche ou en 4 qui
> marche pas, j'ai les même trace...
Vous pourriez utilser WinObj et Process Explorer pour vérifier quels ob jets
kernels sont créés (events) et voir le nombre de threads activés par
process. Cela donnerait sûrement quelques informations
(http://www.microsoft.com/technet/sysinternals/default.mspx).


Oui je peux voir l'état des service qui fonctionne, en revanche, pour
celui qui meurt, il ne reste qu'environ 1 seconde voir 2 en activité
avant de mourir, ce qui ne me laisse pas trop de temps pour voir ce
qui se passe....


> Ma question, n'y aurait-il pas une limitation de l'espace mèmoire
> aloué par le Windows Manager Service ?
Service Control Manager?


Heu oui désolé :-)

> Je pensais qu'ils tournaient dans des espaces mémoire different, mais
> ce pourrait-t-il qu'ils partage un espace commun ?
> Limité à une certaine taille ?
Non. Les services sont des processus comme les autres.


Ok


Bon, je suis en train de voir avec ma boite s'il ne veulent pas
essayer de faire un test avec PBservice, mais c'est pas gagné....

D'un autre coté, je ne sais vraiment trop par ou continuer
chercher....
Avatar
Patrick Philippot
condo4 wrote:

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.



Oui en effet, j'avais pas remarqué; enfin, ce problème devrait
aparaitre a la fermeture du service non?
Donc, ce n'est pas celui la qui doit poser problème pour le lancement
je pense...



En fait, tout le code après le bloc "if
(!StartServiceCtrlDispatcher(DispatchTable)) " dans
ThreadServiceCtrlDispatcher ne sert à rien. On ne l'exécutera (sortie de
StartServiceCtrlDispatcher) que lorsque le service sera terminé et donc il
est inutile d'attendre sur un event qui fait redondance. Vous pouvez déjà
simplifiez votre code en retirant tout ça et en supprimant les events et les
tests/wait correspondants.

A première vu, que je soit en service 1, 2, 3 qui marche ou en 4 qui
marche pas, j'ai les même trace...



Il va falloir traiter ça dans le débogueur.

1. Lancez les 3 premières instances du service de manière autonome.
2. Mettez des breakpoints aux points stratégiques dans le code de la DLL.
3. Lancez la DLL en mode debug depuis votre environnement de développement
(VC++ ?) en précisant le nom de l'exécutable à lancer (la 4ème instance qui
va charger la DLL) et l'argument ligne de commande. Quand l'exécutable
appellera une fonction exportée par votre DLL, le débogueur prendra la main
si un breakpoint est posé.
4. Vérifiez le chemin suivi et le point où cela bloque.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
condo4 wrote:

Le démarage du 4 service plante sur le sleep(100) de
ReportStatusToSCMgr.



Avez-vous regardé dans l'Observateur d'événements si le SCM rapporte une
erreur lors du démarage de la quatrième instance. Que dit GetLastError?

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
Autre point qui me semble important:

Doc de StartServiceCtrlDispatcher

"
The lpServiceTable parameter contains an entry for each service that can run
in the calling process. Each entry specifies the ServiceMain function for
that service. For SERVICE_WIN32_SHARE_PROCESS services, each entry must
contain the name of a service. This name is the service name that was
specified by the CreateService function when the service was installed.
****For SERVICE_WIN32_OWN_PROCESS services, the service name in the table
entry is ignored****.
"

Or c'est grâce à cette info que vous différenciez les services entre eux. Il
y a donc probablement un problème dans la base de données du SCM. La
solution est peut-être d'utiliser un seul processus de type
SERVICE_WIN32_SHARE_PROCESS, ce qui peut changer sensiblement la structure
du code.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
Patrick Philippot
condo4 wrote:
Merci, je vais faire ce test dans la journée, je vous tiens au courant
du résultat.



Bonjour,

Le problème est-il résolu ou bien est-ce que je continue de suivre ce fil?

Cordialement.

--
Patrick Philippot - Microsoft MVP
MainSoft Consulting Services
www.mainsoft.fr
Avatar
condo4
Bonjour, désolé, j'ai dus partire en déplacement, donc, le problème
n'est toujours pas résolut.
Aujourd'hui, je vais essayer d'installer mes services sur un Windows
2000 Serveur, pour voir si le problème est le même.

Je suis en train de reprendre vos suggestion (entre autre, je vais
regarder le journal des erreur...)

donc, je vous tiens au courant de mes avancés....
Avatar
condo4
On 25 mai, 10:07, condo4 wrote:
Bonjour, désolé, j'ai dus partire en déplacement, donc, le problè me
n'est toujours pas résolut.
Aujourd'hui, je vais essayer d'installer mes services sur un Windows
2000 Serveur, pour voir si le problème est le même.

Je suis en train de reprendre vos suggestion (entre autre, je vais
regarder le journal des erreur...)

donc, je vous tiens au courant de mes avancés....



Voila, j'ai donc fait un test sur le serveur.
Ce qui m'a permi de découvrir un problème ailleur.
J'ai l'impression que maintenant la DLL est stabilisé, et que le
problème ne viens pas/plus d'elle (j'ai apporté les corrections que
vous me conseillez).

En revanche, le problème semble etre maintenant dut à la connection PB
avec la Base de Donnée...
Donc, je vais creuser dans ce sens...

En faite, ce que j'aurais aimé savoir; c'est exactement, qu'est ce que
ça change entre une appli normal et une service, a par le fait qu'elle
dois repondre au point d'entré que ma DLL se charge...

Je sais qu'il utilise la session SYSTEM, mais au niveau droit, au
niveau reseau... est-ce qu'il y a des differences majeur que j'ignore?
Ou pourrais-je trouver une doc qui detail justement ces
differences....

Merci beaucoup en tout cas de votre aide, elle m'a permit de mieu
comprendre le fonctionnement et surtout de corriger certain détail qui
me serait un jour ou l'autre tombé dessus.
Aujourd'hui et le début de semaine prochaine, je vais travailler
exclusiveement sur ce projet, j'espère arriver à comprendre un peu
plus les problème restant....

@+Fab
Avatar
Patrick Philippot
Bonjour,

En faite, ce que j'aurais aimé savoir; c'est exactement, qu'est ce que
ça change entre une appli normal et une service, a par le fait qu'elle
dois repondre au point d'entré que ma DLL se charge...



Sur le fond, quasiment rien. Le service doit effectivement fournir les
points d'entrée requis et réagir aux sollicitations du système mais sinon,
c'est un processus "normal". Par contre, comme un service n'est pas attaché
à une session utilisateur particulière, certaines APIs ne fonctionnent pas
ou pas de la même manière (en particulier celles qui prennent un numéro de
session en argument ou qui ont besoin d'une info liée à la session
courante). Par exemple, récupérer le nom de l'utilisateur courant pose des
problèmes particuliers, surtout si on utilise le Fast User Switching : le
service tourne en session 0 et les sessions utilisateurs peuvent être 0,
1,...,n. Sous Vista, c'est même pire puisque la session 0 est réservée aux
services.

Tout ça peut poser 2 types de problèmes:

1. La communication du processus service avec les processus utilisateurs. Si
vous utilisez des objets système comme des sémaphores, des mutexes, etc, il
faut prendre soin de les créer dans le namespace Global (voir la doc).

2. L'interactivité avec l'utilisateur est limitée, même si on autorise le
service à être interactif. Il vaut mieux avoir une appli utilisateur qui
communique avec le service plutôt que de permettre au service de communiquer
avec l'utilisateur directement (sauf dans les cas simples).

En fait, tout cela est assez logique si on comprend que le service tourne
tout le temps et n'est pas lié à une session utilisateur.

La meilleure source d'info sur les services est probablement le MSDN.

Voir également:
http://support.microsoft.com/kb/q156138/


Je sais qu'il utilise la session SYSTEM, mais au niveau droit, au
niveau reseau... est-ce qu'il y a des differences majeur que j'ignore?




Le contexte de sécurité dans lequel s'exécute le service est celui défini
par l'utilisateur spécifié dans la boîte de dialogue Propriétés du service.
Il n'y a rien de spécifique en dehors de cela.

Bon courage.


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