在调试可视化工具中在运行时检测源语言

时间:2019-05-02 13:53:05

标签: c# .net vb.net visual-studio .net-core

我正在编写一个debugging visualizer for Visual Studio,它将表达式树呈现为C#或VB.NET伪代码。

我希望默认渲染语言应与当前调试窗口的语言匹配。

此外,我想添加功能来为表达式树中的给定节点生成Watch表达式;这需要知道当前正在调试哪种语言。

如何从可视化工具代码中检测当前正在调试哪种语言?

(我假设从任意运行时代码中都不可能做到这一点,因为代码已编译为IL。)

Project issue

3 个答案:

答案 0 :(得分:8)

感谢@dymanoid和@Homer Jay 最终版本是:

public enum SourceLanguage
{
    Unknown, // probably C# for it's the only language without any particular symptom
    VB,
    FSharp,
    Cpp
}

static class Extensions
{
    private static readonly Dictionary<Assembly, SourceLanguage> cache = new Dictionary<Assembly, SourceLanguage>();

    public static SourceLanguage GetSourceLanguage(this Type type) => type.Assembly.GetSourceLanguage();

    public static SourceLanguage GetSourceLanguage(this Assembly assembly)
    {
        if (cache.TryGetValue(assembly, out var sourceLanguage))
            return sourceLanguage;

        var name = assembly.GetName().Name;
        var resources = assembly.GetManifestResourceNames();
        var assemblies = assembly.GetReferencedAssemblies().Select(a => a.Name);
        var types = assembly.DefinedTypes;

        if (assemblies.Contains("Microsoft.VisualBasic") &&
            resources.Contains($"{name}.Resources.resources"))
            sourceLanguage = SourceLanguage.VB;
        else if (assemblies.Contains("FSharp.Core") &&
                 resources.Contains($"FSharpSignatureData.{name}") &&
                 resources.Contains($"FSharpOptimizationData.{name}"))
            sourceLanguage = SourceLanguage.FSharp;
        else if (types.Any(t => t.FullName.Contains("<CppImplementationDetails>.")))
            sourceLanguage = SourceLanguage.Cpp;
        else
            sourceLanguage = SourceLanguage.Unknown;

        cache[assembly] = sourceLanguage;

        return sourceLanguage;
    }
}

用法

如果一个人的类型为CSTypeVBTypeFSTypeCppTypeC#VB.NETF#分别C++/CLI

class Program
{
    static readonly Dictionary<SourceLanguage, string> dict = new Dictionary<SourceLanguage, string>()
    {
        [SourceLanguage.Unknown] = "C#",
        [SourceLanguage.VB] = "VB.NET",
        [SourceLanguage.FSharp] = "F#",
        [SourceLanguage.Cpp] = "C++/CLI"
    };

    static void Main(string[] args)
    {
        Console.WriteLine(string.Format($"Entry assembly source language: {dict[Assembly.GetEntryAssembly().GetSourceLanguage()]}"));
        foreach (var t in new[] { typeof(CSType), typeof(VBType), typeof(FSType), typeof(CppType) })
            Console.WriteLine($"{t.Name} source language: {dict[t.GetSourceLanguage()]}");
    }
}

结果

Entry assembly source language: C#
CSType source language: C#
VBType source language: VB.NET
FSType source language: F#
CppType source language: C++/CLI

有关详细信息,请参见编辑。

答案 1 :(得分:6)

Visual Studio Custom Visualizer API非常简洁。它没有提供任何方式来获取有关正在调试的代码的其他信息。

实际上,您可以按照@Alex提出的方法进行操作。但是,有一些缺点。例如。您可能需要手动将要调试的程序集加载到临时AppDomain中。此外,这种方法并不是真正可靠的方法,因为它完全允许C#中实现的程序集引用Microsoft.VisualBasic程序集。

对于更通用,更安全的方法,您可以使用Visual Studio Debugger API

这里是一个实现方法的想法:

  • 实现IDebugEventCallback2界面; Visual Studio调试器将使用IDebugEventCallback2.Event方法通知您的代码。
  • 获取一个IVsDebugger实例。
  • 调用IVsDebugger.AdviseDebugEventCallback方法,将您的IDebugEventCallback2实现作为事件接收器。
  • 在您的Event方法中,检查IDebugBreakpointEvent2-这是要调试的进程到达断点的时间;您可能需要使用QueryInterface进行检查-请参见Note for Callers
  • 到达断点时,使用提供的IDebugThread2实例获取IEnumDebugFrameInfo2对象,请参见IDebugThread2.EnumFrameInfo
  • 调用IEnumDebugFrameInfo2.Next方法以获取FRAMEINFO结构。
  • 从该结构中,获取一个IDebugStackFrame2实例(m_pFrame字段)并使用其GetLanguageInfo方法来获取在当前断点处实现代码的语言。

很长一段时间,是的。但是绝对是(也许是唯一)可靠的。


更新

尽管上面引用的Visual Studio Debugger API看起来像本机代码,您也可以轻松地在托管代码中使用它。该API基于COM,.NET平台完全支持它。您可以安装Visual Studio SDK,也可以只使用相应的NuGet软件包,例如Microsoft.VisualStudio.Debugger.Interop。通过添加这些程序包,您可以获得COM类型的托管包装,并可以在代码中使用它们,就像它们是纯托管C#类型一样。

有关文档,请查看Visual Studio debugger extensibility。您还可以在GitHub上检出Visual Studio SDK samples

答案 2 :(得分:2)

应该考虑到还有另一种可能性。该程序集可以用C ++ / CLI编写。

但这很有趣。它在清单中说明。 C ++ / CLI清单中的所有其他内容都与C#版本相同。

// Metadata version: v4.0.30319 (C++/CLI)
.assembly extern mscorlib
{
...
}
...