简体   繁体   English

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

[英]How to retrieve IVsDebugger from external DTE for automation in Visual Studio 2019

I am trying to write a VSIX for Visual Studio 2019 that controls multiple instances of the Visual Studio IDE.我正在尝试为 Visual Studio 2019 编写一个 VSIX,用于控制 Visual Studio IDE 的多个实例。 We are working on a networked project that requires some automation to perform testing of multiple users.我们正在开展一个网络项目,该项目需要一些自动化来执行多个用户的测试。 In the past I would have used DTE in an external tool, but my understanding is that as of VS2017 the COM guids are no longer globally registered, so doing it within the IDE is the only way.过去我会在外部工具中使用 DTE,但我的理解是,从 VS2017 开始,COM guid 不再全局注册,因此在 IDE 中进行这是唯一的方法。

Regardless, I am trying to get the IVsDebugger so I can track events in the debugger.无论如何,我正在尝试获取 IVsDebugger,以便我可以在调试器中跟踪事件。 However, I am having no luck.但是,我没有运气。 I can get IVsDebugger2, 3, 4, 5 but not IVSDebugger.我可以得到 IVsDebugger2, 3, 4, 5 但不能得到 IVSDebugger。 Here is the general flow of what I am doing:这是我正在做的一般流程:

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;
}

What confuses me is I get get the local IVsDebugger via the call让我困惑的是我通过调用获得了本地 IVsDebugger

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

Which I see is using a GlobalService.我看到的是使用 GlobalService。 I don't think there is an equivalent in the DTE I retrieve.我认为我检索的 DTE 中没有等效项。

Any insight?任何见解?

I ran into this issue as well (however in my case, I'm actually trying to retrieve the IVsDebugger in proc rather than what sounds like out of proc );我也遇到了这个问题(但是在我的情况下,我实际上是在尝试检索proc 中IVsDebugger而不是听起来像是out of proc ); after debugging into how vsdebug!CDebugger::QueryInterface works I determined the actual issue appears to be that the calling thread in your applicationneeds to be STA .在调试到vsdebug!CDebugger::QueryInterface如何工作后,我确定实际问题似乎是您的应用程序的调用线程需要是 STA

  • When the calling thread in your application is MTA, while vsdebug!CDebugger::QueryInterface returns with HRESULT 0当应用程序中的调用线程是 MTA 时,而vsdebug!CDebugger::QueryInterface返回HRESULT 0
  • This shortly gets turned into 0x80040155 ( REGDB_E_IIDNOTREG ) by OLE due to CStdWrapper::GetPSFactory failing to find a proxy DLL for this type由于CStdWrapper::GetPSFactory未能找到这种类型的代理 DLL,这很快就会被 OLE 变成0x80040155 ( REGDB_E_IIDNOTREG )
  • This error in turn gets converted by CRemoteUnknown::RemQueryInterface to 0x80004002 ( E_NOINTERFACE )这个错误反过来被CRemoteUnknown::RemQueryInterface转换为0x80004002 ( E_NOINTERFACE )
  • Which is what is reported back to you if you try and Marshal.QueryInterface in C# to see what's going on directly.如果您尝试使用 C# 中的Marshal.QueryInterface直接查看发生了什么,那么这就是返回给您的信息。

If your program contains in-proc components that live inside the remote Visual Studio process (as mine does) you can retrieve and execute your operations against the IVsDebugger on the UI thread.如果您的程序包含位于远程 Visual Studio 进程中的进程内组件(就像我的一样),您可以在 UI 线程上针对IVsDebugger检索和执行您的操作。 Otherwise, you can potentially create a new Thread and call thread.SetApartmentState(ApartmentState.STA) on it prior to starting it否则,您可能会创建一个新Thread并在启动它之前对其调用thread.SetApartmentState(ApartmentState.STA)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM