OVH Cloud OVH Cloud

Message d'attente

19 réponses
Avatar
Stephane TUET
Bonjour

J'essaie depuis un bon moment maintenant et sans succés de réaliser un
message d'attente pendant un traitement (en fait, un appel à un Web Service
en mode synchrone). Le but est que le message apparaisse lorsque le temps
d'attente devient trop long... Comme j'ai également une image Gif animée sur
cette form, je suppose qu'il faut que je crée un nouveau Thread pour que
l'animation marche correctement et pour gérer l'ouverture retardée.

J'ai bien commencé à écrire quelques lignes de code, mais ça ne fonctionne
par comme je veux (en général je n'ai rien du tout qui s'affiche :-) )

Dans un module j'ai les déclarations suivantes :
Private frm_wait As New frm_wait 'la fenêtre d'attente

Private WithEvents TimerWait As New Timer

Private Thread As New Threading.Thread(AddressOf DebutThreadAttente)


Dans ma fenêtre où il y a l'appel au service web j'ai qq chose du style :

DebutAttente()
try
'Appel du service web
catch
'gestion d'erreur
end try
FinAttente()


Et mes procédures sont les suivantes :

Public Sub DebutAttente()

TimerWait.Interval = K_TIMER_WAIT 'la variable définie en ms pour le
temps d'attente avant affichage de la fenêtre

TimerWait.Start()

End Sub



Private Sub TimerWait_Tick(ByVal sender As Object, ByVal e As
System.EventArgs) Handles TimerWait.Tick

TimerWait.Stop()

Thread.Start()

End Sub



Private Sub DebutThreadAttente()

frm_wait.Show()

End Sub



Public Sub FinAttente()

TimerWait.Stop()

frm_wait.Hide()

End Sub



Avec cela je voulais réaliser uniquement un Show/Hide de la fenêtre
d'attente avant/après chacun des traitements mais ça ne marche pas...
J'avoue que je suis bien perdu avec ce thread. Si quelqu'un pouvait
m'indiquer une piste pour réaliser cela...

Merci d'avance

Steph

9 réponses

1 2
Avatar
Pyroa
Je ne comprends pas pourquoi la fenetre sera detruite lors de la fin du
thread !
On ne fait qu'executer une méthode sur une instance globale ..
L'instance de la form n'est pas déclaré dans le thread donc ne doit pas etre
détruite !

"Elp" a écrit dans le message de news:
171lkqy2fi1uk$.1vuf1v9c5943b$
Salut,

On Thu, 6 Jan 2005 16:09:01 +0100, AlexC wrote:

J'ai trouvé, mais je ne comprends pas pourquoi ...

Il suffit d'enlever le Thread "DebutThreadAttente"
et de remplacer dans le gestionnaire d'évènemement du timer le
Thread.start
par
frm_wait.show et ca marche ...



Dans ta methode précédente, tu lancait un thread qui se contentait
d'afficher ta fenetre d'attente de maniere non modale (Show ne bloque
pas).
Donc ton thread affichait ta fenetre puis terminait (vu qu'il n'avait plus
rien a faire) entrainant du coup la destruction de ta fenetre. La solution
aurait été d'appeler ShowDialog (bloquante tant que la fenetre n'a pas été
fermée) au lieu de Show. Mais de toute facon, cette solution d'utilier un
autre thread pour afficher ta fenetre était tres crade et t'aurai apporté
tout un tas de bugs bizarres.

