Comment : résoudre des erreurs « L'application est occupée » et « L'appel a été rejeté par l'appelé »

Les compléments Visual Studio sont déconseillés dans Visual Studio 2013. Vous devriez mettre vos compléments à niveau vers des extensions VSPackage. Pour plus d'informations sur les mises à jour, consultez FAQ : conversion de compléments en extensions VSPackage.

Si vous appelez par programmation l'automation de Visual Studio depuis une application externe (hors processus) multithread, vous pouvez recevoir les erreurs suivantes :

  • L'application est occupée (RPC_E_CALL_REJECTED 0x80010001)

  • L'appel a été rejeté par l'appelé (0x8001010A RPC_E_SERVERCALL_RETRYLATER)

Ces erreurs se produisent en raison de conflits de threading entre les applications multithread externes et Visual Studio. Vous pouvez les éliminer en implémentant des gestionnaires d'erreurs IOleMessageFilter dans votre application d'automation Visual Studio. (Ne pas confondre IOleMessageFilter et System.Windows.Forms.IMessageFilter)

Pour résoudre les erreurs

  1. Ajoutez la classe suivante à votre application.

  2. Ajoutez une référence COM à "Microsoft Development Environment 8.0." Cela ajoute à votre solution des références à EnvDTE et à EnvDTE80.

    Ajoute une référence à System.Windows.Forms.

  3. Dans le code, créez une instance de EnvDTE80, comme décrit dans l'exemple suivant.

  4. Appelez Message.Register pour gérer les erreurs de thread.

  5. Appelez votre code d'automation comme d'habitude.

  6. Lorsque votre code d'automation est terminé, appelez Message.Revoke pour supprimer les gestionnaires d'erreurs de thread.

Exemple

using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            EnvDTE80.DTE2 dte;
            object obj = null;
            System.Type t = null;

            // Get the ProgID for DTE 8.0.
            t = System.Type.GetTypeFromProgID("VisualStudio.DTE.10.0",
              true);
            // Create a new instance of the IDE.
            obj = System.Activator.CreateInstance(t, true);
            // Cast the instance to DTE2 and assign to variable dte.
            dte = (EnvDTE80.DTE2)obj;

            // Register the IOleMessageFilter to handle any threading 
            // errors.
            MessageFilter.Register();
            // Display the Visual Studio IDE.
            dte.MainWindow.Activate();

            // =====================================
            // ==Insert your automation code here.==
            // =====================================
            // For example, get a reference to the solution2 object
            // and do what you like with it.
            Solution2 soln = (Solution2)dte.Solution;
            System.Windows.Forms.MessageBox.Show
              ("Solution count: " + soln.Count);
            // =====================================
            
            // All done, so shut down the IDE...
            dte.Quit();
            // and turn off the IOleMessageFilter.
            MessageFilter.Revoke();
            
        }
    }

    public class MessageFilter : IOleMessageFilter
    {
        //
        // Class containing the IOleMessageFilter
        // thread error-handling functions.

        // Start the filter.
        public static void Register()
        {
            IOleMessageFilter newFilter = new MessageFilter(); 
            IOleMessageFilter oldFilter = null; 
            CoRegisterMessageFilter(newFilter, out oldFilter);
        }

        // Done with the filter, close it.
        public static void Revoke()
        {
            IOleMessageFilter oldFilter = null; 
            CoRegisterMessageFilter(null, out oldFilter);
        }

        //
        // IOleMessageFilter functions.
        // Handle incoming thread requests.
        int IOleMessageFilter.HandleInComingCall(int dwCallType, 
          System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr 
          lpInterfaceInfo) 
        {
            //Return the flag SERVERCALL_ISHANDLED.
            return 0;
        }

        // Thread call was rejected, so try again.
        int IOleMessageFilter.RetryRejectedCall(System.IntPtr 
          hTaskCallee, int dwTickCount, int dwRejectType)
        {
            if (dwRejectType == 2)
            // flag = SERVERCALL_RETRYLATER.
            {
                // Retry the thread call immediately if return >=0 & 
                // <100.
                return 99;
            }
            // Too busy; cancel call.
            return -1;
        }

        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, 
          int dwTickCount, int dwPendingType)
        {
            //Return the flag PENDINGMSG_WAITDEFPROCESS.
            return 2; 
        }

        // Implement the IOleMessageFilter interface.
        [DllImport("Ole32.dll")]
        private static extern int 
          CoRegisterMessageFilter(IOleMessageFilter newFilter, out 
          IOleMessageFilter oldFilter);
    }

    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), 
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    interface IOleMessageFilter 
    {
        [PreserveSig]
        int HandleInComingCall( 
            int dwCallType, 
            IntPtr hTaskCaller, 
            int dwTickCount, 
            IntPtr lpInterfaceInfo);

        [PreserveSig]
        int RetryRejectedCall( 
            IntPtr hTaskCallee, 
            int dwTickCount,
            int dwRejectType);

        [PreserveSig]
        int MessagePending( 
            IntPtr hTaskCallee, 
            int dwTickCount,
            int dwPendingType);
    }
}

Programmation fiable

Lorsque votre application externe multithread appelle Visual Studio, elle passe par une interface COM. L'interface COM rencontre parfois des problèmes de gestion des threads, notamment en ce qui concerne le minutage. En conséquence, il arrive que le thread entrant en provenance de l'application externe ne puisse pas être géré par Visual Studio dès son arrivée, ce qui provoque les erreurs susmentionnées. Toutefois, cela ne se produit pas si vous appelez d'une application qui s'exécute à l'intérieur de Visual Studio (in-process), telle qu'une macro ou un complément. Pour obtenir une explication détaillée sur les raisons, consultez Prise en charge des threads dans Office.

Pour éviter ces erreurs, implémentez une fonction gestionnaire IOleMessageFilter dans votre application. Dans ce cas, si le thread de votre application externe appelle Visual Studio et qu'il est rejeté (c'est-à-dire qu'il retourne SERVERCALL_RETRYLATER à partir de la méthode IOleMessageFilter.HandleIncomingCall), votre application peut gérer le problème et réessayer ou annuler l'appel. Pour cela, initialisez le nouveau thread à partir de votre application Visual Studio dans un thread cloisonné (STA) et entourez votre code d'automation avec le gestionnaire IOleMessageFilter.

Voir aussi

Tâches

Procédure pas à pas : débogage d'un projet Complément