For Developers
Read Inventor and STEP metadata from your own .NET code with the zero-dependency InventorMeta library.
The desktop app and the command line are both thin front-ends over a small core library,
InventorMeta. It's a net10.0 library with zero external dependencies - drop it into your
own app, service or Inventor add-in and read Inventor and STEP files directly.
The packaged viewer ships on the Microsoft Store; to build any of the three projects yourself, read on.
Build
# core library + CLI (cross-platform)
dotnet build src/InventorMeta.Cli -c Release
# the WinUI desktop viewer (Windows only)
dotnet build src/InventorMeta.App -c Release -r win-x64ExtrabbitCode.Inventor.MetaReader.slnx
└─ src/
├─ InventorMeta/ core library (the parser) - net10.0
├─ InventorMeta.Cli/ command-line tool ("invmeta") - net10.0
└─ InventorMeta.App/ WinUI 3 desktop viewer - net10.0-windowsOpen a document
Everything hangs off one class, InventorDocument. Parsing happens in the constructor; the result
is a read-only snapshot.
using ExtrabbitCode.Inventor.MetaReader;
var doc = new InventorDocument(@"C:\path\SamplePart.ipt");
doc.FileName; // "SamplePart.ipt"
doc.DocumentType; // "Inventor Part (.ipt)"
doc.Kind; // DocKind.Part | Assembly | Drawing | Presentation | UnknownKind comes from the file's root identifier, so it's correct even if the extension is wrong. To
guard against non-Inventor files first:
if (!CompoundFile.LooksLikeCompoundFile(path)) return;Read properties
// the common iProperties, resolved for you
foreach (var kv in doc.Summary)
Console.WriteLine($"{kv.Key,-20} {kv.Value}");
string partNumber = doc.Summary["Part Number"];
// or every property in every set
foreach (var p in doc.Properties)
Console.WriteLine($"[{p.Set}] {p.Name} = {p.Display}");Mass / Volume / Surface Area / Density are cached values and can be stale until Inventor recomputes mass properties - check the Valid Mass Props flag before trusting them. See How it works.
References, model states, thumbnail, JSON
foreach (var r in doc.References) Console.WriteLine(r);
if (doc.HasModelStates)
foreach (var s in doc.ModelStateDetails)
Console.WriteLine($"{s.Name}{(s.IsActive ? " (active)" : "")}");
if (doc.Thumbnail != null)
File.WriteAllBytes("preview." + doc.ThumbnailExt, doc.Thumbnail);
string json = doc.ToJson();STEP files
STEP files (.stp / .step) go through the same class - the constructor sniffs the format, so
you don't have to care which kind of file you were handed:
var doc = new InventorDocument(@"C:\path\bracket.step");
doc.IsStep; // true
doc.DocumentType; // "STEP model (.stp/.step)"
// the ISO-10303 header and a geometry summary arrive as regular property sets
foreach (var p in doc.Properties) // [STEP Header] Author = ..., [STEP Geometry Summary] ...
Console.WriteLine($"[{p.Set}] {p.Name} = {p.Display}");For lower-level access, StepFile.Read returns the parsed header and per-entity counts directly,
and StepFile.LooksLikeStepFile(path) is the cheap format check (also exposed as
InventorDocument.IsStepFile).
The raw container
For lower-level access, CompoundFile parses the OLE Compound File container directly - enumerate
the directory, read any stream:
using var cf = new CompoundFile(path);
Console.WriteLine($"CFB v{cf.MajorVersion}, {cf.SectorSize}-byte sectors");
byte[] ufrx = cf.ReadStream("/UFRxDoc");To understand what these streams and property sets are, read How it works.