Dans ta nouvelle methode, j'imagine que utilise le timer de l'espace de
nom
Windows.Form. Lorsque que ce timer "tick", ta fonction TimerWait_Tick est
executée dans le thread principal (d'ou le timer a été lancé) et non pas
dans un nouveau thread (c'est la particularité du timer Windows.From
comparé aux autres timers disponibles en .NET). Ce thread principal a 2
particularités:
- il ne termine que lorsque tu fermes ton appli
- il possede un boucle de message (c'est cette boucle qui permet aux
fenetres de rester affichées et de recevoir les evenements - ou messages -
clavier, souris...)

Donc ta fonction TimerWait_Tick affiche ta fenetre d'attente dans le
thread
principal puis se termine. Seulement le thread dans lequel est affichée ta
fenetre est toujours la lui, ta fenetre n'est donc pas détruite et reste
affichée.

Cela dit, cela m'étonne quand meme que ta fenetre soit affichée. Car
lorsque TimerWait_Tick s'exécute, ton thread principal devrait en théorie
etre bloqué par l'appel synchrone au service Web et donc incapable de
faire
quoique ce soit, y compris d'afficher ta fenetre (qui ne serait du coup
affichée qu'a la fin de l'appel au service web). Donc je pense que lors de
tes tests, le timer a tické avant que l'appel au web service n'ai
réellement commencé ou alors lorsqu'il était déja terminé. Ce qui n'est
pas
ce que tu cherche a faire je pense.

Dans tout les cas, ton approche ne me semble pas etre la bonne. La regle
d'or pour les applis Windows (c'est dans la doc): tout ce qui a trait a
l'UI doit se faire dans un et un seul thread (le thread principal). Sinon,
bugs bizarres quasiment certains (d'ou mon commentaire sur ta premiere
solution). De plus, toute opération longue et bloquante doit etre évitée
dans le thread principal (celui de l'UI) afin d'éviter que ton appli ne
soit gelée. Donc la solution est: lance l'appel a ton web service de
maniere asychrone en passant l'adresse d'une fonction callback a appeler
lorsque l'appel sera terminé; puis démarre ton Timer; puis dans ta
fonction
callback ferme la fenetre d'attente si elle a été affichée.


Avatar
Stephane TUET
Bonjour,

Ma remarque ne portait pas sur les problèmes transactionnels au niveau SGBD,
mais simplement sur le fait que l'utilisateur ait accès à une fenêtre qu'il
est en train d'enregistrer. Je pense que pour cela la solution de Elp est
suffisante, c'est à dire afficher une fenêtre modale d'attente qui
bloquerait l'accès à la fenêtre en cours d'enregistrement... Par contre, mon
application est en fenêtre MDI, donc la fenêtre d'attente, si je l'affiche
en modale, ça sera au niveau de la fenêtre principale, et ça revient à
bloquer mon application pendant le traitement ! Et donc finalement je me
retrouve avec un mode de fonctionnement équivalent au mode synchrone...
(c'est pas que je revendique le mode synchrone, mais j'ai du mal à
comprendre... :-) )

Steph


"Christophe Lauer [MS]" a écrit dans le
message de news:
Bonjour,

Stephane TUET wrote:

Ok, c'est vrai que c'est beaucoup plus élégant comme méthode... Par
contre, c'était justement parce que l'appli se retrouve "gelée" que
je voulais afficher ce message d'attente...



En fait , le c'est le thread principal de l'application qui a gére
l'affichage et qui doit attendre le retour du service web et donc "gèle"
l'application.

En mode asynchrone, je
suppose que l'utilisateur pourrait faire tout et n'importe quoi
pendant l'appel au service web, ce qui peut parfois être embétant...
Imaginons par exemple que l'utilisateur soit en train de créer un
fournisseur, l'enregistrement de celui-ci se fait via un service web,
et en mode asynchrone, l'utilisateur se retrouve à pouvoir faire des
modifs alors que l'enregistrement est en cours, c'est pas top je
trouve



Les bases de données gère tout ça très bien. Il faut simplement gérer
correctement les exceptions qu'elles peuvent remonter dans ces cas.

Si on reprend votre exemple avec non pas un mais deux utilisateurs, votre
solution ne protège en rien le fait qu'un utilisateur peut agir sur le
même enregistrement qu'un autre utilisateur. L'intérêt est donc assez
limité.

