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

Contribuez .NET Discussion :

[C# .NET 2] FolderBrowserDialog en MultiThread [À publier]


Sujet :

Contribuez .NET

  1. #1
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut [C# .NET 2] FolderBrowserDialog en MultiThread


    Aujourd'hui j'ai eu une jolie petite érreur qui m'a légèrement embétté car je n'ai rien trouvé d'intéréssant sur le net ...

    Scénario :
    Dans la conception de mon logiciel j'ai eu à un moment la possibilité de séparer 2 fenêtres WinForm dans 2 threads différents : le premier thread est celui dans lequel s'exécute la form principale de l'application, et le second est crée avec un BeginInvoke sur une methode anonyme ...
    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
     
    public override void ExecuteWizard()
            {
                MethodInvoker async = new MethodInvoker( delegate
                {
                    try
                    {
                        AsyncExecuteWizard();
                    }
                    catch (Exception ex)
                    {
                        DreamShield.IDE.Elements.Dialogs.ExecutorErrorDialog.RaiseErrorDialog(ex);
                    }
                });
                async.BeginInvoke(null, null);
            }
    Jusque là tout va bien, c'est nikel ce que je voulais, mais dans cette nouvelle fenêtre, crée à partir de AsyncExecuteWizard j'ai voulu afficher un FolderBrowserDialog, et c'est là que j'ai eu ma suprise : il n'y avais dans le FolderBrowserDialog que les bouttons, mais pas le TreeView perméttant d'accéder aux dossiers. Pourtant mon application était bien en STAThread ...

    Astuces
    La seule astuce que j'ai trouvé pour palier à ce problème et d'avoir dans AsyncExecuteWizard une référence à la form principale de l'application. Ensuite à partir de cette form l'on fait un mainForm.Invoke(fonction_appellant_le_FolderBrowserDialog);.
    Seulement, si l'on appelle simplement le Invoke, le thread ( le 2e, pas celui de la form principale ) est bloqué et le paint de la 2e form n'a plus lieu, il faut donc faire encore un BeginInvoke ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    mainForm.BeginInvoke(new MethodInvoker(delegate
    {
       if(folderBrowserDialog.ShowDialog(_la_2e_form) == DialogResult.OK)
       {
       }
    }));
    et là, le FolderBrowserDialog fonctionne correctement...

    Je ne sais pas si c'est la solution la plus simple, mais n'ayant pas trouvé de réele solution sur le net, j'espère qu'elle pourra un jour aider quelqu'un

    NB: pour ceux qui voudrait savoir ce "bug" est du à COM en STAThread ... le FolderBrowserDialog ne peut pas créer d'instance des interfaces du Shell de Windows sur un autre Thread que le premier ... je n'ai pas voulu éssayer avec MTAThread car toutes les pages que j'ai trouvé sur l'explication de l'utilité de l'attribut du Thread préconisent l'utlisation de STAThread dès que l'on fait du WinForms

    Edit: Même situation avec un OpenFileDialog ...

  2. #2
    Rédacteur
    Avatar de Giovanny Temgoua
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    3 830
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 3 830
    Par défaut
    Ce serait une bonne entrée pour la FAQ je pense

    Je pense qu'un petit zip avec un code test serait

  3. #3
    Rédacteur
    Avatar de dev01
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    2 451
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 451
    Par défaut
    Salut .

    Bon je suis un peu tard mais je pense que cette proposition serait mieux sous forme de source.
    Pour ma part je pense que c'est trop long pour être dans la FAQ

  4. #4
    Rédacteur
    Avatar de Giovanny Temgoua
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    3 830
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 3 830
    Par défaut
    Citation Envoyé par dev01
    Salut .

    Bon je suis un peu tard mais je pense que cette proposition serait mieux sous forme de source.
    Pour ma part je pense que c'est trop long pour être dans la FAQ
    Heu ... En effet.
    Je pensais à poster l'explication dans la FAQ, poster le source dans les sources et référencer l'entrée dans les sources dans la QR de la FAQ

  5. #5
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    oops, j'avais oublié, je vais faire l'exemple

  6. #6
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    Voilà ...
    Dans l'exemple j'ai utilisé Thread pour démarrer la 2e fenêtre. Il y a plusieurs petits détails cependant quand à l'utilisation de BeginInvoke :

    si l'on fait un code du genre pour lancer la 2e fenêtre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    this.BeginInvoke(...);
    Il n'y a pas le bug de FolderBrowserDialog, mais en réalité les deux fenêtres ne sont pas séparés comme pour les Thread et donc un blocage dans Form2 bloquerai Form1 par contre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    MethodInvoker method = new MethodInvoker(...);
    method.BeginInvoke(...);
    La les deux fenêtres sont bien séparés dans deux thread différents et le bug survient ...

    La raison est que la bibliotèque OLE/COM du Shell de Windows ne supporte pas les applications multi-thread. La source de cette information et tout simplement le méssage d'érreur que l'on obtient lors du bug en déboggant avec Visual Studio l'application :
    Le thread actuel doit être en mode STA (Single Thread Apartment) avant que des appels OLE puissent être effectués. Assurez-vous que votre fonction Main est marquée comme STAThreadAttribute. Cette exception n'est déclenchée que si un débogueur est attaché au processus.
    Fichiers attachés Fichiers attachés

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    547
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 547
    Par défaut
    Salut,

    en fait, il y a moyen plus simple de faire ca. Je me suis arraché les cheveux pour un probleme equivalent concernant la recuperation d'un dataobject du presse-papier sur un thread lancé par un BeginInvoke. En fait, quand tu avais le bug sur ton premier post, c'etait du au fait que les threads du ThreadPool (qui sont utilisé quand tu BeginInvoke un delegate) sont forcement en MTA, et donc des que tu touches le clipboard (ou un dialog comme celui dont il est question), ca ne marche pas. Maintenant que tu utilises un thread dedié pour chaque form, il existe une solution tres simple (je reprends le callback du bouton de ton exemple qui lancé le thread qui allait planté) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
            private void button1_Click(object sender, EventArgs e)
            {
                Thread thread_bug = new Thread(new ThreadStart(
                delegate
                {
                    Form2 f2 = new Form2();
                    f2.ShowDialog();
                }));
                thread_bug.SetApartmentState(ApartmentState.STA);  /*<=*/
                thread_bug.Start();
            }
    Et la ca marche le thread courant de la Form2 etant en STA. C'est exactement le meme principe que quand tu marques ton Main avec l'attribut [STAThread], etant donné que tous les threads sont initialisés en MTA par defaut par le framework. Nota : Ne pas utiliser la propriété ApartmentState de Thread qui est deprecié mais les GetApartmentState et SetApartmentState(ApartmentState) (apparement en 1.1, on pouvait changer l'apartment apres le depart, mais ca posait des problemes, donc en 2.0, c'est interdit), et le faire avant le lancement effectif du thread sinon ca plantera.

    Donc pour les probleme d'Apartment, soit tu utilises un thread dedié paramétré comme il faut, soit tu te fais un ThreadPool perso qui travaillera en STA (pratique quand tu as besoin d'un threadpool pour travailler en WinForms), mais avec le Pool de base du framework, tu auras des erreurs.

    En esperant que ca aide.

  8. #8
    Expert confirmé
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Par défaut
    Wow, j'avais jamais cherché du coté des propriétés des Thread car j'ai toujours trouvé plus simple de faire direct BeginInvoke : la syncronisation est rapide à implémenter, pas besoin de construire des objets, un simple "delegate" suffit. Je vais éssayer tout ça ... si je retombe sur le même problème


  9. #9
    Membre Expert Avatar de sisqo60
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Février 2006
    Messages
    754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 754
    Par défaut OpenFileDialog en multi-thread
    Merci beaucoup les gars, votre discussion m'a permis de ne pas perdre plus de temps...

    Mais je ne comprends pas pourquoi cela ne fonctionne pas avec un simple [MTAThread]...

    Je m'explique :

    J'ai réalisé un projet avec VS.net 2003 avec des formulaire où j'ai utilisé le multiple Thread (Jusque là c'est pas sorcier, tout fonctionnait nickel...), mais récemment j'ai fait évoluer le projet en VS.net 2008 et là bug sur bug .
    C'est normal puisque ils utilisent des versions du framework différents mais c'est là que que le déboggeur m'affichait une erreur me demandant de mettre STA pour le main() (quand j'appelait simplement un OpenFileDialog) , ce que j'ai fait mais par la suite j'avais d'autres problèmes au niveau de la mémoire(beaucoup de traitement lourds pour certains ...

    Moralité après quelques recherches sur internet je suis tombé ici et j'ai essayé et cela fonctionnait... si quelqu'un peux m'expliquer pourquoi cela fonctionne sous VS 2003 mais pas sur VS 2008? merci par avance...

  10. #10
    Rédacteur
    Avatar de dev01
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    2 451
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 451
    Par défaut
    Citation Envoyé par sisqo60 Voir le message
    si quelqu'un peux m'expliquer pourquoi cela fonctionne sous VS 2003 mais pas sur VS 2008? merci par avance...
    Tout simplement parce que comme tu le dit les versions du framework ont changé.
    En version 1.1 l'accès multi-thread aux composants graphique était "toléré". A partir du framework 2.0 la politique a changé et les accès multi-thread aux compos déclenche systématiquement des exceptions.

  11. #11
    Membre Expert Avatar de sisqo60
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Février 2006
    Messages
    754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Indre et Loire (Centre)

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 754
    Par défaut réponse
    salut

    avec un peu de recherches, j'ai lu que dans .net framework 1.1 oui la communication inter-threads était comme tu dis toléré, mais maintenant avec les versions 2.0 et supérieures, par défaut la communication inter-threads a été bloquée pour une sécurité accrue des données des formulaires etc...
    principalement pour lutter contre le piratage (virus...).

    Mais pour ma part ce que vous avez posté est très bien pour une toute petite application, mais ce n'est pas suffisant pour un gros projet. Je m'explique :

    Il m'est impensable de reprendre tout pour invoquer des threads séparés pour que tout puisse foncionner... c'est trop long en temps...

    Par contre j'ai lu que le bout de code suivant pouvait permettre de faire en sorte que le code fonctionne comme sous vs.net 2003 sous vs.net 2008, problème c'est que je n'arrive pas à savoir où et comment l'implémenter...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Control.CheckForIllegalCrossThreadCalls = false;
    j'ai essayé un peu partout et cela n'a pas donné les résultats attendus, si quelqu'un avait une idée ce serait sympa...

    merci

  12. #12
    Rédacteur
    Avatar de dev01
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    2 451
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 451
    Par défaut
    Citation Envoyé par sisqo60 Voir le message
    Par contre j'ai lu que le bout de code suivant pouvait permettre de faire en sorte que le code fonctionne comme sous vs.net 2003 sous vs.net 2008, problème c'est que je n'arrive pas à savoir où et comment l'implémenter...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Control.CheckForIllegalCrossThreadCalls = false;
    j'ai essayé un peu partout et cela n'a pas donné les résultats attendus, si quelqu'un avait une idée ce serait sympa...

    merci
    Ce bout de code peut surtout faire planter ton UI et cela sans explication. Pour reprendre une image que j'ai déjà utilisé à ce propos sur le forum, c'est comme tuer le messager pour ne pas avoir de mauvaises nouvelles....

    Bref ce "bout de code" est une horreur/erreur/infamie et il ne devrait jamais exister.

  13. #13
    Membre confirmé Avatar de Vonziz
    Inscrit en
    Décembre 2006
    Messages
    215
    Détails du profil
    Informations forums :
    Inscription : Décembre 2006
    Messages : 215
    Par défaut
    Un grand merci aux acteurs de ce post qui m'ont permis de débugger rapidement mon application en multi-thread 2.0.

    Ce sujet mériterait d'être dans la FAQ (si ce n'est pas déjà fait) car l'instanciation de nouveau thread entre Form est relativement courant dans une application de type professionnel.

    Merci encore!

Discussions similaires

  1. [.Net 2] Multithreading
    Par Invité dans le forum Windows Forms
    Réponses: 12
    Dernier message: 15/04/2008, 01h06
  2. Réponses: 2
    Dernier message: 11/05/2007, 23h27
  3. [VB.NET 2.0] Serveur en MultiThreading
    Par Aspic dans le forum Windows Forms
    Réponses: 1
    Dernier message: 15/03/2007, 12h46
  4. Réponses: 2
    Dernier message: 24/03/2006, 10h18
  5. [VC6.0][.NET]MultiThread et MultiProcesseur ?
    Par matazz dans le forum MFC
    Réponses: 3
    Dernier message: 19/09/2005, 10h50

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