[英]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用戶不喜歡這里的鏈接是詳細信息:
使用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 用戶不喜歡鏈接,因為鏈接會損壞;)
我花了一個多小時來寫我的版本,所以不妨把它貼在這里。 需要對envdte
和envte80
的引用(來自添加引用/程序集/擴展)。 提供 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.