Last active
June 6, 2019 13:56
-
-
Save xoofx/95d1b2a7343611891f09a951813038ef to your computer and use it in GitHub Desktop.
List usage of non public Method/Field/Type members from a specified assembly to any external assembly references
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using CecilTool; | |
using Mono.Cecil; | |
namespace CecilTool | |
{ | |
/// <summary> | |
/// Simple tool using Mono.Cecil to dump the list of usage of internals methods/fields | |
/// referencing another assembly from a specified assembly. | |
/// | |
/// Usage: ApiVerifier.DumpUsageOfInternalsFromExternalAssemblies(@"C:\Path\To\My\Assembly.dll") | |
/// | |
/// </summary> | |
public static class ApiVerifier | |
{ | |
public static void DumpUsageOfInternalsFromExternalAssemblies(string assemblyPath) | |
{ | |
if (assemblyPath == null) throw new ArgumentNullException(nameof(assemblyPath)); | |
DumpUsageOfInternalsFromExternalAssemblies(new List<string>() {assemblyPath}); | |
} | |
public static void DumpUsageOfInternalsFromExternalAssemblies(List<string> assemblyPaths) | |
{ | |
if (assemblyPaths == null) throw new ArgumentNullException(nameof(assemblyPaths)); | |
var assemblyFullPaths = new List<string>(); | |
var assemblyResolver = new DefaultAssemblyResolver(); | |
foreach (var assemblyPath in assemblyPaths) | |
{ | |
var fullPath = Path.Combine(Environment.CurrentDirectory, assemblyPath); | |
assemblyFullPaths.Add(fullPath); | |
assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(fullPath)); | |
} | |
for (var i = 0; i < assemblyFullPaths.Count; i++) | |
{ | |
var fullPath = assemblyFullPaths[i]; | |
var assembly = AssemblyDefinition.ReadAssembly(fullPath, new ReaderParameters(ReadingMode.Immediate) | |
{ | |
AssemblyResolver = assemblyResolver | |
}); | |
if (i > 0) Console.WriteLine(); | |
DumpUsageOfInternalsFromExternalAssemblies(assembly); | |
} | |
} | |
public static void DumpUsageOfInternalsFromExternalAssemblies(AssemblyDefinition assembly) | |
{ | |
if (assembly == null) throw new ArgumentNullException(nameof(assembly)); | |
const int mdCountMax = 1 << 24; | |
bool dumpAssemblyName = true; | |
foreach (var module in assembly.Modules) | |
{ | |
for (int i = 1; i < mdCountMax; i++) | |
{ | |
var item = module.LookupToken(new MetadataToken(TokenType.MemberRef, i)); | |
if (item == null) | |
{ | |
break; | |
} | |
if (item is MemberReference memberRef) | |
{ | |
var memberDef = memberRef.Resolve(); | |
bool isInternal = CheckIsInternal(memberDef); | |
if (isInternal && !assembly.Modules.Contains(memberDef.DeclaringType.Module)) | |
{ | |
if (dumpAssemblyName) | |
{ | |
Console.WriteLine($"Internals used in Assembly: {assembly.Name} ({assembly.MainModule.FileName})"); | |
Console.WriteLine("---------------------------------------------------------------------------------------"); | |
dumpAssemblyName = false; | |
} | |
Console.WriteLine($"{memberDef.GetType().Name}: {memberRef} => Assembly: {memberDef.DeclaringType.Module.FileName}"); | |
} | |
} | |
} | |
} | |
} | |
private static bool CheckIsInternal(IMemberDefinition memberDef) | |
{ | |
if (memberDef == null) | |
{ | |
return false; | |
} | |
bool isInternal = false; | |
switch (memberDef) | |
{ | |
case MethodDefinition methodDef: | |
isInternal = methodDef.IsAssembly; | |
break; | |
case FieldDefinition fieldDef: | |
isInternal = fieldDef.IsAssembly; | |
break; | |
case TypeDefinition typeDef: | |
isInternal = !(typeDef.IsPublic || typeDef.IsNestedPublic); | |
break; | |
} | |
// Check also the declaring type | |
return isInternal || CheckIsInternal(memberDef.DeclaringType); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment