Communcation entre Form et procédure asynchrone [Résolu]

Messages postés
98
Date d'inscription
vendredi 1 septembre 2006
Dernière intervention
24 octobre 2018
- - Dernière réponse : MGD Software
Messages postés
98
Date d'inscription
vendredi 1 septembre 2006
Dernière intervention
24 octobre 2018
- 29 août 2018 à 13:38
Bonjour,

J'ai un gros problème avec les threads, et particulièrement avec les socket asynchrones.
Après avoir écumé le net en tous sens traitant des threads et des procédures asynchrones, je n'ai pas trouvé la solution.

J'ai une classe de communication Serveur TCP qui à priori fonctionne bien, mais avec laquelle la feuille principale n'arrive pas à communiquer.
Pour éviter les trop grandes parties de code, je vais me limiter à la seule partie de la connexion.

Principe :
- La feuille principale dispose d'un bouton "Écoute".
- L'appui sur ce bouton crée une instance de serveur, installe les gestionnaires d'évènement de la classe, puis appelle la méthode Start de la classe.
- La classe crée une socket et appelle la méthode BeginAccept avec une fonction de callback.
- Lorsque le client se connecte, la fonction de callback déclenche un évènement "OnCLientConnected"
- Dans la feuille principale, le gestionnaire associé reçoit cet évènement et tente de mettre à jour un contrôle Label avec le texte "Connecté".

Et là, exception !
System.InvalidOperationException : "Opération inter-threads non valide : le contrôle 'lblState' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé."


Quelqu'un peut-il m'expliquer comment faire en sorte de pouvoir récupérer les informations envoyées par une procédure asynchrone dans une classe distincte de la classe principale ?

J'hésite à joindre le code de la classe et de la Form, cela fait plus de 600 lignes...
Afficher la suite 

Votre réponse

5 réponses

Messages postés
12362
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
9 décembre 2018
0
Merci
Bonsoir

Comme le message te le dit, seul le thread du form à le droit d'accéder aux contrôles.
De ce fait, il ne peut pas y avoir de concurrence d'accès.

Il faut utiliser le dispatcher de ton programme.
En supposant que ton évènement ait comme paramètre un string qui s'appelle message
        Dispatcher monDispatcher = Dispatcher.CurrentDispatcher;
        private void OnClientConnected(string Message)
        {
            monDispatcher.BeginInvoke(new Action(delegate
            {
                label1.Text = Message;
            }
            ));
        }


ceci est valable quelque soit le type de thread utilisé
Commenter la réponse de Whismeril
Messages postés
98
Date d'inscription
vendredi 1 septembre 2006
Dernière intervention
24 octobre 2018
0
Merci
Merci Whismeril. Toujours à l'écoute...

Je vais essayer ça dès que possible. Je posterai les résultats.
Je suppose qu'on peut utiliser le même dispatcher pour tous les évènements se produisant dans un callback (de cette classe ou d'une autre) ?

A suivre...
Whismeril
Messages postés
12362
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
9 décembre 2018
-
oui
Commenter la réponse de MGD Software
Messages postés
98
Date d'inscription
vendredi 1 septembre 2006
Dernière intervention
24 octobre 2018
0
Merci
Euh...
J'ai eu un peu de mal avec l'objet dispatcher : inconnu dans mon espace de nom.
Après un recherche dans la MSDN, j'ai trouvé qu'il fallait le using "System.Windows.Threading".

Mais alors, Visual studio ne reconnaissait pas cet espace de nom et soulignait en rouge le mot "Threading".
Recherche du mot Treading dans l'explorateur d'objet : rien.
Recherche du mot Threading dans le panneau d'ajout de références : rien

Après une nouvelle recherche sur le net, j'ai trouvé un post traitant du problème qui indiquait qu'il fallait référencer la dll WindowsBase. Effectivement, désormais le Dispatcher est utilisable.

J'ai donc modifié mon code ainsi :

Dans les déclarations de ma classe de form (wMain) :
private Dispatcher Dispatch = Dispatcher.CurrentDispatcher;

Dans le gestionnaire d'évènement traitant la connexion d'un client :
        private void OnServerClientConnected(object sender, EventArgs e)
        {
            string[] Data = ((string)sender).Split('\t');

            Dispatch.BeginInvoke(new Action(delegate
            {
                lblState.Text = "Client connecté";
                lblRemoteIP.Text = Data.Length >= 0 ? Data[0] : "???.???.???.???";
                txtRemotePort.Text = Data.Length > 0 ? Data[1] : "???";
                txtRemotePort.Text = Data.Length > 1 ? Data[2] : "????????";
            }));
        }

Et ça marche !

Maintenant, il me reste à trouver comment détecter la déconnexion sauvage du client...
Tout ce que j'ai trouvé sur le net ne fonctionne pas ou pas bien. La propriété Connected de la socket n'étant pas significative, j'ai testé le Poll sans résultat. Quelqu'un aurait-il une solution fiable ? Sinon, je vais tenter de mettre un WaitOne avec un timeout, et relancer l’écoute périodiquement, mais j'avoue que cette solution ne me plait pas vraiment car on perd tout l'avantage de l'asynchrone (la classe est bloquée pendant l'attente, et on ne peut l'utiliser pour envoyer des données).
Le contrôle Winsock de VB6 avait un évènement pour cela. J'aimerais bien reproduire cela pour ma classe de comm.

PS à l'intention des puristes : L'utilisation de l'objet sender pour passer les données n'est pas très propre, je sais... Je créerai des EventArgs spécifiques quand cela fonctionnera.
Commenter la réponse de MGD Software
Messages postés
12362
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
9 décembre 2018
0
Merci
Bonjour


J'ai eu un peu de mal avec l'objet dispatcher : inconnu dans mon espace de nom.
....
Mais alors, Visual studio ne reconnaissait pas cet espace de nom et soulignait en rouge le mot "Threading".


Alors, petite astuce d'utilisation de Visual Studio, le capture est faite avec VS2017, c'était présenté un peu différemment dans 2013 mais l'idée reste la même, il faut juste faire un click droit sur l'élément souligné en rouge
Quand il te manque un "using" et que la référence est présente, on a ça:

Si tu choisis "using System.Windows.Threading" ça va ajouter cette lignes dans les using (voir l'aperçu), si tu choisis System.Windows.Threading.Dispacther, ça va ajouter System.Windows.Threading dans la ligne de code en cours.

Si tu n'as pas accès à ce menu, c'est que la référence n'est pas présente, dans ce cas MSDN est la source de vérité


Tu ajoutes d'abord la référence et ensuite tu appliques la procédure du dessus.


Maintenant, il me reste à trouver comment détecter la déconnexion sauvage du client...
c'est un autre sujet, je ne suis pas très calé en TCP, le seul projet où je m'en sers n'est pas de moi au départ et il y avait une classe perso pour la connexion. Cette classe dispose d'un évènement signalant la coupure de la connexion, mais je ne sais pas si cela vient directement de la classe du framework ou d'un code spécifiquement écrit pour.

Je te conseille donc d'ouvrir un sujet dédié, auquel je ne répondrai pas.


Commenter la réponse de Whismeril
Messages postés
98
Date d'inscription
vendredi 1 septembre 2006
Dernière intervention
24 octobre 2018
0
Merci
Merci pour les tuyaux sur VS. Je n'avais pas prêté attention à la ligne "Assembly".
Dommage pour le TCP...

Encore merci pour le threading, ça resservira encore beaucoup.
Commenter la réponse de MGD Software

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.