如何从外部DTE中检索IVsDebugger以在Visual Studio 2019中实现自动化

问题描述

我正在尝试为Visual Studio 2019编写VSIX,以控制Visual Studio IDE的多个实例。我们正在进行一个联网项目,该项目需要一些自动化才能执行多个用户的测试。过去我会在外部工具中使用DTE,但我的理解是,从VS2017开始,COM guid不再全局注册,因此在IDE中进行操作是唯一的方法

无论如何,我都试图获取IVsDebugger,以便可以跟踪调试器中的事件。但是,我没有运气。我可以获得IVsDebugger2、3、4、5,但没有IVSDebugger。这是我正在做的一般流程:

void CaptureDebugger()
{
    DTE dte = GetDTE(GetRemoteProcessID());

    ServiceProvider sp = new     ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte);
    IVsDebugger vsDebugger = sp.GetService(typeof(SVsShellDebugger)) as IVsDebugger;
    // vsDebugger is null!
    IVsDebugger2 vsDebugger2 = sp.GetService(typeof(SVsShellDebugger)) as IVsDebugger2;
    // vsDebugger2 is not null!


}

/// <summary>
/// Gets the DTE object from any devenv process.
/// </summary>
private static EnvDTE.DTE GetDTE(int processId)
{
    object runningObject = null;

    IBindCtx bindCtx = null;
    IRunningObjectTable rot = null;
    IEnumMoniker enumMonikers = null;

    try
    {
        Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0,ppbc: out bindCtx));
        bindCtx.GetRunningObjectTable(out rot);
        rot.EnumRunning(out enumMonikers);

        IMoniker[] moniker = new IMoniker[1];
        IntPtr numberFetched = IntPtr.Zero;
        while (enumMonikers.Next(1,moniker,numberFetched) == 0)
        {
            IMoniker runningObjectMoniker = moniker[0];

            string name = null;

            try
            {
                if (runningObjectMoniker != null)
                {
                    runningObjectMoniker.GetdisplayName(bindCtx,null,out name);
                }
            }
            catch (UnauthorizedAccessException)
            {
                // Do nothing,there is something in the ROT that we do not have access to.
            }
            Regex monikerRegex = new Regex(@"!VisualStudio.DTE\.\d+\.\d+\:" + processId,RegexOptions.IgnoreCase);
            if (!string.IsNullOrEmpty(name) && monikerRegex.IsMatch(name))
            {
                Marshal.ThrowExceptionForHR(rot.Getobject(runningObjectMoniker,out runningObject));
            }
        }
    }
    finally
    {
        if (enumMonikers != null)
            Marshal.ReleaseComObject(enumMonikers);

        if (rot != null)
            Marshal.ReleaseComObject(rot);

        if (bindCtx != null)
            Marshal.ReleaseComObject(bindCtx);
    }

    return runningObject as EnvDTE.DTE;
}

让我困惑的是我通过调用获得了本地IVsDebugger

var MYDEBUGGER = Package.GetGlobalService(typeof(SVsShellDebugger)) as IVsDebugger;

我看到的是使用GlobalService。我认为我检索到的DTE中没有类似的东西。

有见识吗?

解决方法

我也遇到了这个问题(但是就我而言,我实际上是在尝试检索 IVsDebugger in proc 而不是听起来像 out of proc);在调试 vsdebug!CDebugger::QueryInterface 的工作原理后,我确定实际问题似乎是您的应用程序 the calling thread 中的 needs to be STA

  • 当您的应用程序中的调用线程是 MTA 时,而 vsdebug!CDebugger::QueryInterfaceHRESULT 0 返回
  • 由于 0x80040155 未能找到此类型的代理 DLL,这很快就被 OLE 变成了 REGDB_E_IIDNOTREG (CStdWrapper::GetPSFactory)
  • 此错误依次由 CRemoteUnknown::RemQueryInterface 转换为 0x80004002 (E_NOINTERFACE)
  • 如果您尝试在 C# 中使用 Marshal.QueryInterface 直接查看发生的情况,系统会向您报告哪些内容。

如果您的程序包含位于远程 Visual Studio 进程中的进程内组件(就像我的一样),您可以检索并执行针对 UI 线程上的 IVsDebugger 的操作。否则,您可能会创建一个新的 Thread 并在启动它之前对其调用 thread.SetApartmentState(ApartmentState.STA)