簡體   English   中英

在 Visual Studio 外獲取 EnvDTE.DTE 實例 IDE

[英]Getting EnvDTE.DTE instance outside Visual Studio IDE

我在 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”)
  • 詢問用戶他們想要使用哪一個。
  • 使用process.Id在ROT中找到一個對象,我認為這是OP的問題。 這是通過使用IEnumMoniker.Next()枚舉ROT來完成的,它返回了monikers和進程id(在VS的情況下)。

  • 找到了綽號。 將運行對象投射到DTE然后離開。

COM,ROT和Moniker對我來說聽起來太復雜了,所以我很高興地看到在上面的鏈接已經完成了繁重的工作。

我讓這個例子在幾分鍾內完成。 它是我第一次使用調試器時工作。 但是在全速運行時,我需要添加一些睡眠或重試,因為很容易從HRESULT中獲取異常:0x8001010A(RPC_E_SERVERCALL_RETRYLATER))

另外,我用一個容忍其他版本VS的正則表達式替換了完全匹配:

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

VS可能繁忙,打開對話框或編譯許多項目的問題對於許多應用程序來說很常見,您可能會嘗試強制執行提要鍵擊或COM請求。 如果您在幾秒鍾內重試錯誤。 最后,如果需要,彈出一個消息框。

一些 SO 用戶不喜歡鏈接,因為鏈接會損壞;)

我花了一個多小時來寫我的版本,所以不妨把它貼在這里。 需要對envdteenvte80的引用(來自添加引用/程序集/擴展)。 提供 static 方法,用於在 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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM