Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
mono-rt
Dynamic bindings to the Mono runtime, designed for process injection into Unity games and other Mono-hosted applications on Windows.
Rather than starting a new JIT domain, this crate attaches to a Mono runtime that is already
running in the target process. All exports are resolved at runtime via GetModuleHandleW and
GetProcAddress, so no import library or compile-time link to mono.dll is needed.
Platform support
| Platform | Status |
|---|---|
| Windows | Supported |
| Linux | Planned but contributions welcome! |
The core binding layer is platform-agnostic; only init() uses a Windows-specific API to locate
the loaded module. A Linux port would replace that with dlopen/dlsym and is a self-contained
change.
Getting started
Add the dependency:
[]
= "0.1.0"
# or, for the latest commit:
= { = "https://github.com/theo-abel/mono-rt" }
Then in your injected code:
use *;
// 1. Resolve exports from the already-loaded Mono DLL.
// Common names: "mono.dll" (Unity <= 2017), "mono-2.0-bdwgc.dll" (Unity 2018+)
init?;
// 2. Attach the current thread. Keep the guard alive for the duration of all Mono work.
let _guard = unsafe ;
// 3. Navigate the assembly graph.
let image = find?.expect;
let class = image.class_from_name?.expect;
// 4. Look up a method and invoke it.
let method = class.method?.expect;
let domain = root?.expect;
let obj = class.new_object?.expect;
let result = unsafe ;
// 5. Read a field value directly from a live instance.
let hp_field = class.field?.expect;
let offset = hp_field.offset?;
// let hp: f32 = unsafe { mono_rt::read_field(obj_ptr, offset) };
Threading model
Mono requires every thread that calls into the runtime to be registered with the garbage
collector. MonoThreadGuard::attach() handles this registration, and the guard automatically
deregisters the thread when dropped.
All handle types (MonoClass, MonoObject, MonoMethod, ...) are !Send + !Sync, they are
bound to the thread on which they were obtained. The compiler prevents them from crossing thread
boundaries silently. If you do need to transfer a handle to another thread where you can
guarantee both threads are attached, you can opt in with an explicit unsafe impl Send.
Runtime API coverage
The table below reflects the current state. The goal is to cover the APIs most relevant to game modding and runtime inspection; lower-level or rarely-needed functions can be added as needed.
| Area | Covered | Not yet covered |
|---|---|---|
| Initialization | init, thread attach/detach, root domain |
domain unload, domain creation |
| Assembly | open by path, enumerate loaded, get image | get by name, get list from domain |
| Image | find by name, class lookup | enumerate all classes |
| Class | field/method by name, field/method enumeration, type descriptor, vtable, object allocation | parent class, interfaces, properties, events, nested types |
| Field | offset, name, type, static read | instance write, static write |
| Method | invoke (raw + typed Value), name |
full signature, parameter types, return type, flags |
| Object | unbox | get class, get type, clone |
| String | create from &str, convert to String |
|
| Array | length, element address | create, set element |
| Type | kind (TypeKind enum), boxing |
is_valuetype, get_class |
| GC | — | pinned handles (gc_handle_new/free) |
| Exceptions | surface as MonoError::ManagedException |
inspect message, stack trace |
Safety
unsafe appears in two places in the public API:
MonoThreadGuard::attach(): you assert that the returned guard will be dropped on the same thread that calledattach.MonoMethod::invoke/invoke_with: you assert that the object and argument types match the method's actual signature, which Mono does not validate at the call site.read_field/write_field: you assert that the offset and typeTare correct for the target field, and that the object pointer is live.
Everything else - null checks, CString conversion, error propagation - is handled by the library.
Credits
Some inspiration was drawn from the mono-rs project, particularly the public API design. Shoot out to Bartosz for paving the way!
License
GPL-3.0-only. See LICENSE.