Определять исходный язык во время выполнения из визуализатора отладки
Я пишу визуализатор отладки для Visual Studio, который отображает деревья выражений в псевдокод С# или VB.NET.
Я хочу, чтобы язык рендеринга по умолчанию соответствовал языку текущего окна отладки.
Также я хочу добавить функциональность для генерации выражения Watch для данного узла в дереве выражений; для этого необходимо знать, какой язык в настоящее время отлаживается.
Как определить из кода визуализатора, какой язык в настоящее время отлаживается?
(Я предполагаю, что это невозможно из произвольного кода времени выполнения, так как код компилируется в IL.)
(Вопрос проекта)
Ответы
Ответ 1
Благодаря @dymanoid и @Homer Jay окончательная версия:
public enum SourceLanguage
{
Unknown, // probably C# for it 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;
}
}
Использование:
Если у вас есть типы CSType
, VBType
, FSType
и CppType
созданные с C#
, VB.NET
, F#
и 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
Для более подробной информации см. Правки.
Ответ 2
Visual Studio Custom Visualizer API довольно лаконичен. Он не предоставляет никакой возможности получить дополнительную информацию об отлаживаемом коде.
Действительно, вы можете пойти по пути, предложенному @Alex. Однако есть некоторые недостатки. Например, вам может понадобиться вручную загрузить отлаживаемую сборку (во временный AppDomain
). Кроме того, этот способ не совсем надежен, поскольку он позволяет сборке, реализованной на С#, ссылаться на сборку Microsoft.VisualBasic
.
Для более общего и безопасного подхода вы можете использовать Visual Studio Debugger API.
Вот идея, как это реализовать:
-
IDebugEventCallback2
интерфейс IDebugEventCallback2
; отладчик Visual Studio уведомит ваш код с IDebugEventCallback2.Event
метода IDebugEventCallback2.Event
. - Получить экземпляр
IVsDebugger
. - Вызовите метод
IVsDebugger.AdviseDebugEventCallback
предоставив вашу реализацию IDebugEventCallback2
в качестве приемника событий. - В вашем методе
Event
проверьте наличие IDebugBreakpointEvent2
- это когда отлаживаемый процесс достигает точки IDebugBreakpointEvent2
; вам может понадобиться использовать QueryInterface
чтобы проверить это - см. Примечание для абонентов. - При достижении точки
IDebugThread2
используйте предоставленный экземпляр IDebugThread2
чтобы получить объект IEnumDebugFrameInfo2
, см. IDebugThread2.EnumFrameInfo
. - Вызовите метод
IEnumDebugFrameInfo2.Next
чтобы получить структуру FRAMEINFO
. - Из этой структуры получите экземпляр
IDebugStackFrame2
(поле m_pFrame
) и используйте его метод GetLanguageInfo
чтобы получить язык, реализующий код в текущей точке GetLanguageInfo
.
Долгий путь, да. Но определенно (может быть, только) надежный.
Обновить
Несмотря на то, что API отладчика Visual Studio, упомянутый выше, выглядит как собственный код, его можно легко использовать и в управляемом коде. Этот API основан на COM, и платформа .NET полностью его поддерживает. Вы можете установить Visual Studio SDK или просто использовать соответствующие пакеты NuGet, например Microsoft.VisualStudio.Debugger.Interop. Добавляя эти пакеты, вы получаете управляемые оболочки для типов COM и можете использовать их в своем коде, как если бы они были чисто управляемыми типами С#.
Для документации, посмотрите на расширяемость отладчика Visual Studio. Вы также можете ознакомиться с примерами Visual Studio SDK на GitHub.
Ответ 3
Нужно учитывать, что есть другая возможность. Сборка может быть написана в C++/CLI.
Но это интересно просто. Это указано в манифесте. Все остальное в манифесте C++/CLI совпадает с версией С#.
// Metadata version: v4.0.30319 (C++/CLI)
.assembly extern mscorlib
{
...
}
...