Автоматизация Visual Studio с помощью EnvDTE - PullRequest
30 голосов
/ 26 марта 2010

Я успешно инстанцирую / автоматизирую Visual Studio, используя следующий код:

System.Type t = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
object obj = Activator.CreateInstance(t, true);
dte = (DTE)obj;
Solution sln = dte.Solution;
sln.Open(SolutionFile);
System.Threading.Thread.Sleep(1000);
//Do stuff with the solution

Обратите внимание на звонок Thread.Sleep(1000)? Если я не включу это, код попытается вызвать ошибку в экземпляре, прежде чем он будет готов, и я получу исключение:

the message filter indicated that the application is busy.

Вместо того, чтобы ждать ровно n секунд, есть ли способ опроса этого объекта на готовность?

Ответы [ 3 ]

32 голосов
/ 26 марта 2010

В качестве решения этой проблемы вы можете зарегистрироваться на событие, уведомляющее о завершении загрузки решения.

Это образец класса, который позволяет прослушивать события при загрузке решения:

public class SolutionEventsListener : IVsSolutionEvents, IDisposable
{
    private IVsSolution solution;
    private uint solutionEventsCookie;

    public event Action AfterSolutionLoaded;
    public event Action BeforeSolutionClosed;

    public SolutionEventsListener(IServiceProvider serviceProvider)
    {
        InitNullEvents();

        solution = serviceProvider.GetService(typeof (SVsSolution)) as IVsSolution;
        if (solution != null)
        {
            solution.AdviseSolutionEvents(this, out solutionEventsCookie);
        }
    }

    private void InitNullEvents()
    {
        AfterSolutionLoaded += () => { };
        BeforeSolutionClosed += () => { };
    }

    #region IVsSolutionEvents Members

    int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)
    {
        AfterSolutionLoaded();
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)
    {
        BeforeSolutionClosed();
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
    {
        return VSConstants.S_OK;
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        if (solution != null && solutionEventsCookie != 0)
        {
            GC.SuppressFinalize(this);
            solution.UnadviseSolutionEvents(solutionEventsCookie);
            AfterSolutionLoaded = null;
            BeforeSolutionClosed = null;
            solutionEventsCookie = 0;
            solution = null;
        }
    }

    #endregion
}

Пример использования:

DTE2 applicationObject = dte;
var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider);
solutionEventsListener = new SolutionEventsListener(serviceProvider);
solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ;
8 голосов
/ 22 июля 2015

Хотя решения здесь креативны, они либо не решат проблему полностью, либо очень громоздки в использовании. Вам нужно просто зарегистрировать фильтр сообщений, поскольку Microsoft рекомендует .

Код, скопированный здесь для удобства (замените VisualStudio.DTE.10.0 любой версией VS, которую вы хотите открыть), просто обратите внимание, чтобы украсить метод Main атрибутом STAThread, фильтрация сообщений не будет работать без него пропущено в оригинальном решении MSDN.

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
    {
        [STAThread]
        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; 
            int hr = CoRegisterMessageFilter(newFilter, out oldFilter);
            if (hr != 0)
              Marshal.ThrowExceptionForHR(hr);
        }

        // 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);
    }
}
0 голосов
/ 19 декабря 2011

Мне не очень повезло с техникой IVSSolutionEvents (хотя я не пробовал код точно так, как описано выше). Вместо этого я создал небольшую функцию, чтобы помочь мне повторить вызов. Я знаю, что это не особенно красиво, но это просто позвонить, и это работает!

Вот ссылка на мой ответ на другой, похожий вопрос: https://stackoverflow.com/a/8565990/1106459

(Это также помогает при ошибках «сервер занят» при вызове других функций EnvDTE, а также при открытии и закрытии решения.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...