在Visual Studio IDE外部获取EnvDTE.DTE实例



>我正在Visual Studio 2013中创建项目自动化工具,其中我有自己的项目模板,并尝试以编程方式将其添加到现有解决方案中。我在控制台应用程序中使用以下代码。

EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.12.0");
string solDir = dte.Solution.FullName;
solDir=solDir.Substring(0, solDir.LastIndexOf("\"));
dte.Solution.AddFromTemplate(path, solDir+"\TestProj", "TestProj", false);

当我从Visual Studio IDE运行应用程序时,它正在工作。但是当我尝试从命令提示符运行 exe 时,出现以下异常。

Unhandled Exception: System.Runtime.InteropServices.COMException: Operation unav
ailable (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
at System.Runtime.InteropServices.Marshal.GetActiveObject(Guid& rclsid, IntPtr reserved, Object&   ppunk)
at System.Runtime.InteropServices.Marshal.GetActiveObject(String progID)
at ProjectAutomation.Console.Program.Main(String[] args) 

我想知道是否有任何方法可以在Visual Studio IDE之外获取活动的EnvDTE.DTE实例。

从外部工具自动执行现有 Visual Studio 实例以修改加载的解决方案是一个坏主意。如果您使用 GetActiveObject(...( 并且启动了两个 Visual Studio 实例,您如何知道返回了正确的实例?如果用户或Visual Studio在用户启动外部工具时对解决方案执行某些操作,该怎么办?有两种更好的方法:

1( 使用外部工具自动执行新的 Visual Studio 实例,加载所需的解决方案并对其进行修改。即使 VS 实例不可见,也可以执行此操作。要创建新实例,正确的代码是:

System.Type type = Type.GetTypeFromProgID("VisualStudio.DTE.12.0");
EnvDTE.DTE dte = (EnvDTE.DTE) System.Activator.CreateInstance(type);
dte.MainWindow.Visible = true;
...

2( 使用 Visual Studio 扩展(如宏(VS 2010 或更低版本(、加载项(VS 2013 或更低版本(或包(任何 VS 版本(来提供菜单项或按钮工具栏,单击该菜单项或按钮工具栏时,将修改当前加载的解决方案。这可以防止"繁忙"方案,因为如果 VS 繁忙,则无法单击菜单项或工具栏按钮(除非"繁忙"操作是异步的(。

我在这里找到了GetActiveObject的替代方案,Kiril解释了如何枚举ROT。MSDN 上还有其他示例。

由于一些SO用户不喜欢链接,因此以下是详细信息:

  • 枚举名为 devenv.exe 的所有进程。
  • 显示主窗口标题的列表。(我把"Microsoft Visual Studio"去掉了(
  • 询问用户他们要使用哪一个。
  • 使用该过程。id在ROT中查找对象,我认为这是OP的问题。这是通过使用IEnumMoniker.Next((枚举ROT来完成的,它返回名字对象和进程ID(在VS的情况下(。

  • 找到了绰号。将正在运行的对象强制转换为 DTE,然后就可以了。

COM,ROT和绰号对我来说听起来太复杂了,所以我很高兴看到上面的链接已经完成了繁重的工作。

我在几分钟内就完成了这个示例。它在我第一次使用调试器时起作用。但是在全速行驶时,我需要添加一些睡眠或重试,因为很容易从 HRESULT 中得到异常:0x8001010A (RPC_E_SERVERCALL_RETRYLATER((

此外,我用容忍其他版本的 VS 的正则表达式替换了完全匹配:

Regex monikerRegex = new Regex(@"!VisualStudio.DTE.d+.d+:" + processId, RegexOptions.IgnoreCase);

VS 可能繁忙、打开对话框或编译许多项目的问题对于您可能尝试强制馈送击键或 COM 请求的许多应用程序都很常见。如果收到错误,请重试几秒钟。最后,如果需要,会弹出一个消息框。

一些SO用户不喜欢链接,因为链接会损坏;)

我花了一个多小时来写我的版本,所以不妨在这里发布。需要对envdteenvte80的引用(从添加引用/程序集/扩展(。提供静态方法,用于在 Visual Studio 或 Notepad++ 中打开 C# 文件作为备份,并选择性地导航到特定行。

using System;
using System.Diagnostics;
using EnvDTE80;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
namespace whatever
{
    public static class CsFile
    {
        public static void Open(string fileName, int? lineNr = null)
        {
            try
            {
                OpenFileInVisualStudio(fileName, lineNr);
            }
            catch
            {
                try
                {
                    OpenFileInNotePadPlusPlus(fileName, lineNr);
                }
                catch
                {
                    // Woe is me for all has failed. Somehow show an error.
                }
            }
        }
        public static void OpenFileInVisualStudio(string fileName, int? lineNr = null)
        {
            DTE2 dte = null;
            TryFor(1000, () => dte = GetDteByName("VisualStudio.DTE"));
            if (dte == null) throw new Exception("Visual Studio not running?");
            dte.MainWindow.Activate();
            TryFor(1000, () => dte.ItemOperations.OpenFile(fileName));
            if (lineNr.HasValue) TryFor(1000, () => ((EnvDTE.TextSelection)dte.ActiveDocument.Selection).GotoLine(lineNr.Value, true));
        }
        public static void OpenFileInNotePadPlusPlus(string fileName, int? lineNr = null)
        {
            if (lineNr.HasValue) fileName += " -n" + lineNr.Value.ToString();
            Process.Start(@"C:Program Files (x86)Notepad++notepad++.exe", fileName);
        }
        private static void TryFor(int ms, Action action)
        {
            DateTime timeout = DateTime.Now.AddMilliseconds(ms);
            bool success = false;
            do
            {
                try
                {
                    action();
                    success = true;
                }
                catch (Exception ex)
                {
                    if (DateTime.Now > timeout) throw ex;
                }
            } while (!success);
        }
        static DTE2 GetDteByName(string name)
        {
            IntPtr numFetched = Marshal.AllocHGlobal(sizeof(int));
            IRunningObjectTable runningObjectTable;
            IEnumMoniker monikerEnumerator;
            IMoniker[] monikers = new IMoniker[1];
            IBindCtx bindCtx;
            Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
            bindCtx.GetRunningObjectTable(out runningObjectTable);
            runningObjectTable.EnumRunning(out monikerEnumerator);
            monikerEnumerator.Reset();
            while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
            {
                IBindCtx ctx;
                CreateBindCtx(0, out ctx);
                string runningObjectName;
                monikers[0].GetDisplayName(ctx, null, out runningObjectName);
                if (runningObjectName.Contains(name))
                {
                    object runningObjectVal;
                    runningObjectTable.GetObject(monikers[0], out runningObjectVal);
                    DTE2 dte = (DTE2)runningObjectVal;
                    return (dte);
                }
            }
            return null;
        }
        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
    }
}

最新更新