Various tools and helpers to read assembly metadata.
C#
274
520 commits
updated Sep 26, 2026
A collection of small command-line tools for inspecting .NET assemblies on disk: their versions, references, signatures, debug info, MVIDs, and binary compatibility. Most are published as dotnet global tools.
For LLM agents: see
llms.txtfor a dense, token-conscious reference that teaches an agent the whole toolset in one file.
| Tool | NuGet | Purpose |
|---|---|---|
| checkbinarycompat | checkbinarycompat | Detect binary incompatibilities across a set of assemblies: missing types/members/assemblies, version mismatches, facade/interop issues, broken app.config binding redirects. Compares against a baseline .txt and returns a non-zero exit code on change — designed to plug into CI builds. |
| lbi | lbi | List binary info for assemblies in a tree: AssemblyName, AssemblyVersion, FileVersion, InformationalVersion, TargetFramework, platform, MVID, SHA1, strong-name signing, Authenticode signature, file size. Default output groups identical files by AssemblyName then SHA1; -l switches to a flat directory-listing format. |
| refdump | refdump | Print all assembly references for one or more .dll/.exe. Optionally list types/members consumed per reference, filter to a specific reference name (e.g. find every consumer of Newtonsoft.Json), or emit a Graphviz reference graph. |
| pdb | pdb | Print .pdb info from a .dll's debug directory (Pdb Guid / Age / path / Stamp, embedded-pdb flag, SourceLink). Verify a .dll matches a .pdb. Download .pdbs from a symbol server. Find the matching .pdb for a .dll in a directory tree. Handles portable and native pdbs. |
Install / update any of them (dotnet tool update -g installs if missing, upgrades to latest if present):
dotnet tool update -g checkbinarycompat
dotnet tool update -g lbi
dotnet tool update -g refdump
dotnet tool update -g pdb