(mais y'a sûrement un autre moyen d'éviter cela...). En fait
ma question reviendrait plutôt à quelque chose du genre : tout les
appels à des services web ne peuvent pas forcément se faire en mode
asynchrone, et donc comment faire dans ce cas là ? (si je dis
n'importe quoi je m'en excuse d'avance :-) )




Hmmm, pas certain de bien comprendre la question. Pour les besoins
spécifiques tels que les appels dans des contextes transactionnels, il fut
en passer par les extensions proposées dans le WSE Toolkit.

HTH,

--
Christophe Lauer - Relations Techniques Editeurs de Logiciels
Division Développeurs et Plateforme d'Entreprise - Microsoft France
http://www.microsoft.com/france/msdn/
This posting is provided "AS IS" with no warranties, and confers no
rights.




Avatar
Elp
On Thu, 6 Jan 2005 19:46:20 +0100, Pyroa wrote:

Je ne comprends pas pourquoi la fenetre sera detruite lors de la fin du
thread !
On ne fait qu'executer une méthode sur une instance globale ..
L'instance de la form n'est pas déclaré dans le thread donc ne doit pas etre
détruite !



Oui si la fenetre a été créé dans le thread principal et simplement
affichée dans le second thread. Ce qui semble etre le cas ici. Au temps
pour moi. La fenetre n'est donc pas détruite.

Cela dit, ca ne m'étonne toujours pas qu'elle disparaise immédiatement.
Pour 2 raisons:
- cette fenetre est créé dans le thread principal mais l'appel a Show est
fait dans un autre thread. Ce n'est pas supporté par le .NET Framework et
peux entrainer certains bugs imprévisibles (meme si la plupart du temps ca
a l'air de fonctionner). Voir la doc de Control.BeginInvoke par exemple:

"Note There are four methods on a control that are safe to call from any
thread: Invoke, BeginInvoke, EndInvoke, and CreateGraphics. For all other
method calls, you should use one of the invoke methods to marshal the call
to the control's thread."

Il y a régulierement des discussions a ce sujet dans les forums. La
solution pour ce probleme est de ne pas appeler Show directement mais de
marshaller l'appel via la méthode Invoke. Mais dans le cas de Stephane,
cela ne fonctionnera pas car le thread principal sera déja bloqué par
l'appel au service Web lorsqu'il recevra cette commande et ne pourra donc
pas afficher la fenetre avant la fin de l'appel au service Web.

- Deuxieme raison et c'est je pense que c'est la cause de la disparition de
la fenetre: lorsque l'application démarre, une boucle de messages (Message
Loop) est démarrée sur le thread principal. C'est cette boucle de message
qui permet aux fenetre de recevoir les évenements Windows (evenements
souris, clavier, demande de redessinage de la fenetre,
redimensionnement...). Lorsque que tu cree un autre thread, il n'y a pas de
Message Loop qui tourne dessus. Du coup, si tu affiche une fenetre avec
Show dans ce nouveau thread, cette fenetre aura tendance a disparaitre
immédiatement faute de messages pour la maintenir en vie (la processus
exact est sans doute plus compliqué mais je ne suis pas un expert). La
solution pour ce probleme est de lancer un boucle de message sur ce thread.
Soit en appelant Application.Run soit en affichant la fenetre avec
ShowDialog qui démarre un boucle de message au contraire de Show.
Avatar
Elp
On Fri, 7 Jan 2005 09:39:57 +0100, Stephane TUET wrote:

Par contre, mon
application est en fenêtre MDI, donc la fenêtre d'attente, si je l'affiche
en modale, ça sera au niveau de la fenêtre principale, et ça revient à
bloquer mon application pendant le traitement ! Et donc finalement je me
retrouve avec un mode de fonctionnement équivalent au mode synchrone...
(c'est pas que je revendique le mode synchrone, mais j'ai du mal à
comprendre... :-) )



