OVH Cloud OVH Cloud

VB/DoEvents C++ ?

33 réponses
Avatar
PurL
Bonjour,

Dans un programme C++ en mode console (fonction main) je dois lancer un
timer (SetTimer) et rien d'autre. Mais il ne faut pas que je sorte du main.
Venant du monde VB, je ferais en VB :

bOK = false
hTimer = SetTimer(0, 0, delay, TimerProc)
While(not bOK) if GetInputState Then DoEvents 'pour pas bloquer
KillTimer hTimer


Sachant que c'est la procédure TimerProc qui mettera à true bOK et donc
mettra fin au programme.
En C++ le programme est quasiment le meme, sauf que je ne sais pas
reproduire le comportement de la ligne :
"if GetInputState then DoEvents"

Comment faire ?

Merci,

PurL

10 réponses

1 2 3 4
Avatar
Manuel Leclerc
Christian ASTOR a écrit :

Manuel Leclerc a écrit:

> A part ça, SI "un programme console a une message queue",

"
SetTimer() was not designed to be used with a console application
because it requires a message loop
"



Je ne parle pas d'une message loop, je parle d'une message queue.

Il est clair que quand tu fais une application console, tu es
parti pour avoir une approche procédurale classique dans laquelle
ta fonction main appelle une fonction qui elle même appelle une
fonction, etc.. Peut être as-tu aussi de temps en temps une
"boucle" pour lire le clavier.

Si tu as une telle application, et que tu décides d'utiliser SetTimer
sans créer un nouveau thread, ça ne va pas être simple. C'est peut
être tout simplement pour ça que Microsoft dit "because it requires
a message loop". Comprendre : "et où donc allez vous coder votre
message loop ???"

Maintenant, si ton application console, comme le disait l'OP, ne
fait QUE CA (une fonction doit être appelé toutes les x secondes),
tu n'as aucun problème pour savoir où coder ta message loop. La question
est : le thread 1 d'une application console a-t-il une _message queue_ ?

Réponse : OUI.

Finalement, je crois que j'ai compris le "should not" et le
"not designed". Il parle du fait que pour utiliser un Timer tu
te retrouves obligé _d'utiliser_ une message loop. C'est tout.

Une appli console n'a pas de *message* loop (I/O events)
Il suffit d'en rajouter 1 avec GetMessage() et on voit que msg.message
est tjrs = 0.



???

Rien compris.

La queue de message n'est effectivement créée dans le thread qu'au
moment où le thread réalise son _premier_ appel à User ou à Gdi.
J'imagine que c'est une optimisation. Extrait de la doc MSDN de
PostThreadMessage : "The system creates a thread's message queue
when the thread makes its first call to one of the User or GDI
functions."

Donc application console ou pas, ce n'est pas la question. Ton
thread démarre, tu fais appel à SetTimer (qui est dans User) :
boum, tu as une message queue. Et tu peux poster un message depuis
un autre process, je viens de faire le test avec PostThreadMessage
et ça marche parfaitement.

Mais bien sûr qu'on peut créer ensuite des fenêtres.



Fenêtres qui ne marcheraient pas sans message queue...
Avatar
Ambassadeur Kosh
> Ambassadeur Kosh wrote:
ça me dérange ta boucle while(!bOk).
c'est de l'attente active.
c'est pourri et inutile.



Et le Sleep alors? Ce n 'est pas de l'attente active!



attend, Purl peut passer à côté de quelque chose d'important la.
évidement que ça fonctionne bien le Sleep, dans ce cas particulier.

mais c'est une rustine. on a des mécanismes systemes adaptés comme les
threads, ou les messages dans ce genre de cas. autant s'en servir plutôt que
de reinventer un sous systeme qui ne supportera pas qu'on le bricole. un
Sleep, ça ne remplace pas un rendez vous.

pour causer riche, la prog systeme côté user, c'est plus déclarer un besoin
/ spécifier des contraintes que d'imposer un acte. sinon c'est le bordel.

et Sleep, c'est imposer un "acte".

je me la joue peut être un peu "prof je sais", mais, peut être que ça va lui
apporter de l'aide aussi.

