83 votes

Invoke ou BeginInvoke ne peuvent pas être appelés sur un contrôle tant que le descripteur de fenêtre n'a pas été créé.

J'ai un SafeInvoke de Contrôle de la méthode d'extension similaire à celui Greg d'discute ici (moins le IsHandleCreated vérifier).

Je suis de l'appeler à partir d'un System.Windows.Forms.Form comme suit:

public void Show(string text) {
    label.SafeInvoke(()=>label.Text = text);
    this.Show();
    this.Refresh();
}

Parfois, cet appel peut provenir d'une variété de threads) présente les résultats dans l'erreur suivante:

System.InvalidOperationException s'est produite

Message= "Invoke ou BeginInvoke ne peut pas être appelée sur un contrôle jusqu'à ce que la poignée de la fenêtre a été créé."

Source= "Le système de.De Windows.Les formes"

StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action) 
in C:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16

Ce qui se passe et comment puis-je résoudre ce problème? Je sais que beaucoup comme il n'est pas un problème de création de forme, étant donné que parfois, il fonctionnera une fois et ne parviennent pas, la prochaine fois donc, ce que pourrait être le problème?

PS. Vraiment, je suis vraiment nul en WinForms, personne ne sait une bonne série d'articles qui explique l'ensemble de la maquette et de la façon de travailler avec elle?

78voto

Greg D Points 24218

Il est possible que vous êtes en train de créer vos contrôles sur le mauvais fil. Considérez les points suivants de la documentation à partir de MSDN:

Cela signifie que InvokeRequired peut retourne false si l'Invoquer n'est pas nécessaire (l'appel sur le même thread), ou si le contrôle a été créé sur un thread différent, mais le contrôle de l' la poignée n'a pas encore été créé.

Dans le cas où le contrôle de la poignée de la n'a pas encore été créé, vous devez pas simplement appeler les propriétés, les méthodes,les ou des événements sur le contrôle. Cela pourrait cause le contrôle de la poignée à être créé sur le thread d'arrière-plan, isoler le contrôle sur un fil sans un message de la pompe et de faire le application instable.

Vous pouvez vous protéger contre ce cas, par aussi, en vérifiant la valeur de IsHandleCreated quand InvokeRequired retourne false en cas d'un thread d'arrière-plan. Si la poignée de commande n'a pas encore été créé, vous devez attendre jusqu'à ce qu'il a été créé avant d'appeler les Invoquer ou de BeginInvoke. Généralement, cela arrive seulement si un thread d'arrière-plan est créé dans le constructeur de la forme primaire pour l'application (comme dans Application.Run(new MainForm()), avant que le formulaire a été montré ou Application.Run a été appelé.

Voyons ce que cela signifie pour vous. (Ce serait plus facile à comprendre si l'on a vu votre mise en œuvre de SafeInvoke aussi)

En supposant que votre mise en œuvre est identique à référencé un seul, à l'exception de la contre-vérifier les IsHandleCreated, suivons la logique:

public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
    if (uiElement == null)
    {
        throw new ArgumentNullException("uiElement");
    }

    if (uiElement.InvokeRequired)
    {
        if (forceSynchronous)
        {
            uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
        else
        {
            uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
    }
    else
    {    
        if (uiElement.IsDisposed)
        {
            throw new ObjectDisposedException("Control is already disposed.");
        }

        updater();
    }
}

Considérons le cas où nous appelons SafeInvoke de la non-gui fil pour un contrôle dont la poignée n'a pas été créé.

uiElement n'est pas nulle, donc nous vérifions uiElement.InvokeRequired. Par la MSDN docs (en gras) InvokeRequired sera de retour false , parce que même si il a été créé sur un autre thread, la poignée n'a pas été créé! Ceci nous renvoie à l' else condition où nous vérifions IsDisposed ou immédiatement procéder à l'appel du soumis action... de la thread d'arrière-plan!

À ce stade, tous les paris sont éteints re: qui contrôle parce que la poignée a été créé sur un thread qui n'a pas un message de la pompe pour elle, comme mentionné dans le deuxième paragraphe. C'est peut-être le cas, vous êtes rencontrer?

37voto

Mathieu Points 1762

J'ai trouvé le InvokeRequired pas fiable, alors j'utilise simplement

 if (!this.IsHandleCreated)
{
    this.CreateHandle();
}
 

25voto

Benjol Points 16334

Voici ma réponse à une semblable question:

Je pense (pas encore tout à fait sûr) que c'est parce que InvokeRequired va toujours retourner false si le contrôle a pas encore été chargé indiqué. Je l'ai fait une solution qui semble fonctionner pour le moment, ce qui est simple référence de la poignée de l'associé contrôle de son créateur, comme suit:

var x = this.Handle; 

(Voir http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html)

5voto

Arnshea Points 6270

La méthode dans le post de vous lier à des appels d'Invoquer/BeginInvoke avant de vérifier si le contrôle de la poignée a été créé dans le cas où il est appelé à partir d'un thread qui n'a pas le contrôle.

Ainsi, vous obtiendrez l'exception lorsque votre méthode est appelée à partir d'un thread autre que celui qui a créé le contrôle. Cela peut se produire à partir de l'accès distant d'événements ou de travail en file d'attente de l'utilisateur les éléments...

MODIFIER

Si vous cochez InvokeRequired et HandleCreated avant d'appeler appeler vous ne devriez pas obtenir que l'exception.

3voto

Limited Atonement Points 1446

Si vous allez utiliser un Control partir d'un autre thread avant de montrer ou de faire d'autres choses avec l' Control, envisagez de forcer la création de la poignée dans le constructeur. Ceci est fait en utilisant l' CreateHandle fonction.

Dans un environnement multi-thread du projet, où le "contrôleur" la logique n'est pas dans une WinForm, cette fonction est déterminante dans l' Control constructeurs pour éviter cette erreur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X