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

Signaler
Messages postés
189
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
30 avril 2021
-
Messages postés
189
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
30 avril 2021
-
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...

5 réponses

Messages postés
15927
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
13 mai 2021
544
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é
Messages postés
189
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
30 avril 2021
2
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...
Messages postés
15927
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
13 mai 2021
544
oui
Messages postés
189
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
30 avril 2021
2
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.
Messages postés
15927
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
13 mai 2021
544
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.


Messages postés
189
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
30 avril 2021
2
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.