IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++/CLI Discussion :

Threads et TextBox


Sujet :

C++/CLI

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Juillet 2010
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2010
    Messages : 3
    Points : 1
    Points
    1
    Par défaut Threads et TextBox
    Bonjour,

    Tout d'abord je voudrai remercier la communauté de developpez.net où j'ai pu trouver des solutions à la plupart de mes problèmes rien qu'en parcourant les forums, les FAQ et les tutoriaux depuis plus d'un an maintenant.

    Je réalise en ce moment un projet sous visual studio C++ 2008 basé sur une windows form sensée afficher des données reçues via un périphérique externe (type port série) dans une textBox. Il y a donc un thread (n°1) consacré aux contrôles de la windows Form et un autre thread (n°2) qui gère les communications avec le périphérique.

    Lorsqu'une trame est reçue par le thread n°2 je ne peux pas directement l'écrire dans la textBox de mon interface. J'ai donc mis en place un delegate pour pouvoir accéder aux routines de l'interface graphique. Il est déclaré dans un .h commun à tout le projet :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    delegate void Ecrire(void);
    Le code à exécuter pour écrire dans la textbox est décrit dans la classe de la windows form(Projet::Form1). Pour l'instant je veux juste afficher OK à chaque réception d'une trame:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public: void EcrireTextBox(void)
    		{
    			textBox1->AppendText("OK");
    		}
    Enfin, dans le fichier .cpp contenant les fonctions bas de niveau de communication avec le port série je fais appel au délégué pour accéder à EcrireTextBox :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Projet::Form1 ^ pnt = gcnew Projet::Form1;
    Ecrire ^ delegue = gcnew Ecrire(pnt, &Projet::Form1::EcrireTextBox);
    delegue->Invoke();
    Quand je met tout ça en debug je vois que lorsqu'une trame est reçue je rentre bien dans la fonction "EcrireTextBox" et son code est exécuté. Par contre rien ne s'affiche dans la TextBox. En observant les outils de debug j'ai vu que le programme restait toujours dans le thread n°2 ce qui, pour moi, explique qu'il n'y a aucun effet sur la textBox (gérée par le thread n°1).

    Comment puis-je redonner la main à l'interface graphique (thread n°1) juste le temps de modifier la textbox?

    Merci d'avance

  2. #2
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Bonjour

    Ton problème est plus un problème .net qu'un problème C++.
    Il serait peut être préférable de l'exposer dans le forum .Net


    Sinon, et à vue de nez (bien que ayant perdu de vue le C++ depuis ... longtemps et surtout ne comprenant pas trop bien l'interêt de son usage en environnement managé) :

    - je ne comprends pas pourquoi tu instancies ta form avant d'écrire dans le TextBox.

    - si tu veux écrire depuis un thread secondaire, il faut que tu utilises le thread qui a instancié la forme pour accéder à un control quelconque.

    - pour cela, utiliser la méthode Invoke sur la Form et pas sur le delegate. (tester préalablement via la propriété InvokeRequired)

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Juillet 2010
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2010
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    En effet je ne sais pas non plus pourquoi je créait une nouvelle instance avant l'appel de ma fonction. Ça m'apprendra à copier des exemples.
    J'ai donc créé une variable de type Projet::Form1 et pointant sur le thread principal contenant les contrôles.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static Projet::Form1 ^MainForm;
    et dans le code constructeur de la form :
    J'ai adapté l'appel de mon délégué comme tu me l'as indiqué :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Ecrire ^ pointeur = gcnew Ecrire(Projet::Form1::MainForm, &Projet::Form1::EcrireTextBox);
    if(Projet::Form1::MainForm->InvokeRequired) 
    {
    	Projet::Form1::MainForm->Invoke(pointeur);		 
     
    }
    Et ça marche ! merci beaucoup!

    J'ai utilisé le C++ car les fonctions bas-niveau pour la liaison série sont fournies par le constructeur du périphérique en C++. Si tu vois une autre solution beaucoup plus simple, ça m'intéresse. (Sachant que le fabricant propose aussi son code en C# mais je ne connais pas du tout ce langage).

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    STOP.
    Pas de variables globales déguisées.

    Si vous avez besoin d'accéder à un objet unique depuis n'importe quel endroit de votre programme, cela s'appel un singleton et cela s'implémente avec le Design Pattern Singleton, pas avec un membre static non thread-safe, non AppDomain-Aware etc. .

    Citation Envoyé par Bluedeep Voir le message
    - je ne comprends pas pourquoi tu instancies ta form avant d'écrire dans le TextBox.


    Vous devez avoir la form instancié pour avoir les contrôles qu'elle contient instanciés. Il faut que l'objet contrôle soit instancié pour pouvoir initialiser une de ses propriétés.

    Moi, je passerais tout simplement une référence de la form à la routine du thread (n°2) qu'elle utiliserait directement.



    Targus003, je pense que vous souffrez d'un syndrome extrêmement commun pour un programmeur C++ débutant dans les applications multi-threadés avec IHM, que j'appellerais le clivage Objet/Thread.

    La conception d'Objet en C++ n'est pas celle d'Objective-C ou de SmallTalk.
    Dans ces langages, chaque objet dispose de sa propre file de message et l'appel de méthode correspond à l'envoie synchrone d'un message et de la récupération de sa réponse. Un seul thread a le droit de lire la file de message d'un objet. Il y a donc cloisonnement strict entre les autres threads et l'objet. L'objet n'est associé qu'à un thread dans ces langages.
    (Je ne suis pas un spécialistes de ces langages, j'espère n'avoir pas dit trop de conneries )

    En C++, ce n'est pas du tout le cas. Un objet n'est pas "hébergé" dans un thread. Tous les threads peuvent appeler les méthodes de tous les objets. Le fait de déclarer une méthode dans la form ne garanti pas que celle-ci ne sera appelé que depuis le thread créateur de la form.

    Pour les coupeurs de cheveux en quatre, on peut avoir recoure au STA de COM ou à de multiples AppDomain en .NET pour simuler le comportement "à la smallTalk" de l'hébergement des objets dans des threads.
    C'est très complexe et ne correspondra pas aux habitudes de conception des programmeurs C++ expérimentés.

    Pour ma part, j'essaye de conserver ces problématiques loin de mon code par l'utilisation du principe d'encapsulation de la POO.
    En termes clairs, c'est d'encapsuler les méthodes qui sont "sensibles" au thread les appelants, dans une espèce de sas à thread.

    Le sas a, dans le cas d'une form, toujours la même tête:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    public: void méthode_SAS(void)
    {
            if(InvokeRequired)
            {
                    Invoke(méthode_sensible);
            }
            else
            {
                    méthode_sensible();
            }
    }
     
    private: void méthode_sensible(void)
    {
            ...
    }
    Et ne jamais appeler méthode_sensible directement.

  5. #5
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Citation Envoyé par bacelar Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    - je ne comprends pas pourquoi tu instancies ta form avant d'écrire dans le TextBox.


    Vous devez avoir la form instancié pour avoir les contrôles qu'elle contient instanciés. Il faut que l'objet contrôle soit instancié pour pouvoir initialiser une de ses propriétés.
    IL est évident qu'il faisait une instanciation de sa form à l'intérieur du code de celle-ci. Ce qui n'a aucun sens.

    Moi, je passerais tout simplement une référence de la form à la routine du thread (n°2) qu'elle utiliserait directement.
    Cela me semble évident. Mais on a pas le code global, donc inférer sur les détails d'implémentation est délicat.


    En C++, ce n'est pas du tout le cas. Un objet n'est pas "hébergé" dans un thread. Tous les threads peuvent appeler les méthodes de tous les objets. Le fait de déclarer une méthode dans la form ne garanti pas que celle-ci ne sera appelé que depuis le thread créateur de la form.
    Ici la problèmatique n'est pas du tout C++, mais .Net.

    C'est très complexe et ne correspondra pas aux habitudes de conception des programmeurs C++ expérimentés.
    Programmeurs qui founissent en général des codes déplorables dans les énvironnements managés ....

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Citation Envoyé par Bluedeep Voir le message
    Ici la problèmatique n'est pas du tout C++, mais .Net.


    Un objet est accessible par n'importe quel thread.
    Sur cet aspect, .NET, C++/CLI et C++ ont tous le même comportement.


    Exemple C++ non .NET, les MFC, où il n'y a pas un gramme de .NET, utilisent les des SendMessage et du hooking de fenêtre pour palier à ces "limitations".

    Où, dans la norme C++, est indiqué que les accès aux objets sont circonscrit à un thread "créateur"?

  7. #7
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Citation Envoyé par bacelar Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Ici la problèmatique n'est pas du tout C++, mais .Net.


    Un objet est accessible par n'importe quel thread.
    C'est ici que vous proférez une erreur grossiére, visiblement par méconnaissance de l'environnement .Net (qui est celui utilisé par le posteur initial).

    Dans l'environnement .Net, les propriétés (sauf 'InvokeRequired') des instances d'objets hérités de Control ne sont manipulables que par le thread qui les a instanciés.

    Comme vous ne disposez pas forcément d'une référence à ce thread, tous ces objets exposent une méthode 'Invoke' permettant l'exécution d'un delegate dans le contexte du thread ayant créé l'instance de l'objet en question.

    Sur cet aspect, .NET, C++/CLI et C++ ont tous le même comportement.
    Je ne vois pas le rapport entre le langage et le framework.

    Exemple C++ non .NET, les MFC, où il n'y a pas un gramme de .NET, utilisent les des SendMessage et du hooking de fenêtre pour palier à ces "limitations".
    Oui, c'est la b-a-ba de la programmation windows "native".

    Où, dans la norme C++, est indiqué que les accès aux objets sont circonscrit à un thread "créateur"?
    Encore une fois, le langage utilisé n'a strictement rien à voir avec la problématique.

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Pour le problème de Targus003 ; Bluedeep es-tu d'accord que mon code est convenable ?

    C'est qu'après, on va avoir droit à une discutions de "casse-couilles" qui n'aidera pas à résoudre correctement le problème de Targus003.

    Citation Envoyé par Bluedeep Voir le message
    C'est ici que vous proférez une erreur grossiére, visiblement par méconnaissance de l'environnement .Net (qui est celui utilisé par le posteur initial).

    Dans l'environnement .Net, les propriétés (sauf 'InvokeRequired') des instances d'objets hérités de Control ne sont manipulables que par le thread qui les a instanciés.
    Cela n'a strictement rien avoir avec .NET et vu votre connaissance du code natif sous Windows, vous le savez très bien.
    Utilisez Reflector pour voir l'implémentation de la propriété Text et vous tomberez sur des SetWindowText de l'API Win32 notablement dangereuse pour cause de deadlock.
    Cette limitation vient directement des contrôles Windows, comme en code natif.
    C'est étrange, c'est lié à .NET mais en code natif, C++ ou pas, on a le même problème. Bizarre non ?

    Il faut juste que l'utilisateur comprenne que l'objet n'est pas isolé dans un thread comme en smallTalk mais qu'il est accessible par tous les threads. Et cela est vrai en C++ mais pas dans des langages .NET comme F#, je crois.

  9. #9
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 314
    Points
    13 314
    Par défaut
    Citation Envoyé par bacelar Voir le message
    Pour le problème de Targus003 ; Bluedeep es-tu d'accord que mon code est convenable ?
    Entiérement d'accord, mais j'ai l'impression qu'on l'embrouille quelque peu

    Il faut juste que l'utilisateur comprenne que l'objet n'est pas isolé dans un thread comme en smallTalk mais qu'il est accessible par tous les threads. Et cela est vrai en C++ mais pas dans des langages .NET comme F#, je crois.
    Je ne connais pas du tout SmallTalk, même approximativement, donc ne ferais pas de comparaison avec.

    Je n'ai par ailleurs jamais dit que l'objet était isolé dans un thread.

    Concernant F# je ne l'ai pas regardé encore d'assez près pour être affirmatif.

    Concernant les autres langages .Net (VB.Net et C#), il n'y a aucune limitation à l'accés entre thread des objets, du point de vue de la visibilité; il y peu y avoir en revanche des limitations de manipulations inter-thread, liés à l'implémentation. (valable en C++ aussi).

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  10. #10
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Juillet 2010
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2010
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    Merci pour ces précisions. J'avoue être un peu perdu dans tout ça mais l'essentiel est que mon petit bout de code marche et mon projet est débloqué. Un électronicien se contente de peu.

    Je suis toujours preneur de conseils pour la réalisation rapide d'interface graphique communicant avec un port série (sujet récurrent dans mes projets). J'ai déjà travaillé avec les MFC, maintenant les Windows Form et je trouve la programmation très fastidieuse pour réaliser des fonctions assez basiques.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Je pense que le caractère fastidieux est un corolaire d'utilisation de code mal conçu.

    Exemple, notre cas de figure :

    Votre Code ~
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    ...
    delegate void Ecrire(void);
    ...
    static Projet::Form1 ^MainForm;
     
    public ref class Projet::Form1 : ...
    {
    ...
       MainForm=this;
    ...
    public: void EcrireTextBox(void)
    		{
    			textBox1->AppendText("OK");
    		}
    ...
    }
    ...
    TITI::méthode1
    {
    ...
           Ecrire ^ pointeur = gcnew Ecrire(Projet::Form1::MainForm, &Projet::Form1::EcrireTextBox);
          if(Projet::Form1::MainForm->InvokeRequired) 
          {
    	Projet::Form1::MainForm->Invoke(pointeur);		 
     
          }
    ...
    }
    ...
    TATA::méthode2
    {
    ...
           Ecrire ^ pointeur = gcnew Ecrire(Projet::Form1::MainForm, &Projet::Form1::EcrireTextBox);
          if(Projet::Form1::MainForm->InvokeRequired) 
          {
    	Projet::Form1::MainForm->Invoke(pointeur);		 
     
          }
    ...
    }
    ...
    TOTO::méthode3
    {
    ...
           Ecrire ^ pointeur = gcnew Ecrire(Projet::Form1::MainForm, &Projet::Form1::EcrireTextBox);
          if(Projet::Form1::MainForm->InvokeRequired) 
          {
    	Projet::Form1::MainForm->Invoke(pointeur);		 
     
          }
    ...
    }
    Mon code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
     
    public ref class Projet::Form1 : ...
    {
    ...
       public: void SetOK(void)
       {
            if(InvokeRequired)
            {
    	Invoke(_EcrireTextBoxOK());
            }
            else
            {
    	_EcrireTextBoxOK();
            }
       }
     
       private: void _EcrireTextBoxOK(void)
       {
            textBox1->AppendText("OK");
       }
    ...
    }
     
     
    TITI::méthode1
    {
    ...
       myForm1.SetOK();
    ...
    }
    ...
    TATA::méthode2
    {
    ...
       myForm1.SetOK();
    ...
    }
    ...
    TOTO::méthode3
    {
    ...
       myForm1.SetOK();
    ...
    }

  12. #12
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    289
    Détails du profil
    Informations personnelles :
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2008
    Messages : 289
    Points : 151
    Points
    151
    Par défaut
    bonjour,
    je reprend le sujet car je suis dans la même config que targus003 avec un thread2 créé par thread1 et qui veut afficher ce qu'il reçoit dans une textBox portée par thread1.
    J'ai donc écrit ceci en m'inspirant de ce que vous avez proposé et d'un peu de msdn:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    fichier form1.h:
    ----------------
    private:
        delegate void Ecrire();	//la "fonction" déléguée
        Ecrire^ myDelegate;	//une référence pointant sur la fonction déléguée
     
    Form1()
    {
        myDelegate = gcnew Ecrire(this, &Form1::writeTextBox);
    }
     
    private: System::Void btnStart_Click(System::Object^ sender, System::EventArgs^ e) {
        Thread^ InstanceCaller = gcnew Thread(gcnew ThreadStart(this, &Form1::ThreadFunction));
        // Start the thread.
        InstanceCaller->Start();
    }
     
    private: void ThreadFunction() {
        //Instancier un objet de la classe threadClass en lui passant la Form1
        threadClass^ myThreadClassObject = gcnew threadClass(this);
        myThreadClassObject->Run();
    }
     
    Fichier threadClass.cpp
    -----------------------
    threadClass::threadClass(Form^ f)
    {
        mainForm = f;    //recopie de la Form1 dans l'attribut Form^ mainForm
    }
     
    void threadClass::Run()
    {
        mainForm->Invoke(mainForm->myDelegate);
    }
    J'ai toujours l'erreur
    threadClass.cpp(32): error C2039: 'myDelegate'*: n'est pas membre de 'System::Windows::Forms::Form'
    comme si mainForm ne "pointait" pas vraiment sur Form1

    Merci de rabaisser mon niveau d'incompétence.

  13. #13
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Je n'ai pas tout le code, mais je pense que vous avez déclaré le champ "mainForm" de la classe threadClass comme un objet de type "Form" et non un objet de type Form1
    (attention les noms des classes devraient commencer par une majuscule )

    Changez la déclaration de ce champ et la signature de "threadClass::threadClass(Form^ f)"" en "threadClass::threadClass(Form1^ f)"

    Le compilateur vous foutra la paix.

  14. #14
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    289
    Détails du profil
    Informations personnelles :
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2008
    Messages : 289
    Points : 151
    Points
    151
    Par défaut
    bonjour
    je poste mon code expurgé de ce qui ne concerne pas le problème.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    Fichier form1.h:
    ----------------
    namespace tstThread {
        public ref class Form1 : public System::Windows::Forms::Form
        {
            public:
                Form1(void)
    	    {
    		myDelegate = gcnew Ecrire(this, &Form1::writeTextBox);
    	    }
     
            private:
                delegate void Ecrire();	//la "fonction" déléguée
    	Ecrire^ myDelegate;	//une référence sur la fonction déléguée
     
    	//Gestion du bouton Exit
    	private: System::Void btnExit_Click(System::Object^ sender, System::EventArgs^ e) {
    	    Application::Exit();
    		}
     
    	//Gestion du bouton Start
    	private: System::Void btnStart_Click(System::Object^ sender, System::EventArgs^ e) {
    	    threadClass^ myThread = gcnew threadClass(this);    //passe une référence sur Form1
    	    Thread^ InstanceCaller = gcnew Thread(gcnew ThreadStart(myThread, &threadClass::Run));
    	    // Start the thread.
    	    InstanceCaller->Start();
    	}
     
    	private: void ThreadFunction() {
    	    System::Windows::Forms::MessageBox::Show("ThreadFunction");
    	    threadClass^ myThreadClassObject = gcnew threadClass(this);
    	    myThreadClassObject->Run();
    	}
     
    	/* méthode exécutée par le délégué */
    	public: void writeTextBox() {
    	    this->textBox1->Text = "Hello";
    	}
        };
    }
     
    Fichier threadClass.h:
    ----------------------
    ref class threadClass
    {
        private:
            System::Windows::Forms::Form^ mainForm;
     
        public:
            threadClass(Form^);
            void Run();
    };
     
    Fichier threadClass.cpp:
    ------------------------
    #include "threadClass.h"
     
    threadClass::threadClass(Form^ f)
    {
        mainForm = f;
    }
     
    void threadClass::Run()
    {	
        if(mainForm->InvokeRequired)
            System::Windows::Forms::MessageBox::Show("threadClass::InvokeRequired = true");
        else System::Windows::Forms::MessageBox::Show("threadClass::InvokeRequired = false");
     
        mainForm->Invoke(mainForm->myDelegate);
    }
    Utiliser Form1 au lieu de Form comme suggéré ne compile pas car Form1 n'est pas connu au niveau de threadClass. Il me manque peut-être une déclaration quelque part.

    La méthode Run affiche true (si je commente l'appel à la propriété
    InvokeRequired) sinon le compilateur répond toujours:
    error C2039: 'myDelegate'*: n'est pas membre de 'System::Windows::Forms::Form'
    Le souci ne viendrait-il pas de l'instruction: mainForm = f; ? qui fait que mainForm est bien de type Form^ mais ne "pointe" pas vraiment sur Form1 et donc ne connaît pas l'attribut myDelegate. Une mauvaise affectation en quelque sorte.

    Merci.

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Vos remarques/explications semble montrer que vous n'êtes pas très à l’aise avec les langages orienté objet à typage statique, comme C++, C++/CLI, JAVA ou encore C#.

    Peut-être avez-vous l'habitude de langage à typage dynamique comme python ou PHP ?

    En tout cas en C++/CLI, le typage est statique, c'est à la compilation que le type des variables est indiqué ou inféré. C'est le compilateur qui doit vérifier la concordance des types et soit faire les conversions implicites soit indiquer les erreurs de typage (erreur "statique").

    Donc c'est normal que le compilateur hurle quand il voit :
    Si mainForm est déclaré avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System::Windows::Forms::Form^ mainForm;
    Le compilateur connait l'ensemble des membre de "System::Windows::Forms::Form" et sait, à la compilation, qu'il ne contient pas de membre "myDelegate" et refuse d'aller plus loin.
    Même si, à l'exécution, l'objet fourni au constructeur de "threadClass" contiendrait ce membre, le compilateur n'en a cure. il fait une vérification statique et Form ne contient pas de "myDelegate".

    Si le champ "mainForm" de la classe 'threadClass" n'est plus une "System::Windows::Forms::Form" mais une "...::Form1", il n'y a plus ce problème car le compilateur c'est qu'un objet de type ""...::Form1" a un membre "myDelegate".
    Le seul hic, c'est que le compilateur vous indiquera que vous ne pouvez pas accéder à un membre privé d'une classe depuis le code d'une autre. Il suffit de déclarer "myDelegate" comme public et non comme private.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                delegate void Ecrire();
    Ceci est une déclaration d'un type de delegate, pas d'un delegate.
    OK ?

    Le type du delegate doit aussi être connu par le code appelant, donc soit vous faite en sorte que la déclaration du type soit public, soit, et je vous le conseil, d'utiliser des types de delegate déjà définis dans le Framework comme la classe Action (http://msdn.microsoft.com/en-us/libr...em.action.aspx).

    Donc le problème fondamental est pourquoi votre code de la classe "threadClass" ne connais pas le type "...::Form1".
    Normalement, un simple sous le #pragme once devrait suffire.

    Attention, par défaut, la classe Form1 est déclaré dans un namespace dont le nom est en lien avec le nom du projet. il faut donc soit utiliser un "using namespace ..." avant la classe threadClass, soit préfixer Form1 par ce namespace lors de la définition du membre de type Form1.

    Ceci est un POC.
    Dans un projet, je pense que votre classe threadClass doit pouvoir être utilisé avec plusieurs types de Form. Cela est facilement réalisable :
    - soit en spécifiant une interface déclarant le delegate et toutes les classes de Form devraient l'implémenter. threadClass pendrait un objet de "type" cette interface.
    - soit en spécifiant une classe dérivant de Form déclarant le delegate et toutes les classes de Form devraient en hériter. threadClass pendrait un objet de "type" cette classe.

  16. #16
    Membre habitué
    Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    289
    Détails du profil
    Informations personnelles :
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Février 2008
    Messages : 289
    Points : 151
    Points
    151
    Par défaut
    Merci pour vos précieuses explications.
    Je viens du C et j'ai une connaissance toute livresque du C++ et de l'objet en général. Je n'aurais peut être pas dû commencer par Visual et son CLI/C++.

    J'ai modifié mon code comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    Fichier threadClass.h:
    ----------------------
    #pragma once
     
    #include "Form1.h"
    using namespace tstThread;
     
    ref class threadClass
    {
        private:
    	Form1^ mainForm;    //mainForm est un handle sur Form1
     
        public:
    	threadClass(Form1^);//le constructeur reçoit un handle sur Form1
    };
     
    Fichier threadClass.cpp:
    ------------------------
    #include "StdAfx.h"
    #include "threadClass.h"
     
    using namespace System::Windows::Forms;
    using namespace System::Threading;
    using namespace tstThread;
     
    threadClass::threadClass(Form1^ f)
    {
        mainForm = f;
    }
     
    Fichier Form1.h:
    ----------------
    #pragma once
     
    #include "threadClass.h"
     
    namespace tstThread {
     
        public ref class Form1 : public System::Windows::Forms::Form
        {
    	..... code de la classe .....
     
    	private: System::Void btnStart_Click(System::Object^ sender, System::EventArgs^ e) {
    	    threadClass^ myThread = gcnew threadClass(this);    //this est un handle sur Form1
    	}
        }
    };
    Du coup, le #include "Form1.h" dans le fichier threadClass.h génère des dizaines de lignes d'erreurs jusque dans le fichier tstThread.cpp et le compilateur me dit que tstThread n'est pas un espace de noms connu.
    Est-ce qu'il n'y aurait pas une sorte de pb d'inclusions multiples? (dont le #pragma once aurait dû nous prémunir).

    Interface et classe Action au menu des vacances de Pâques...

Discussions similaires

  1. Thread et TextBox
    Par archer dans le forum C#
    Réponses: 8
    Dernier message: 20/05/2008, 00h14
  2. Réponses: 5
    Dernier message: 19/05/2008, 10h31
  3. Controler des textBox dans des thread
    Par ramaro dans le forum Windows Forms
    Réponses: 5
    Dernier message: 25/04/2008, 14h21
  4. Textbox et Thread
    Par migutz dans le forum VB.NET
    Réponses: 1
    Dernier message: 22/04/2008, 12h46
  5. [vc++ 2005] acceder a un textbox a partir d un thread
    Par jerome86600 dans le forum VC++ .NET
    Réponses: 9
    Dernier message: 12/07/2006, 13h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo