如何检测哪种 .NET 语言正在调用我的代码
How to detect which .NET language is calling my code
我正在构建一个生成用户代理字符串的库,该字符串报告一些漂亮的数据,例如 OS 版本和 currently installed .NET Framework versions。我很好奇:
是否可以通过编程方式检测哪种语言正在调用我的库?或者源语言一旦编译成 CIL 就完全不透明了?
不幸的是,这是不可能的,正如您通过提及 CIL 所确定的那样。
我能想到两种可能的解决方案:
- 但是,如果相应的 .NET dll(调用代码的程序集)的 PDB 文件可用,那么您可以检查该文件以确定所使用的语言。然而,这是一个很大的 "IF",我会远离这样的解决方案。
- 检查调用程序集引用的程序集。有时(很少)可能会引用特定于语言的程序集。
编辑:我把它变成了一个small library,它封装了一些启发式算法,便于调用。
我想出了一个启发式方法,它似乎可以很好地满足我自己的需要。
@Don 的回答和这些问题给了我一些提示:
- Decompiled DLL - Clues to help tell whether it was C# or VB.NET?
- Decompiling VB.Net assembly produces code with invalid member variable names; names starting with $STATIC$
注意事项:
- 仅区分 VB.NET 和 C#,不区分任何其他 CLR 语言。如果没有足够的 VB.
证据,则假定 C#
- 这是一种有根据的猜测,因此误报的可能性 > 0。
- 一些提示基于编译器实现细节,这些细节可能会发生变化。
- 这似乎也适用于 Mono,但 YMMV。
- 它是昂贵的反射,所以在现实生活中你会想要将它包装在
Lazy<>
或其他一些机制中以确保它只被调用一次。
- 如@HABO 所述,这可能是也可能不是非常有用的信息。我主要是想看看它是否可以完成。
var lang = DetectAssemblyLanguage(Assembly.GetCallingAssembly());
public static string DetectAssemblyLanguage(Assembly assembly)
{
var referencedAssemblies = assembly
.GetReferencedAssemblies()
.Select(x => x.Name);
var types = assembly
.GetTypes();
// Biggest hint: almost all VB.NET projects have a
// hidden reference to the Microsoft.VisualBasic assembly
bool referenceToMSVB = referencedAssemblies.Contains("Microsoft.VisualBasic");
// VB.NET projects also typically reference the special
// (YourProject).My.My* types that VB generates
bool areMyTypesPresent = types.Select(x => x.FullName).Where(x => x.Contains(".My.My")).Any();
// If a VB.NET project uses any anonymous types,
// the compiler names them like VB$AnonymousType_0`1
bool generatedVbNames = types.Select(x => x.Name).Where(x => x.StartsWith("VB$")).Any();
// If a C# project uses dynamic, it'll have a reference to Microsoft.CSharp
bool referenceToMSCS = referencedAssemblies.Contains("Microsoft.CSharp");
// If a C# project uses any anonymous types,
// the compiler names them like <>f__AnonymousType0`1
bool generatedCsNames = types.Select(x => x.Name).Where(x => x.StartsWith("<>")).Any();
var evidenceForVb = new bool[]
{
referenceToMSVB,
myTypesPresent,
vbGeneratedNames
};
var evidenceForCsharp = new bool[] {
true, // freebie. ensures ties go to C#
referenceToMSCS,
csGeneratedNames
};
var scoreForVb = evidenceForVb.Count(x => x)
- evidenceForCsharp.Count(x => x);
// In the case of a tie, C# is assumed
return scoreForVb > 0
? "vb"
: "cs";
}
我正在构建一个生成用户代理字符串的库,该字符串报告一些漂亮的数据,例如 OS 版本和 currently installed .NET Framework versions。我很好奇:
是否可以通过编程方式检测哪种语言正在调用我的库?或者源语言一旦编译成 CIL 就完全不透明了?
不幸的是,这是不可能的,正如您通过提及 CIL 所确定的那样。
我能想到两种可能的解决方案:
- 但是,如果相应的 .NET dll(调用代码的程序集)的 PDB 文件可用,那么您可以检查该文件以确定所使用的语言。然而,这是一个很大的 "IF",我会远离这样的解决方案。
- 检查调用程序集引用的程序集。有时(很少)可能会引用特定于语言的程序集。
编辑:我把它变成了一个small library,它封装了一些启发式算法,便于调用。
我想出了一个启发式方法,它似乎可以很好地满足我自己的需要。
@Don 的回答和这些问题给了我一些提示:
- Decompiled DLL - Clues to help tell whether it was C# or VB.NET?
- Decompiling VB.Net assembly produces code with invalid member variable names; names starting with $STATIC$
注意事项:
- 仅区分 VB.NET 和 C#,不区分任何其他 CLR 语言。如果没有足够的 VB. 证据,则假定 C#
- 这是一种有根据的猜测,因此误报的可能性 > 0。
- 一些提示基于编译器实现细节,这些细节可能会发生变化。
- 这似乎也适用于 Mono,但 YMMV。
- 它是昂贵的反射,所以在现实生活中你会想要将它包装在
Lazy<>
或其他一些机制中以确保它只被调用一次。 - 如@HABO 所述,这可能是也可能不是非常有用的信息。我主要是想看看它是否可以完成。
var lang = DetectAssemblyLanguage(Assembly.GetCallingAssembly());
public static string DetectAssemblyLanguage(Assembly assembly)
{
var referencedAssemblies = assembly
.GetReferencedAssemblies()
.Select(x => x.Name);
var types = assembly
.GetTypes();
// Biggest hint: almost all VB.NET projects have a
// hidden reference to the Microsoft.VisualBasic assembly
bool referenceToMSVB = referencedAssemblies.Contains("Microsoft.VisualBasic");
// VB.NET projects also typically reference the special
// (YourProject).My.My* types that VB generates
bool areMyTypesPresent = types.Select(x => x.FullName).Where(x => x.Contains(".My.My")).Any();
// If a VB.NET project uses any anonymous types,
// the compiler names them like VB$AnonymousType_0`1
bool generatedVbNames = types.Select(x => x.Name).Where(x => x.StartsWith("VB$")).Any();
// If a C# project uses dynamic, it'll have a reference to Microsoft.CSharp
bool referenceToMSCS = referencedAssemblies.Contains("Microsoft.CSharp");
// If a C# project uses any anonymous types,
// the compiler names them like <>f__AnonymousType0`1
bool generatedCsNames = types.Select(x => x.Name).Where(x => x.StartsWith("<>")).Any();
var evidenceForVb = new bool[]
{
referenceToMSVB,
myTypesPresent,
vbGeneratedNames
};
var evidenceForCsharp = new bool[] {
true, // freebie. ensures ties go to C#
referenceToMSCS,
csGeneratedNames
};
var scoreForVb = evidenceForVb.Count(x => x)
- evidenceForCsharp.Count(x => x);
// In the case of a tie, C# is assumed
return scoreForVb > 0
? "vb"
: "cs";
}