Skip to main content
The CLR probe attaches to any running .NET application — including WinForms, WPF, Mission Planner, Visual Studio, and Microsoft Office — and reads its live heap state via a managed C# bootstrapper injected through the Windows CLR Hosting API. Once injected, the bootstrapper communicates with axon-daemon over a zero-copy Memory-Mapped File (MMF) channel, walking the CLR heap using managed reflection to produce a fully-typed SubstrateState. You do not need to deploy any DLL, modify any project file, or make any code changes to the target application.

Architecture

The CLR probe uses a two-process architecture: the native axon-daemon on one side, and a managed C# bootstrapper running inside the target application’s AppDomain on the other. They communicate over a named MMF channel.
Why MMF instead of named pipes? MMF communication is synchronous and zero-copy. The managed bridge writes the response directly into the shared memory region — no serialization overhead and no buffering latency. Round-trip latency for a typical field read is under 5ms.

Injection: No Code Changes Required

NSP uses the Windows CLR Hosting API (ICLRRuntimeHost) to load axon_clr_bridge.dll into an already-running process. From your perspective as an operator:
  • You do not deploy axon_clr_bridge.dll anywhere — the daemon carries it internally.
  • You do not add any reference, NuGet package, or startup hook to the target project.
  • You do not restart the application — injection happens into the live, running process.
  • You do not need the application’s source code or PDB files.
Injection via the CLR Hosting API requires that axon-daemon is running with sufficient privileges to open a handle to the target process. On Windows, this typically means running the daemon as Administrator or as the same user that launched the .NET application.

How CLR Heap Walk Works

After injection, the managed bootstrapper uses ICorProfiler-style reflection to walk the CLR heap:
1

Type enumeration

The bridge calls AppDomain.CurrentDomain.GetAssemblies() and iterates all loaded assemblies to enumerate every live type in the process.
2

Instance discovery

For each type of interest, the bridge locates live instances via GC root traversal and static field inspection.
3

Field extraction

FieldInfo.GetValue() reads the current value of each field on each live instance, including nested objects, collections, and JIT-compiled value types.
4

Normalization

Field values are serialized to SSF dot-notation keys (e.g. mavlink.interface.is_armed, flight_data.altitude) and written to the MMF response buffer for the daemon to consume.

Supported Applications

The CLR probe supports all .NET 4.x and .NET 6+ applications, including both WinForms and WPF desktop applications. The following have validated signature entries:
For Microsoft Office applications, state is read via the .NET COM Interop layer (VSTO / Office Interop). The CLR probe connects to the managed interop process — Office itself remains a native COM server, but its object model is fully accessible through the managed wrapper.

Confidence Baseline

The CLR probe achieves high confidence because .NET retains rich reflection metadata at runtime — assembly names, type names, field names, and method signatures are available even in release builds.

Reading State via the Python SDK


Invoking .NET Methods

The managed bridge supports full method invocation with automatic Python-to-.NET type coercion:

Type Coercion Reference


Error Reference

Native AOT compilation strips most reflection metadata from the output binary. The CLR probe can still read process memory, but confidence will be lower (0.50–0.70) because type and field names may not be available. Use the probe in read-only mode for Native AOT targets until the schema is enriched.
Yes. NSP’s CLR probe ships both x86 and x64 builds of axon_clr_bridge.dll. The daemon selects the correct architecture automatically based on the target process bitness.
Yes. WPF ViewModels that implement INotifyPropertyChanged are especially well-covered because their property names are preserved as string literals in the compiled assembly. The probe reads the backing fields directly and maps them to the declared property names.