web-dev-qa-db-fra.com

InvalidOperationException - l'objet est actuellement utilisé ailleurs - croix rouge

J'ai une application de bureau C # dans laquelle un fil que je crée obtient continuellement une image d'une source (c'est en fait un appareil photo numérique) et la place sur un panneau (panel.Image = img) dans l'interface graphique (qui doit être un autre c'est le code-behind d'un contrôle.

L'application fonctionne mais sur certaines machines, l'erreur suivante apparaît à des intervalles aléatoires (imprévisible)

************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere. 

Ensuite, le panneau se transforme en croix rouge, X rouge. Je pense que ceci est l’icône d’image non valide qui peut être modifiée à partir des propriétés. L'application continue à fonctionner mais le panneau n'est jamais mis à jour.

D'après ce que je peux dire, cette erreur provient de l'événement onpaint du contrôle où je dessine quelque chose d'autre sur la photo.

J'ai essayé d'utiliser une serrure mais pas de chance :(

La manière dont j'appelle la fonction qui place l'image sur le panneau est la suivante:

if (this.ReceivedFrame != null)
{
    Delegate[] clients = this.ReceivedFrame.GetInvocationList();
    foreach (Delegate del in clients)
    {
        try
        {
            del.DynamicInvoke(new object[] { this, 
                new StreamEventArgs(frame)} );
        }
        catch { }
    }
}

c'est le délégué:

public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
    public event ReceivedFrameEventHandler ReceivedFrame;

et voici comment la fonction à l'intérieur du code de contrôle situé derrière s'y enregistre:

Camera.ReceivedFrame += 
    new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);

J'ai aussi essayé

del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });

au lieu de

del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });

mais pas de chance

Est-ce que quelqu'un sait comment je pourrais corriger cette erreur ou au moins la rattraper d'une manière ou d'une autre et faire en sorte que le fil de discussion remette les images sur le panneau?

30
para

En effet, la classe Gdi + Image n'est pas thread-safe. Cependant, vous pouvez éviter InvalidOperationException en utilisant le verrouillage à chaque fois que vous devez accéder à une image, par exemple pour peindre ou obtenir une taille d'image:

Image DummyImage;

// Paint
lock (DummyImage)
    e.Graphics.DrawImage(DummyImage, 10, 10);

// Access Image properties
Size ImageSize;
lock (DummyImage)
    ImageSize = DummyImage.Size;

En passant, l’invocation n’est pas nécessaire si vous utilisez le modèle ci-dessus.

20
arbiter

J'ai eu un problème similaire avec le même message d'erreur mais essayez comme je pourrais, verrouiller le bitmap n'a rien arrangé pour moi. Puis j'ai réalisé que je dessinais une forme à l'aide d'un pinceau statique. Effectivement, c'est le pinceau qui était à l'origine du conflit de thread.

var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
    e.Graphics.FillRectangle(brush, location);

Cela a fonctionné pour mon cas et la leçon apprise: Vérifiez tous les types de référence utilisés au point où la contention de threads se produit.

5
Nick Gotch

Il me semble que le même objet Camera est utilisé plusieurs fois.

Par exemple. essayez d'utiliser un nouveau tampon pour chaque trame reçue. Il me semble que pendant que la zone d'image dessine le nouveau cadre, votre bibliothèque de capture remplit à nouveau ce tampon. Par conséquent, sur des machines plus rapides, cela pourrait ne pas être un problème, avec des machines plus lentes, cela pourrait être un problème.

J'ai programmé quelque chose de similaire une fois, après chaque trame reçue, nous devions demander à recevoir la trame suivante et définir le tampon de réceptionNEWframe dans cette requête.

Si vous ne pouvez pas le faire, copiez d'abord la trame reçue de la caméra dans un nouveau tampon et ajoutez ce tampon à une file d'attente, ou utilisez simplement 2 tampons alternés et vérifiez les dépassements . Utilisez myOutPutPanel.BeginInvoke pour appeler la méthode camera_ReceivedFrame ou, mieux, exécutez un thread qui vérifie la file d'attente. Lorsqu'il a une nouvelle entrée, il appelle mnyOutPutPanel.BeginInvoke pour appeler la méthode afin de définir le nouveau tampon comme image sur le panneau.

De plus, une fois que vous avez reçu le tampon, utilisez la méthode Panel Invoke pour invoquer le paramètre de l'image (assurez-vous qu'elle s'exécute dans le fil de la fenêtre et non dans celui de votre bibliothèque de capture).

L'exemple ci-dessous peut être appelé à partir de n'importe quel thread (bibliothèque de capture ou autre thread séparé):

void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
    if(myOutputPanel.InvokeRequired)
    {
        myOutPutPanel.BeginInvoke( 
            new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame), 
            sender, 
            e);
    }
    else
    {
        myOutPutPanel.Image = e.Image;
    }
}
2
Dennis Kuhn

Je pense que c'est un problème de multithreading. Utilisez la règle d'or Windows et mettez à jour le panneau dans le panneau d'utilisation principal du fil.

0
Ahmed Said