enfin bon, moi ce que j'en dis...
Avatar
Manuel Leclerc
Ambassadeur Kosh a écrit :

pour causer riche, la prog systeme côté user, c'est
plus déclarer un besoin / spécifier des contraintes
que d'imposer un acte. sinon c'est le bordel.

et Sleep, c'est imposer un "acte".



Tu exagère un peu, je trouve :-)

Les besoins/contraintes, tu peux aussi les voir tout
simplement comme ça :

FAIRE_BIDULE
ATTENDRE_UN_PEU_GENRE_6_SECONDES_ENVIRON
RECOMMENCER

Et bon.... Pour ça, Sleep, c'est pas si mal :-)
Avatar
Ambassadeur Kosh
>> pour causer riche, la prog systeme côté user, c'est
plus déclarer un besoin / spécifier des contraintes
que d'imposer un acte. sinon c'est le bordel.

et Sleep, c'est imposer un "acte".



Tu exagère un peu, je trouve :-)

Les besoins/contraintes, tu peux aussi les voir tout
simplement comme ça :

FAIRE_BIDULE
ATTENDRE_UN_PEU_GENRE_6_SECONDES_ENVIRON
RECOMMENCER



for(;;)
if(test()) action() ;
else Sleep() ;

c'est un acte.

void main()
{
planifier(MonEvent,18:37:00) ;
}

void callback MonEvent(Sender)
{
action() ;
}

c'est l'expression d'un besoin.

maintenant, pour l'exageration, bof.

j'en ai 15 au boulot autours de moi qui font comme ça. et voila, je te met
un Sleep par ci au milieu de ma super boucle parceque ça pompe trop de temps
et l'autre thread en a pas assez pour sa boucle et loupe des infos de la
carte controleur, et le lendemain, ah oui, mais faut un Sleep dans le
deuxieme paske finalement, la, on déclenche le traitement trop tard. et
quand le client telephone pour geuler, on lui dit "ah ben oui, mais c'est
windows qui n'est pas temps reel, vous savez, on y peut rien, blablablabla".
c'est beau hein ? tu pompes du temps pour verifier qqchose qui ne s'est pas
produit. tu t'endors pour l'eternité (une seconde, c'est l'infini à cette
echelle), et un epsilon apres t'être endormi, l'evenement en question se
produit. alors apres on pipote, "à ben oui, mais ça n'arrive pas souvent,
seulement de temps en temps blablabla.", on fait du téléphone et de la
psychologie avec le client. c'est ça l'attente active. de toute façon, les
semaphores, ça peut pas être mieux, la preuve, ça provoque des interblocages
:)

maintenant, moi au boulot, je fais de l'attente active tiens. je regarde sur
le site de ma banque en permanence pour voir si mon cheque de paye a été
viré sur mon compte. non, je fais pas que ça, je dors une heure entre chaque
consultation. mais bon une heure, c'est une heure. pas moins. et quand le
telephone sonne, je repond quand je me reveille. et la bizarrement,
l'information qu'il y a avait a traiter à disparu.


sur le coup la, c'est pas dramatique effectivement.

mais bon, la solution à Møgluglu c'est le début d'une solution plus generale
qui répond efficacement à cette vaste catégorie de problemes. alors que le
polling, ben ca daube.

voila voila...
Avatar
Manuel Leclerc
Ambassadeur Kosh a écrit :

> > pour causer riche, la prog systeme côté user,
> > c'est plus déclarer un besoin / spécifier des
> > contraintes que d'imposer un acte. sinon c'est
> > le bordel.
> >
> > et Sleep, c'est imposer un "acte".
>
> Tu exagère un peu, je trouve :-)
>
> Les besoins/contraintes, tu peux aussi les voir tout
> simplement comme ça :
>
> FAIRE_BIDULE
> ATTENDRE_UN_PEU_GENRE_6_SECONDES_ENVIRON
> RECOMMENCER

for(;;)
if(test()) action() ;
else Sleep() ;

c'est un acte.

void main()
{
planifier(MonEvent,18:37:00) ;
}

void callback MonEvent(Sender)
{
action() ;
}

c'est l'expression d'un besoin.

maintenant, pour l'exageration, bof.



Quand je disais que je trouvais que tu exagérais, je
voulais juste dire qu'il ne faut pas non plus se prendre
la tête pour faire des choses simples. Tu peux toujours
prendre n'importe quel exemple et sur-spécifier, sur-généraliser.

Là, on parlait de lancer une certaine fonction environ toute
les 6 secondes (et non pas à 18:37), et le programme ne fait
rien d'autre. Aller chercher des Events, c'est juste un marteau
pilon pour écraser une mouche. Mais ça marche sûrement, hein.

A part ça, la fonction "planifier", tu l'implémentes comment ?

j'en ai 15 au boulot autours de moi qui font comme ça. et voila,
je te met un Sleep par ci au milieu [...]



Je suis d'accord avec ce genre de chose. Un Sleep dans un programme
parce que "si on l'enlève, ça marche plus" est simplement le signe
que le programmeur n'a pas bien compris quelque chose. Et il arrive
souvent que tel ou tel changement dans l'environnement fait que
"ça marche plus, même avec le Sleep" :-)

M'enfin bon, les bugs, c'est pas nouveau. Faut pas non plus tout
mettre sur le dos des actes vs les besoins.

sur le coup la, c'est pas dramatique effectivement.



Voilà.

mais bon, la solution à Møgluglu c'est le début d'une solution
plus generale qui répond efficacement à cette vaste catégorie
de problemes. alors que le polling, ben ca daube.

voila voila...



La solution à Møgluglu, je ne l'ai pas comprise. Si toi tu as
compris, tu peux m'expliquer ?

Aucune des solutions que j'ai proposées ne comportait de polling.
Avatar
Ambassadeur Kosh
> Quand je disais que je trouvais que tu exagérais, je
voulais juste dire qu'il ne faut pas non plus se prendre
la tête pour faire des choses simples. Tu peux toujours
prendre n'importe quel exemple et sur-spécifier, sur-généraliser.



manie d'universitaire ça.
Admis dans ce cas precis, quoique.

ceci dit, un programme, on lui en rajoute à faire toutes les 10 minutes.
donc finalement, ça vaux peut être le coup de reflechir bien du premier
coup.

c'est comme les variables globales ça. c'est tellement complexe de passer
un parametre et de ne pas faire de side-effect. pfff....

Là, on parlait de lancer une certaine fonction environ toute
les 6 secondes (et non pas à 18:37)





et le programme ne fait rien d'autre. Aller chercher des Events, c'est


juste un marteau
pilon pour écraser une mouche.
Mais ça marche sûrement, hein.



ben non, c'est cucul la praline et ça n'induit pas d'effet pervers. faut
arreter les gars la.




A part ça, la fonction "planifier", tu l'implémentes comment ?



tu l'implementes pas, elle est déja implantée. ça s'appelle le systeme.
genre un Timer.




j'en ai 15 au boulot autours de moi qui font comme ça. et voila,
je te met un Sleep par ci au milieu [...]



Je suis d'accord avec ce genre de chose. Un Sleep dans un programme
parce que "si on l'enlève, ça marche plus" est simplement le signe
que le programmeur n'a pas bien compris quelque chose. Et il arrive
souvent que tel ou tel changement dans l'environnement fait que
"ça marche plus, même avec le Sleep" :-)



et c'est bien domage parceque justement le systeme offre les moyens de
modeliser des rendez vous, de la synchronisation, des taches paralleles et
tout et tout...

M'enfin bon, les bugs, c'est pas nouveau. Faut pas non plus tout
mettre sur le dos des actes vs les besoins.



c'est pas un bug ça. c'est être en dessous de tout. c'est garantir d'emblée
que l'ensemble du systeme sera completement bancal.
"je fais de l'attente active au boulot", c'est exactement ce qui se passe au
niveau systeme.

sur le coup la, c'est pas dramatique effectivement.


Voilà.



La solution à Møgluglu, je ne l'ai pas comprise. Si toi tu as
compris, tu peux m'expliquer ?



"Le mieux est d'utiliser un event. Tu appelles CreateEvent avec
bInitialState suf FALSE, puis tu l'attends avec WaitForSingleObject.
Et la procédure TimerProc appelle SetEvent au lieu de mettre bOK à true."

c'est un event. on aurait pu utiliser un callback. ou chopper le WM_TIMER.
mais bon circonstance oblige.
on aurait pu utiliser une appli fenetre et connecter une console, si ça
avait un interet. sans console, sinon, ça aurait été encore plus simple.
on aurait pu utiliser le planificateur de taches, en ajoutant des entrées
qui vont bien.
mais c'est déja avoir admis d'essence qu'il fallait scruter pour voir quand
le fichier était "disponible".
un FileMonitor aurait certainement été une meilleure solution. t'es prevenu
sur les "evenements" qui t'interessent pour un fichier precis.
les moyens ne manquent pas.

c'est comme dans un entretient, si tu induits la réponse, tu perds
enormement.



Aucune des solutions que j'ai proposées ne comportait de polling.


c'est pas un peu fini la paranoia
Avatar
Manuel Leclerc
Ambassadeur Kosh a écrit :

ceci dit, un programme, on lui en rajoute à faire
toutes les 10 minutes. donc finalement, ça vaux peut
être le coup de reflechir bien du premier coup.



C'est vrai.

Mais je constate par contre que j'ai trop souvent voulu
"réfléchir" et perdu en fait du temps à mettre
en place quelque chose de trop générique pour l'usage
que j'en ai eu par la suite. Je crois d'ailleurs qu'il
existe en ce moment un engouement pour certaines méthodes
de programmation qui vise à obtenir vite une première version,
quitte à changer les choses QUAND il s'avère que c'est nécessaire.
Je vois bien d'où viennent ces méthodes : c'est une sorte de
retour de bâton, face aux usines à gaz sur-modélisées

c'est comme les variables globales ça. c'est tellement complexe
de passer un parametre et de ne pas faire de side-effect. pfff....



:-)

Je viens de commencer un projet personnel où j'ai décidé que tout
un tas de variable seraient globales (sauf si je sais de manière
évidente, à l'avance, que j'aurais besoin de plusieurs instances
de la variable). Et bien, figures-toi que c'est très reposant :-)

> et le programme ne fait rien d'autre. Aller chercher des
> Events, c'est juste un marteau pilon pour écraser une mouche.
> Mais ça marche sûrement, hein.

ben non, c'est cucul la praline et ça n'induit pas d'effet pervers.
faut arreter les gars la.

> A part ça, la fonction "planifier", tu l'implémentes comment ?

tu l'implementes pas, elle est déja implantée. ça s'appelle le systeme.
genre un Timer.



Tu pourrais me montrer le code cucul la praline de ta fonction
"planifier" ?

> La solution à Møgluglu, je ne l'ai pas comprise. Si toi tu as
> compris, tu peux m'expliquer ?

"Le mieux est d'utiliser un event. Tu appelles CreateEvent avec
bInitialState suf FALSE, puis tu l'attends avec WaitForSingleObject.
Et la procédure TimerProc appelle SetEvent au lieu de mettre bOK à true."

c'est un event.



Merci, je sais lire, mais je n'ai _pas_ compris, et je crois que toi
non plus en fait.

On dirait qu'il y a un bout de code qui est en train d'attendre dans
WaitForSingleObject et un autre bout de code qui est en train de faire
SetEvent dans une TimerProc. C'est bizarre, non ?
Avatar
Møgluglu
Manuel Leclerc a écrit:
On dirait qu'il y a un bout de code qui est en train d'attendre dans
WaitForSingleObject et un autre bout de code qui est en train de faire
SetEvent dans une TimerProc. C'est bizarre, non ?



Exact, désolé, ma solution ne marche pas. En fait je n'avais pas pensé
que la TimerProc était appelée dans le même thread, j'avais raisonné
comme avec SetConsoleCtrlHandler...

--
Møgluglu
Avatar
Frédéric DIDIER
> Mais je constate par contre que j'ai trop souvent voulu
"réfléchir" et perdu en fait du temps à mettre
en place quelque chose de trop générique pour l'usage
que j'en ai eu par la suite. Je crois d'ailleurs qu'il
existe en ce moment un engouement pour certaines méthodes
de programmation qui vise à obtenir vite une première version,
quitte à changer les choses QUAND il s'avère que c'est nécessaire.
Je vois bien d'où viennent ces méthodes : c'est une sorte de
retour de bâton, face aux usines à gaz sur-modélisées



c'est vrai aussi ça. c'est vrai que c'est plus agréable de reflechir sur
un prototype que sur une idée completement subjective. ça donne
des idées, ça aide, mais bon, ça dépend aussi beaucoup du but
recherché.

moi mon but, c'est de resoudre des problemes précis mais de
maniere à ce que ça ai un interet pour la majorité des clients.
mais j'ai un Boss qui pique des crises de cretinisme et qui ne
te file une prime que si la fenetre de saisie des données et tip
top.

ouaih, c'est vrai en fait, faut gérer entre interet collectif/global
et interet individuel/local.

Je viens de commencer un projet personnel où j'ai décidé que tout
un tas de variable seraient globales (sauf si je sais de manière
évidente, à l'avance, que j'aurais besoin de plusieurs instances
de la variable). Et bien, figures-toi que c'est très reposant :-)



tu vas voir le reposant quand tu vas chercher d'ou proviennent
les erreurs. les 20 minutes que t'as gagné comment qu'elles
vont se transformer en 200 heures de perdues :o)

Tu pourrais me montrer le code cucul la praline de ta fonction
"planifier" ?



http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/
_mfc_CWnd.3a3a.SetTimer.asp

mais bon, faut interpreter un peu. la ils te donnent la technique ou tu file
NULL comme callback. donc il faut poulper le message WM_TIMER dans la pompe.
y'a du class wizard en pagaille, et c'est dans une CWnd. mais ça change pas
grand chose en fait.

sinon, tu t'ecris ton traitement.

void CALLBACK EXPORT MyTimerTraitement
(
HWND hWnd, // handle of CWnd that called SetTimer
UINT nMsg, // WM_TIMER
UINT nIDEvent // timer identification
DWORD dwTime // system time
)
{
// ce qu'il faut faire
}

et tu fais un nTimer = SetTimer(1, 2000, &MyTimerTraitement);

si c'est pas cucul la praline ça, je sais pas ce que c'est.

Merci, je sais lire, mais je n'ai _pas_ compris, et je crois que toi
non plus en fait.



comme mogluglu l'expliquait, tu rentres dans le main et tu fais ton
SetTimer, en te preparant un Event.
et la tu te mets en attente avec le WaitForSingleObject. le thread s'endors
alors, la machine fait plein de truc, et un jour l'heure arrive, l'evenement
se produit. le processeur prend ses petites jambes, arrete ses conneries,
range l'appart vite fait, et va ouvrir la porte : il continue l'execution du
main en passant à la ligne suivante. et voila, il a dormi 6 secondes sans
poulper un pet de temps.

bon, maintenant, cette fonction elle a un peu trop de... potentiel. elle
attend quelque chose, certes, mais entre attendre un event et considerer
qu'attendre un semaphore, c'est faire un lock dessus, bon, je trouve ça pas
intuitif pour le commun des mortels.

On dirait qu'il y a un bout de code qui est en train d'attendre dans
WaitForSingleObject et un autre bout de code qui est en train de faire
SetEvent dans une TimerProc. C'est bizarre, non ?



SetTimer(gnagnagna Event) ;

while(!finished)
{
WaitForSingleObject(Event) ;
finished = Traitement() ;
}

remplace WaitForSingleObject par Sleep si ça te fait plaisir :o)


enfin bon. philosopher sur le timer, c'est joli, mais la, c'est pareil.
quantitativement, c'est pas mal, mais qualitativement, c'est toujours de
l'attente active.
je pense qu'un FileMonitor aurait été plus adéquat.

voila voila
Avatar
Frédéric DIDIER
> > On dirait qu'il y a un bout de code qui est en train d'attendre dans
> WaitForSingleObject et un autre bout de code qui est en train de faire
> SetEvent dans une TimerProc. C'est bizarre, non ?



maintenant que je relis, j'ai l'impression qu'il a pensé "deux threads".
avec un seul, c'est sur que la synchro, elle va pas se faire bien.
1 2 3 4