Mmm, si tu veux bloquer un de tes fenetres Mdi et laisser l'utilisateur la
possibilité d'utiliser les autres, alors tu pourrai désactiver ta fenetre
Mdi (pour empecher l'utilisateur de cliquer dedans) et afficher un Panel
par dessus avec un message d'attente par exemple. Cela dit est tu sur qu'il
ne serai pas plus judicieux de tout simplement désaciver le boutton
permettant d'envoyer la requete au service Web mais de laisser
l'utilisateur faire ce qu'il veux dans la fenetre. Apres tout, pourquoi ne
pourrai t'il pas s'amuser a faire d'autre modifications pendant que les
précédentes sont en train d'etre envoyées au service web. La seule chose
importante est que ces modifs soient envoyées dans l'ordre ou elles ont été
faites (d'ou la désactivation du boutton Envoyer pendant l'appel au service
Web).
Avatar
Stephane TUET
> Mmm, si tu veux bloquer un de tes fenetres Mdi et laisser l'utilisateur la
possibilité d'utiliser les autres, alors tu pourrai désactiver ta fenetre
Mdi (pour empecher l'utilisateur de cliquer dedans) et afficher un Panel
par dessus avec un message d'attente par exemple. Cela dit est tu sur
qu'il
ne serai pas plus judicieux de tout simplement désaciver le boutton
permettant d'envoyer la requete au service Web mais de laisser
l'utilisateur faire ce qu'il veux dans la fenetre. Apres tout, pourquoi ne
pourrai t'il pas s'amuser a faire d'autre modifications pendant que les
précédentes sont en train d'etre envoyées au service web. La seule chose
importante est que ces modifs soient envoyées dans l'ordre ou elles ont
été
faites (d'ou la désactivation du boutton Envoyer pendant l'appel au
service
Web).



Alors, pour resituer un peu mon problème... Je vais prendre un cas très
simple... Dans mon appli MDI, j'ai un menu qui me permet d'accéder à la
liste des fournisseurs (disons via l'équivalent d'un datagrid), à partir de
laquelle je peux ouvrir la fiche d'un fournisseur (jusque là, rien de bien
particulier). Si je suis ce que tu me dis (en gros, bloquer l'appel à un
service web s'il est déjà lancé), il faudrait que j'interdise à
l'utilisateur de pouvoir réouvrir le même fournisseur (sinon il va se
retrouver avec deux fenêtres identiques).

J'ai peur que ce principe m'oblige à faire un développement particulier en
fonction de chacun des services web que j'appelle (dans un cas bloquer une
ligne d'un datagrid, dans d'autre bloquer uniquement des champs, etc), ce
qui me parait trop fastidieux vu l'état d'avancement du projet... D'autant
plus que cela m'obligerais à reprendre chacun des appels aux services web
pour les rendre asynchrones. Même si l'idée de pouvoir faire une chose
pendant qu'une autre est en chargement me plait beaucoup...

D'autant plus que la plupart de mes services web ne prendront pas 15 jours à
s'éxécuter (en fait, le problème est surtout dans le cas où un client à une
connexion très lente). Je voulais juste indiquer à l'utilisateur qu'une
communication avec le serveur était en cours, et donc juste lui montrer que
c'était pas l'appli qui avait plantée :-) Même si l'attente est inférieure à
2s, ça peut toujours faire bizarre de n'avoir aucune réaction de l'appli à
une opération de l'utilisateur.

Je pense donc que je vais revenir sur mon idée de départ (même si les votres
me semblent très bonnes également, le prenez pas mal :-) ). J'arrive presque
à réaliser ce que je veux avec le code suivant (TimerWait étant maintenant
un System.Timers.Timer) :

Public Sub DebutAttente()

TimerWait.AutoReset = False

TimerWait.Interval = K_TIMER_WAIT

TimerWait.Start()

End Sub



Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

TimerWait.Stop()

frm_wait.ShowDialog()

End Sub



Public Sub FinAttente()

TimerWait.Stop()

frm_wait.Hide()

End Sub



Mon seul problème maintenant est que si FinAttente intervient pendant le
ShowDialog, le Hide intervient avant que la fenêtre soit complètement
ouverte et donc, elle se réouvre... Mais je ne sais pas comment faire pour
éviter cela... Peut être ajouter un booléen qui indiquerait à ma fenêtre
qu'à la fin de son ouverture, il faut qu'elle se referme ;-)

En tout cas je tiens à vous remercier pour votre aide et toutes vos
explications, j'en tiendrai compte pour mes prochains développements :-)

Steph
Avatar
Elp
On Fri, 7 Jan 2005 12:19:20 +0100, Stephane TUET wrote:

Je pense donc que je vais revenir sur mon idée de départ (même si les votres
me semblent très bonnes également, le prenez pas mal :-) ). J'arrive presque
à réaliser ce que je veux avec le code suivant (TimerWait étant maintenant
un System.Timers.Timer) :



Fait gaffe avec le System.Timers.Timer. Au contraire du
System.Windows.Form.Timer, la fonction exécutée lors du tick l'est dans un
nouveau thread et non pas dans le thread principal. Lit ma réponse a Pyroa
pour avoir une idée du probleme.

Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

TimerWait.Stop()

frm_wait.ShowDialog()

End Sub



Donc voila le probleme. Cette fonction s'exécute dans un thread différent
du thread principal. Tu appele ShowDialog sur une fenetre créé dans un
autre thread -> tu va tres probablement te retrouver un jour avec des bugs
a la con. Le truc marrant dans ces histoires d'acces a des controles depuis
différents thread, c'est que parfois (le plus souvent en fait) ca marche,
et parfois ca plante. Donc je suis sur que lors des tes tests, tout
fonctionne bien. Et puis un beau jour, un de tes clients t'appelera pour te
dire que ton appli passe son temps a planter lors de l'appel au service
Web. Et bon courage pour en trouver la raison.

Donc corrige le bug des maintenant: ne cree pas ta fenetre d'attente a
l'avance (dans le thread principal) mais crée la dans ta fonction
TimerWait_Elapsed. Du coup, ta fenetre sera crée et accédée dans un et
unique thread.

Public Sub FinAttente()

TimerWait.Stop()

frm_wait.Hide()

End Sub



Si tu applique mon conseil donné au dessus tu va te retrouver avec un
probleme la aussi car ta fonction FinAttente() s'exécute elle dans le
thread principal et tu ne peux donc pas appeler Hide sans t'exposer a des
problemes certains. A la place, utilise la methode Invoke de ta fenetre
d'attente pour marshaller l'appel a Hide vers le second thread.

Mon seul problème maintenant est que si FinAttente intervient pendant le
ShowDialog, le Hide intervient avant que la fenêtre soit complètement
ouverte et donc, elle se réouvre... Mais je ne sais pas comment faire pour
éviter cela... Peut être ajouter un booléen qui indiquerait à ma fenêtre
qu'à la fin de son ouverture, il faut qu'elle se referme ;-)



Une méthode qui marche sans etre peut etre tres propre:
1)déclare un booléen appelTerminé (variable membre de ta classe) initialisé
a false.
2) Met le contenu de tes fonctions TimerWait_Elapsed et FinAttente() dans
un lock (lock sur le meme objet bien sur). Du coup, une seule de ces
fonction pourra s'exécuter a un moment donné.
3) Dans TimerWait_Elapsed, a l'intérieur du lock, vérifie la valeur de
appelTerminé. Si c'est a true, ne fait rien. Sinon, stoppe le timer et
affiche la fenetre d'attente.
4) Dans FinAttente(), a l'intérieur du lock met appelTerminé a true.

Ca devrait marcher.


