web-dev-qa-db-fra.com

Pomper des messages Windows pendant une longue opération?

Je reçois le message suivant sur une grosse opération que je lance:

Le CLR n'a pas pu passer du contexte COM 0x1fe458 au contexte COM 0x1fe5c8 pendant 60 secondes. Le thread qui possède le contexte/appartement de destination est probablement en train de faire une attente sans pompage ou de traiter une opération très longue sans pomper les messages Windows. Cette situation a généralement un impact négatif sur les performances et peut même entraîner une non-réactivité de l'application ou une utilisation de la mémoire de plus en plus longue. Pour éviter ce problème, tous les threads STA (Single Threaded Apartment) doivent utiliser des primitives d'attente de pompage (telles que CoWaitForMultipleHandles) et pomper régulièrement les messages lors d'opérations de longue durée.

Comment puis-je envoyer des messages Windows pour que cette erreur ne se produise plus lors de longues opérations?

21
sooprise

Le contexte n'est pas clairement défini - effectuez-vous une tâche de longue durée sur le thread d'interface utilisateur d'une application WinForms ou WPF? Si c'est le cas, ne le faites pas - utilisez BackgroundWorker ou exécutez directement la tâche sur le pool de threads ou sur un nouveau thread (éventuellement avec Control.Invoke/BeginInvoke ou Dispatcher si vous devez mettre à jour l'interface utilisateur). Si votre grande opération utilise le composant COM qui se plaint, ce sera plus difficile ...

18
Jon Skeet

Comme je sais, cela ne se produit qu'avec le débogueur attaché. Vous n'obtiendrez jamais cette exception en production.

6
Tony Kh

J'ai tendance à utiliser Application.DoEvents dans ces scénarios. Je ne sais pas si cela fonctionnera dans votre situation si. Il nécessite une référence à System.Windows.Forms mais fonctionnera également dans les applications console.

Sinon, vous pouvez essayer de multi-threading vos applications.

3
Matthew Steeples

Si cela se produit dans un débogueur, cela peut être dû au MDA ContextSwitchDeadlock, que vous pouvez désactiver (utilisez la fenêtre Exceptions de Visual Studio). Cependant, cela indique un problème plus important: vous ne devez pas effectuer d'opérations de longue durée sur votre thread d'interface utilisateur.

3
Sasha Goldshtein

La méthode win32 traditionnelle est:

void PumpMessages()
{
    MSG msg;
    for( ;; ) {
        if( !PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) {
            return;
        }
        if( msg.message == WM_QUIT ) {
            s_stopped = true;
            return;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
}

Mais je suppose que vous utilisez .NET.

1
user590812

Vous devez traiter votre longue opération sur un thread séparé pour éviter de geler l'interface utilisateur. Cela résoudra également le problème ci-dessus

0
WraithNath

Je sais que cela a été demandé il y a des années, mais j'espère que cela aidera les autres à venir. Si vous ne voulez pas vous inquiéter de travailler avec un arrière-plan ou de transmettre des messages, une solution simple consiste à mettre à jour quelque chose sur l'interface utilisateur. Par exemple, j'ai un outil que j'utilise seulement, alors je me fiche de savoir s'il utilise le thread d'interface utilisateur. Donc, je mets simplement à jour un textbox.text sur l'interface utilisateur pour tout ce sur quoi je travaille. voici un extrait du code. C’est une façon très astucieuse, et probablement incorrecte, de le faire de manière professionnelle, mais cela fonctionne. 

for (int i = 0; i < txtbxURL.LineCount; i++)
{
    mytest.NavigateTo(mytest.strURL);
    mytest.SetupWebDoc(mytest.strURL);
    strOutput = mytest.PullOutPutText(mytest.strURL);
    strOutput = mytest.Tests(strOutput);
    mytest.CheckAlt(mytest.strURL);
    mytest.WriteError(txtbxWriteFile.Text);
    txtblCurrentURL.Text = mytest.strURL;
    //This ^^^ is what is being updated, which keeps the thread from throwing the exception noted above.
}
0
Moose