在Visual Studio IDE外部获取EnvDTE.DTE实例
本文关键字:EnvDTE DTE 实例 获取 外部 Visual Studio IDE | 更新日期: 2023-09-27 18:33:26
>我正在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用户不喜欢链接,因为链接会损坏;)
我花了一个多小时来写我的版本,所以不妨在这里发布。需要对envdte
和envte80
的引用(从添加引用/程序集/扩展(。提供静态方法,用于在 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);
}
}