En tout cas je tiens à vous remercier pour votre aide et toutes vos
explications, j'en tiendrai compte pour mes prochains développements :-)



On peut se tutoyer hein, ca m'évitera d'avoir l'impression d'etre un vioc
:-)
Avatar
Stephane TUET
Bien... alors après avoir recherché ce qu'était le marshalling et après
avoir découvert comment on faisait un lock sur un objet, je suis arrivé à ça
:

Private frm_wait As FRM_Wait

Private WithEvents TimerWait As New System.Timers.Timer

Private Delegate Sub myHide()

Private lock As System.Threading.Monitor

Private objLock As Integer

Private AppelTermine As Boolean = False


Public Sub DebutAttente()

TimerWait.AutoReset = False

TimerWait.Interval = K_TIMER_WAIT

AppelTermine = False

TimerWait.Start()

End Sub



Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

lock.Enter(objLock)

If Not AppelTermine Then

TimerWait.Stop()

frm_wait = New FRM_Wait

frm_wait.ShowDialog()

End If

lock.Exit(objLock)

End Sub



Public Sub FinAttente()

lock.Enter(objLock)

TimerWait.Stop()

If Not frm_wait Is Nothing Then frm_wait.Invoke(New myHide(AddressOf
frm_wait.Hide))

AppelTermine = True

lock.Exit(objLock)

End Sub


Maintenant, d'abord ce que je ne comprends pas :-) : Comment le
lock.Exit(objLock) de TimerWait_Elapsed peut être exécuté alors qu'il est
après un showdialog ?

Ensuite, apparemment j'ai toujours le même problème puisque dans certains
cas j'obtiens l'erreur suivante :

Cannot call Invoke or InvokeAsync on a control until the window handle has
been created.

C'est à dire quand FinAttente arrive alors que la fenêtre d'attente est en
cours d'ouverture (je suppose).


Encore merci

Steph


"Elp" a écrit dans le message de news:
1anllq52pe02j.bmpq3xpxrcjg$
On Fri, 7 Jan 2005 12:19:20 +0100, Stephane TUET wrote:

Je pense donc que je vais revenir sur mon idée de départ (même si les
votres
me semblent très bonnes également, le prenez pas mal :-) ). J'arrive
presque
à réaliser ce que je veux avec le code suivant (TimerWait étant
maintenant
un System.Timers.Timer) :



Fait gaffe avec le System.Timers.Timer. Au contraire du
System.Windows.Form.Timer, la fonction exécutée lors du tick l'est dans un
nouveau thread et non pas dans le thread principal. Lit ma réponse a Pyroa
pour avoir une idée du probleme.

Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

TimerWait.Stop()

frm_wait.ShowDialog()

End Sub



Donc voila le probleme. Cette fonction s'exécute dans un thread différent
du thread principal. Tu appele ShowDialog sur une fenetre créé dans un
autre thread -> tu va tres probablement te retrouver un jour avec des bugs
a la con. Le truc marrant dans ces histoires d'acces a des controles
depuis
différents thread, c'est que parfois (le plus souvent en fait) ca marche,
et parfois ca plante. Donc je suis sur que lors des tes tests, tout
fonctionne bien. Et puis un beau jour, un de tes clients t'appelera pour
te
dire que ton appli passe son temps a planter lors de l'appel au service
Web. Et bon courage pour en trouver la raison.

Donc corrige le bug des maintenant: ne cree pas ta fenetre d'attente a
l'avance (dans le thread principal) mais crée la dans ta fonction
TimerWait_Elapsed. Du coup, ta fenetre sera crée et accédée dans un et
unique thread.

Public Sub FinAttente()

TimerWait.Stop()

frm_wait.Hide()

End Sub



Si tu applique mon conseil donné au dessus tu va te retrouver avec un
probleme la aussi car ta fonction FinAttente() s'exécute elle dans le
thread principal et tu ne peux donc pas appeler Hide sans t'exposer a des
problemes certains. A la place, utilise la methode Invoke de ta fenetre
d'attente pour marshaller l'appel a Hide vers le second thread.

