Files
Radicade/Tools/InteropInspect/Program.cs
T
2026-10-16 23:33:13 -07:00

119 lines
3.5 KiB
C#

using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
if (args.Length < 2)
{
Console.Error.WriteLine("Usage: InteropInspect <assembly.dll> <keyword>... | <assembly.dll> --type <FullName>");
return 1;
}
var path = args[0];
var detail = false;
string detailType = null;
var keywords = new List<string>();
for (int i = 1; i < args.Length; i++)
{
if (args[i] == "--type" && i + 1 < args.Length) { detail = true; detailType = args[++i]; }
else keywords.Add(args[i]);
}
using var fs = File.OpenRead(path);
using var pe = new PEReader(fs);
var md = pe.GetMetadataReader();
string GetString(StringHandle h) => md.GetString(h);
string GetNs(TypeDefinition t)
{
var ns = GetString(t.Namespace);
var decl = t.GetDeclaringType();
if (!decl.IsNil)
{
var outer = md.GetTypeDefinition(decl);
return GetNs(outer) + "+" + GetString(outer.Name);
}
return ns;
}
foreach (var tdh in md.TypeDefinitions)
{
var td = md.GetTypeDefinition(tdh);
var fullName = GetNs(td) + "." + GetString(td.Name);
bool match;
if (detail)
match = fullName.Equals(detailType, StringComparison.OrdinalIgnoreCase);
else
match = keywords.Any(k => fullName.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0 ||
(k.StartsWith("field:") && td.GetFields().Any(f =>
GetString(md.GetFieldDefinition(f).Name).Contains(k[6..], StringComparison.OrdinalIgnoreCase))));
if (!match) continue;
if (!detail)
{
Console.WriteLine(fullName);
continue;
}
var sb = new StringBuilder();
sb.AppendLine($"TYPE {fullName}");
if (!td.BaseType.IsNil)
{
try { sb.AppendLine($" base: {ResolveName(md, td.BaseType)}"); } catch { }
}
foreach (var iih in td.GetInterfaceImplementations())
{
var ii = md.GetInterfaceImplementation(iih);
try { sb.AppendLine($" impl: {ResolveName(md, ii.Interface)}"); } catch { }
}
sb.AppendLine(" fields:");
foreach (var fh in td.GetFields())
{
var f = md.GetFieldDefinition(fh);
var constantHandle = f.GetDefaultValue();
var constantValue = "";
if (!constantHandle.IsNil)
{
var constant = md.GetConstant(constantHandle);
var reader = md.GetBlobReader(constant.Value);
constantValue = " = " + reader.ReadConstant(constant.TypeCode);
}
sb.AppendLine($" {GetString(f.Name)}{constantValue}");
}
sb.AppendLine(" methods:");
foreach (var mh in td.GetMethods())
{
var m = md.GetMethodDefinition(mh);
var attrs = m.Attributes;
var prms = m.GetParameters().Select(ph => md.GetParameter(ph))
.Select(pp => $"{GetString(pp.Name)}");
sb.AppendLine($" {GetString(m.Name)}({string.Join(", ", prms)}) [{attrs}]");
}
sb.AppendLine(" properties:");
foreach (var ph in td.GetProperties())
{
var p = md.GetPropertyDefinition(ph);
sb.AppendLine($" {GetString(p.Name)}");
}
Console.Write(sb.ToString());
}
return 0;
static string ResolveName(MetadataReader md, EntityHandle h)
{
if (h.Kind == HandleKind.TypeReference)
{
var tr = md.GetTypeReference((TypeReferenceHandle)h);
return $"{md.GetString(tr.Namespace)}.{md.GetString(tr.Name)}";
}
if (h.Kind == HandleKind.TypeDefinition)
{
var td2 = md.GetTypeDefinition((TypeDefinitionHandle)h);
return $"{md.GetString(td2.Namespace)}.{md.GetString(td2.Name)}";
}
return h.Kind.ToString();
}