StrongNameSigned bit in the COR header of a managed assembly. FakeSign foo.dll marks a delay-signed assembly as signed so the runtime will load it without a real key; FakeSign foo.dll -u un-signs; add -f to force when the assembly is already in the requested state.Prefers32Bit / Requires32Bit flags in the COR header: corflags foo.exe /32BITPREF-.System.Reflection.Metadata.LargeAddressAware bit in a 32-bit executable to unlock the 4 GB user-mode address space.| Question | Command |
|---|---|
What versions of XYZ.dll exist under this directory tree? | lbi XYZ.dll |
Which assemblies in this folder reference Newtonsoft.Json, and what types/members do they use? | refdump *.dll -s -a:Newtonsoft.Json -t -m (drop -t -m for just the consumer list) |
What types from System.Collections.Immutable does Foo.dll use? | refdump Foo.dll -t -a:System.Collections.Immutable |
Does my bin\Debug\ directory have binding redirect issues or missing types/members across its references? | cd bin\Debug && checkbinarycompat |
Is the report from the last checkbinarycompat run different from the checked-in baseline? | checkbinarycompat returns exit code 1 if any line differs — wire it into CI. |
| Validate many startup binaries that share one output directory | checkbinarycompat config.json — see batch mode. |
| Find unreferenced assemblies in a directory | refdump *.dll -g then read AssemblyGraph.txt (or open in MSBuild Structured Log Viewer's graph viewer). |
Where did X.dll get copied from? Which NuGet package shipped it? | Build with msbuild /bl (or capture the CI binlog), then ask binlogmcp: $copy <full path to X.dll> and $nuget X.dll. See Binlogs below. |
What's the transitive NuGet dependency chain that brought in version A of Foo? | Binlog — ask binlogmcp: $nuget Foo A (or $nuget project(MyApp.csproj) Foo). |
Does this .dll have a matching .pdb on this symbol server? | pdb Foo.dll http://symbol.server/ |
| What's the MVID of this assembly? | mvid foo.dll (or lbi foo.dll -mvid) |
| Is this assembly strong-name signed? | lbi foo.dll -sn |
The tools above answer what is on disk. They cannot answer how it got there — which Copy task produced this file, which NuGet package shipped it, what transitive dependency chain pulled in version 13.0.3 instead of 12.0.0. Those questions require an MSBuild binary log (.binlog):
/bl to your build: msbuild MySolution.sln /bl or dotnet build MySolution.sln /bl. It costs negligible build time and writes msbuild.binlog next to the build.binlogmcp MCP server (must be configured in your agent host). The two killer queries:
$copy <full path to X.dll> — explains where every copy of X.dll came from (producing task, project, NuGet source, content item, project reference).$nuget <package> [version] — full NuGet dependency graph; traces which <PackageReference> or transitive chain forced a specific version.Typical use: lbi/refdump/checkbinarycompat identify what is wrong on disk; the binlog explains why the build put it there. See llms.txt for the full playbook.
The sections below document C# library APIs for retrieving an assembly's MVID programmatically, not the CLI tools above. They're aimed at developers building their own tools. If you just want to print MVIDs on the command line, use
lbi -mvidand stop here.
Mono.Cecil stripped down to a single file to read an assembly's MVID without dependencies. Here are the various ways to retrieve the MVID of an assembly:
Just embed this file: https://github.com/KirillOsenkov/MetadataTools/blob/master/src/PEFile/ImageReader.cs
var mvid = ImageReader.ReadAssemblyMvid(filePath);
Cold run on a warm file: 7ms
Or see this sample from Roslyn: https://github.com/dotnet/roslyn/blob/1c98fe1fc6ef122cb3bb436aa90a0ea729fdcf37/src/Compilers/Core/MSBuildTask/MvidReader.cs#L13
https://www.nuget.org/packages/Mono.Cecil
var module = Mono.Cecil.ModuleDefinition.ReadModule(filePath);
var mvid = module.Mvid;
Cold run on a warm file: 16ms
This has the downside that it loads the assembly:
var assemblyName = AssemblyName.GetAssemblyName(filePath);
var assembly = Assembly.Load(assemblyName);
var mvid = assembly.ManifestModule.ModuleVersionId;
Cold run on a warm file: 33ms
https://www.nuget.org/packages/System.Reflection.Metadata
Sample in Roslyn: https://github.com/dotnet/roslyn/blob/1c98fe1fc6ef122cb3bb436aa90a0ea729fdcf37/src/Compilers/Core/Portable/AssemblyUtilities.cs#L84
using (var stream = File.OpenRead(filePath))
{
PEReader reader = new PEReader(stream);
var metadataReader = reader.GetMetadataReader();
var mvidHandle = metadataReader.GetModuleDefinition().Mvid;
var mvid = metadataReader.GetGuid(mvidHandle);
}
Cold run on a warm file: 76ms
BenchmarkDotNet paints quite a different performance picture:
Method | Mean | StdDev |
--------------------- |------------ |---------- |
ReadUsingReflection | 208.2023 us | 0.4036 us |
ReadUsingSRM | 98.9620 us | 0.3674 us |
ReadUsingCecil | 153.1947 us | 0.3085 us |
ReadUsingImageReader | 147.0230 us | 1.2618 us |
1,406 followers · starred Apr 2019
221 followers · starred Jun 2025
498 followers · starred Jun 2025
184 followers · starred Jul 2023
Various tools and helpers to read assembly metadata.
C#
274
520 commits
updated Sep 26, 2026
A collection of small command-line tools for inspecting .NET assemblies on disk: their versions, references, signatures, debug info, MVIDs, and binary compatibility. Most are published as dotnet global tools.
For LLM agents: see
llms.txtfor a dense, token-conscious reference that teaches an agent the whole toolset in one file.
| Tool | NuGet | Purpose |
|---|---|---|
| checkbinarycompat | checkbinarycompat | Detect binary incompatibilities across a set of assemblies: missing types/members/assemblies, version mismatches, facade/interop issues, broken app.config binding redirects. Compares against a baseline .txt and returns a non-zero exit code on change — designed to plug into CI builds. |
| lbi | lbi | List binary info for assemblies in a tree: AssemblyName, AssemblyVersion, FileVersion, InformationalVersion, TargetFramework, platform, MVID, SHA1, strong-name signing, Authenticode signature, file size. Default output groups identical files by AssemblyName then SHA1; -l switches to a flat directory-listing format. |
| refdump | refdump | Print all assembly references for one or more .dll/.exe. Optionally list types/members consumed per reference, filter to a specific reference name (e.g. find every consumer of Newtonsoft.Json), or emit a Graphviz reference graph. |
| pdb | pdb | Print .pdb info from a .dll's debug directory (Pdb Guid / Age / path / Stamp, embedded-pdb flag, SourceLink). Verify a .dll matches a .pdb. Download .pdbs from a symbol server. Find the matching .pdb for a .dll in a directory tree. Handles portable and native pdbs. |
Install / update any of them (dotnet tool update -g installs if missing, upgrades to latest if present):
dotnet tool update -g checkbinarycompat
dotnet tool update -g lbi
dotnet tool update -g refdump
dotnet tool update -g pdb

StrongNameSigned bit in the COR header of a managed assembly. FakeSign foo.dll marks a delay-signed assembly as signed so the runtime will load it without a real key; FakeSign foo.dll -u un-signs; add -f to force when the assembly is already in the requested state.Prefers32Bit / Requires32Bit flags in the COR header: corflags foo.exe /32BITPREF-.System.Reflection.Metadata.LargeAddressAware bit in a 32-bit executable to unlock the 4 GB user-mode address space.| Question | Command |
|---|---|
What versions of XYZ.dll exist under this directory tree? | lbi XYZ.dll |
Which assemblies in this folder reference Newtonsoft.Json, and what types/members do they use? | refdump *.dll -s -a:Newtonsoft.Json -t -m (drop -t -m for just the consumer list) |
What types from System.Collections.Immutable does Foo.dll use? | refdump Foo.dll -t -a:System.Collections.Immutable |
Does my bin\Debug\ directory have binding redirect issues or missing types/members across its references? | cd bin\Debug && checkbinarycompat |
Is the report from the last checkbinarycompat run different from the checked-in baseline? | checkbinarycompat returns exit code 1 if any line differs — wire it into CI. |
| Validate many startup binaries that share one output directory | checkbinarycompat config.json — see batch mode. |
| Find unreferenced assemblies in a directory | refdump *.dll -g then read AssemblyGraph.txt (or open in MSBuild Structured Log Viewer's graph viewer). |
Where did X.dll get copied from? Which NuGet package shipped it? | Build with msbuild /bl (or capture the CI binlog), then ask binlogmcp: $copy <full path to X.dll> and $nuget X.dll. See Binlogs below. |
What's the transitive NuGet dependency chain that brought in version A of Foo? | Binlog — ask binlogmcp: $nuget Foo A (or $nuget project(MyApp.csproj) Foo). |
Does this .dll have a matching .pdb on this symbol server? | pdb Foo.dll http://symbol.server/ |
| What's the MVID of this assembly? | mvid foo.dll (or lbi foo.dll -mvid) |
| Is this assembly strong-name signed? | lbi foo.dll -sn |
The tools above answer what is on disk. They cannot answer how it got there — which Copy task produced this file, which NuGet package shipped it, what transitive dependency chain pulled in version 13.0.3 instead of 12.0.0. Those questions require an MSBuild binary log (.binlog):
/bl to your build: msbuild MySolution.sln /bl or dotnet build MySolution.sln /bl. It costs negligible build time and writes msbuild.binlog next to the build.binlogmcp MCP server (must be configured in your agent host). The two killer queries:
$copy <full path to X.dll> — explains where every copy of X.dll came from (producing task, project, NuGet source, content item, project reference).$nuget <package> [version] — full NuGet dependency graph; traces which <PackageReference> or transitive chain forced a specific version.Typical use: lbi/refdump/checkbinarycompat identify what is wrong on disk; the binlog explains why the build put it there. See llms.txt for the full playbook.
The sections below document C# library APIs for retrieving an assembly's MVID programmatically, not the CLI tools above. They're aimed at developers building their own tools. If you just want to print MVIDs on the command line, use
lbi -mvidand stop here.
Mono.Cecil stripped down to a single file to read an assembly's MVID without dependencies. Here are the various ways to retrieve the MVID of an assembly:
Just embed this file: https://github.com/KirillOsenkov/MetadataTools/blob/master/src/PEFile/ImageReader.cs
var mvid = ImageReader.ReadAssemblyMvid(filePath);
Cold run on a warm file: 7ms
Or see this sample from Roslyn: https://github.com/dotnet/roslyn/blob/1c98fe1fc6ef122cb3bb436aa90a0ea729fdcf37/src/Compilers/Core/MSBuildTask/MvidReader.cs#L13
https://www.nuget.org/packages/Mono.Cecil
var module = Mono.Cecil.ModuleDefinition.ReadModule(filePath);
var mvid = module.Mvid;
Cold run on a warm file: 16ms
This has the downside that it loads the assembly:
var assemblyName = AssemblyName.GetAssemblyName(filePath);
var assembly = Assembly.Load(assemblyName);
var mvid = assembly.ManifestModule.ModuleVersionId;
Cold run on a warm file: 33ms
https://www.nuget.org/packages/System.Reflection.Metadata
Sample in Roslyn: https://github.com/dotnet/roslyn/blob/1c98fe1fc6ef122cb3bb436aa90a0ea729fdcf37/src/Compilers/Core/Portable/AssemblyUtilities.cs#L84
using (var stream = File.OpenRead(filePath))
{
PEReader reader = new PEReader(stream);
var metadataReader = reader.GetMetadataReader();
var mvidHandle = metadataReader.GetModuleDefinition().Mvid;
var mvid = metadataReader.GetGuid(mvidHandle);
}
Cold run on a warm file: 76ms
BenchmarkDotNet paints quite a different performance picture:
Method | Mean | StdDev |
--------------------- |------------ |---------- |
ReadUsingReflection | 208.2023 us | 0.4036 us |
ReadUsingSRM | 98.9620 us | 0.3674 us |
ReadUsingCecil | 153.1947 us | 0.3085 us |
ReadUsingImageReader | 147.0230 us | 1.2618 us |
1,406 followers · starred Apr 2019
221 followers · starred Jun 2025
498 followers · starred Jun 2025
184 followers · starred Jul 2023