Mon seul problème maintenant est que si FinAttente intervient pendant le
ShowDialog, le Hide intervient avant que la fenêtre soit complètement
ouverte et donc, elle se réouvre... Mais je ne sais pas comment faire
pour
éviter cela... Peut être ajouter un booléen qui indiquerait à ma fenêtre
qu'à la fin de son ouverture, il faut qu'elle se referme ;-)



Une méthode qui marche sans etre peut etre tres propre:
1)déclare un booléen appelTerminé (variable membre de ta classe)
initialisé
a false.
2) Met le contenu de tes fonctions TimerWait_Elapsed et FinAttente() dans
un lock (lock sur le meme objet bien sur). Du coup, une seule de ces
fonction pourra s'exécuter a un moment donné.
3) Dans TimerWait_Elapsed, a l'intérieur du lock, vérifie la valeur de
appelTerminé. Si c'est a true, ne fait rien. Sinon, stoppe le timer et
affiche la fenetre d'attente.
4) Dans FinAttente(), a l'intérieur du lock met appelTerminé a true.

Ca devrait marcher.


En tout cas je tiens à vous remercier pour votre aide et toutes vos
explications, j'en tiendrai compte pour mes prochains développements :-)



On peut se tutoyer hein, ca m'évitera d'avoir l'impression d'etre un vioc
:-)


Avatar
Elp
On Fri, 7 Jan 2005 15:35:35 +0100, Stephane TUET wrote:

Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

lock.Enter(objLock)

If Not AppelTermine Then

TimerWait.Stop()

frm_wait = New FRM_Wait

frm_wait.ShowDialog()

End If

lock.Exit(objLock)

End Sub



Maintenant, d'abord ce que je ne comprends pas :-) : Comment le
lock.Exit(objLock) de TimerWait_Elapsed peut être exécuté alors qu'il est
après un showdialog ?



Bon d'accord, j'ai honte, surtout apres tout le temps que j'ai passé a
m'amuser avec threads et autres joyeusetés du meme genre. Effectivement, le
lock n'est jamais relaché. Théoriquement, tu devrais avoir un deadlock vu
que que pour fermer ta fenetre tu doit acquérir le lock qui n'est relaché
que lorsque la fenetre sera fermée (donc appli gelée indéfiniment).

Maintenant tu va me dire que non, il n'y a pas de deadlock puisque tu a
essayé et que l'appli n'était pas gelée. Et c'est la qu'arrive un des
subtilités du Monitor.Enter. Dans la doc de cette fonction, tu va trouver:

"Use Monitor to lock objects (that is, reference types), not value types.
When you pass a value type variable to Enter, it is boxed as an object. If
you pass the same variable to Enter again, it is boxed as a separate
object, and the thread does not block."

Tu utilise un Integer comme lock. C'est un "value type". Donc a chaque fois
que tu appele Enter sur ton Integer, le lock s'effectue sur un nouvel
objet. Autrement, ton lock ne sert a rien. Remplace:

Private objLock As Integer
par
Private objLock As New Object

Execute maintenant ton appli et ca va bloquer (car Object est un "reference
type" donc tu lock toujours sur le meme objet).

Ensuite, apparemment j'ai toujours le même problème puisque dans certains
cas j'obtiens l'erreur suivante :

Cannot call Invoke or InvokeAsync on a control until the window handle has
been created.

C'est à dire quand FinAttente arrive alors que la fenêtre d'attente est en
cours d'ouverture (je suppose).



Oui vu qu'en fait ton lock ne fonctionnait pas donc tu était dans la meme
situation qu'avant.

Reste que ca ne résout pas ton probleme. Et la je seche. Si je trouve un
truc, je te le dirai mais la je vois pas.
Avatar
Stephane TUET
Bien... Je suis arrivé à un truc qui marche avec ceci :

Public Sub DebutAttente()

TimerWait.AutoReset = False

TimerWait.Interval = K_TIMER_WAIT

TimerWait.Start()

End Sub



Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

TimerWait.Stop()

frm_wait = New FRM_Wait

frm_wait.ShowDialog()

End Sub



Public Sub FinAttente()

TimerWait.Stop()

If Not frm_wait Is Nothing Then

If frm_wait.IsHandleCreated Then

frm_wait.Invoke(New myClose(AddressOf frm_wait.Close))

Else

frm_wait.SetFermetureDemandee = True

End If

End If

End Sub


Avec dans FRM_Wait le code suivant :

Private FermetureDemandee As Boolean = False


Public WriteOnly Property SetFermetureDemandee()

Set(ByVal Value)

FermetureDemandee = Value

End Set

End Property



Private Sub FRM_Wait_VisibleChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyBase.VisibleChanged

If FermetureDemandee Then

Application.DoEvents()

Me.Close()

End If

End Sub

Je n'ai pas réussi à faire plus simple, alors si quelqu'un à une meilleure
idée, je suis toujours preneur :-)

Voilà voilà, et encore merci à tout ceux qui m'ont aidé.

@+
Steph


"Elp" a écrit dans le message de news:
bhez21opaifw$
On Fri, 7 Jan 2005 15:35:35 +0100, Stephane TUET wrote:

Private Sub TimerWait_Elapsed(ByVal sender As Object, ByVal e As
System.Timers.ElapsedEventArgs) Handles TimerWait.Elapsed

lock.Enter(objLock)

If Not AppelTermine Then

TimerWait.Stop()

frm_wait = New FRM_Wait

frm_wait.ShowDialog()

End If

lock.Exit(objLock)

End Sub



Maintenant, d'abord ce que je ne comprends pas :-) : Comment le
lock.Exit(objLock) de TimerWait_Elapsed peut être exécuté alors qu'il est
après un showdialog ?



Bon d'accord, j'ai honte, surtout apres tout le temps que j'ai passé a
m'amuser avec threads et autres joyeusetés du meme genre. Effectivement,
le
lock n'est jamais relaché. Théoriquement, tu devrais avoir un deadlock vu
que que pour fermer ta fenetre tu doit acquérir le lock qui n'est relaché
que lorsque la fenetre sera fermée (donc appli gelée indéfiniment).

Maintenant tu va me dire que non, il n'y a pas de deadlock puisque tu a
essayé et que l'appli n'était pas gelée. Et c'est la qu'arrive un des
subtilités du Monitor.Enter. Dans la doc de cette fonction, tu va trouver:

"Use Monitor to lock objects (that is, reference types), not value types.
When you pass a value type variable to Enter, it is boxed as an object. If
you pass the same variable to Enter again, it is boxed as a separate
object, and the thread does not block."

Tu utilise un Integer comme lock. C'est un "value type". Donc a chaque
fois
que tu appele Enter sur ton Integer, le lock s'effectue sur un nouvel
objet. Autrement, ton lock ne sert a rien. Remplace:

Private objLock As Integer
par
Private objLock As New Object

Execute maintenant ton appli et ca va bloquer (car Object est un
"reference
type" donc tu lock toujours sur le meme objet).

Ensuite, apparemment j'ai toujours le même problème puisque dans certains
cas j'obtiens l'erreur suivante :

Cannot call Invoke or InvokeAsync on a control until the window handle
has
been created.

C'est à dire quand FinAttente arrive alors que la fenêtre d'attente est
en
cours d'ouverture (je suppose).



Oui vu qu'en fait ton lock ne fonctionnait pas donc tu était dans la meme
situation qu'avant.

Reste que ca ne résout pas ton probleme. Et la je seche. Si je trouve un
truc, je te le dirai mais la je vois pas.


1 2