visual-studio 使用 EnvDTE 自动化 Visual Studio

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2525457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 11:42:32  来源:igfitidea点击:

Automating Visual Studio with EnvDTE

c#visual-studioautomation

提问by Dave Swersky

I am successfully instantiating/automating Visual Studio using the following code:

我使用以下代码成功实例化/自动化 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

Notice the Thread.Sleep(1000)call? If I don't include that, the code tries to bug the instance before it's ready and I get an exception:

注意到Thread.Sleep(1000)电话了吗?如果我不包括它,代码会尝试在实例准备好之前对其进行调试,并且我会收到一个异常:

the message filter indicated that the application is busy.

Rather than wait exactly n seconds, is there a way to poll this object for readiness?

有没有办询这个对象以准备就绪,而不是正好等待 n 秒?

回答by Elisha

As a solution to this issue you can register to an event that notifies when the solution load is done.

作为此问题的解决方案,您可以注册一个事件,该事件在解决方案加载完成时发出通知。

This is a sample of class that lets you listen to events on solution loading:

这是一个类示例,可让您侦听解决方案加载事件:

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
}

Usage example:

用法示例:

DTE2 applicationObject = dte;
var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider);
solutionEventsListener = new SolutionEventsListener(serviceProvider);
solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ;

回答by poizan42

While the solutions here are creative they either won't completely fix the problem or are very cumbersome to use. You should just register a message filter as Microsoft recommends.

虽然这里的解决方案很有创意,但它们要么不能完全解决问题,要么使用起来非常麻烦。您应该只按照 Microsoft 的建议注册一个消息过滤器

Code copied here for convenience (replace VisualStudio.DTE.10.0with whatever version of VS you want to open), just pay attention to decorate the Mainmethod with STAThreadattribute, message filtering won't work without it and it is skipped in original MSDN solution.

代码复制到这里方便(用VisualStudio.DTE.10.0你想打开的任何版本的VS替换),只要注意MainSTAThread属性装饰方法,没有它就无法进行消息过滤,原始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);
    }
}

回答by Richard Shepherd

I haven't had much luck with the IVSSolutionEvents technique (though I didn't try the code exactly as above). Instead, I created a small function to help me retry the call. I know that isn't particularly beautiful, but it is simple to call and it works!

我对 IVSSolutionEvents 技术不太走运(虽然我没有完全像上面那样尝试代码)。相反,我创建了一个小函数来帮助我重试调用。我知道这不是特别漂亮,但调用起来很简单,而且很管用!

Here's a link to my answer to another, similar question: https://stackoverflow.com/a/8565990/1106459

这是我对另一个类似问题的回答的链接:https: //stackoverflow.com/a/8565990/1106459

(It also helps with 'server busy' errors when calling other EnvDTE functions as well as opening and closing the solution.)

(在调用其他 EnvDTE 函数以及打开和关闭解决方案时,它还有助于解决“服务器繁忙”错误。)