Procedura: correggere gli errori di "Applicazione impegnata" e "Chiamata respinta dal chiamato"

I componenti aggiuntivi di Visual Studio sono deprecati in Visual Studio 2013. Si consiglia di effettuare l'aggiornamento dei componenti aggiuntivi alle estensioni di VSPackage. Per ulteriori informazioni sull'aggiornamento, consultare Domande frequenti: conversione di componenti aggiuntivi in VSPackage Extensions.

Se si esegue una chiamata a livello di codice nell'automazione di Visual Studio da un'applicazione multithread esterna (out-of-process), possono verificarsi gli errori seguenti:

  • Applicazione impegnata (RPC_E_CALL_REJECTED 0x80010001)

  • Chiamata respinta dal chiamato (RPC_E_SERVERCALL_RETRYLATER 0x8001010A)

Si tratta di errori dovuti a problemi di conflitto di threading tra applicazioni multithread esterne e Visual Studio. È possibile risolvere questi problemi implementando i gestori di errori IOleMessageFilter nell'applicazione di automazione di Visual Studio. Non si confonda IOleMessageFilter con System.Windows.Forms.IMessageFilter.

Per correggere gli errori

  1. Aggiungere la classe seguente all'applicazione.

  2. Aggiungere un riferimento COM a "Microsoft Development Environment 8.0". In questo modo vengono aggiunti alla soluzione riferimenti a EnvDTE e a EnvDTE80.

    Aggiungere un riferimento a System.Windows.Forms.

  3. All'interno del codice, creare un'istanza di EnvDTE80, come indicato nell'esempio riportato di seguito.

  4. Chiamare Message.Register per la gestione degli errori di thread.

  5. Chiamare il codice di automazione, come di consueto.

  6. Al termine del codice di automazione, chiamare Message.Revoke per rimuovere i gestori di errori del thread.

Esempio

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);
    }
}

Programmazione efficiente

Le chiamate dell'applicazione multithread esterna in Visual Studio passano attraverso un'interfaccia COM. È possibile che talvolta si verifichino problemi nella gestione corretta dei thread da parte dell'interfaccia COM, in particolare per quanto riguarda i tempi. Di conseguenza, può capitare che il thread in entrata proveniente dall'applicazione esterna non possa essere gestito da Visual Studio nel momento preciso in cui arriva, provocando gli errori indicati sopra. Se tuttavia si effettua la chiamata da un'applicazione in esecuzione in Visual Studio (in-proc), ad esempio una macro o un componente aggiuntivo, questi errori non si verificano. Per una spiegazione più dettagliata dei motivi alla base di questi errori, vedere Supporto del threading in Office.

Per evitare tali errori, implementare una funzione del gestore IOleMessageFilter nell'applicazione. Se si esegue questa implementazione, quando il thread dell'applicazione esterna esegue la chiamata in Visual Studio e viene rifiutato (ovvero restituisce SERVERCALL_RETRYLATER dal metodo IOleMessageFilter.HandleIncomingCall), l'applicazione può gestire l’operazione riprovando a eseguire la chiamata oppure ad annullarla. A questo scopo, avviare il nuovo thread dall'applicazione Visual Studio in un apartment a thread singolo (STA) e delimitare il codice di automazione con il gestore IOleMessageFilter.

Vedere anche

Attività

Procedura dettagliata: debug di un progetto di componente aggiuntivo