1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! Dynamic bindings to the Mono runtime for Windows.
//!
//! This crate attaches to a Mono runtime that is **already loaded in the current process** — it
//! does not start a new JIT domain. The intended use case is process injection into Unity games
//! and other Mono-hosted applications, where the Mono DLL is already mapped into memory before
//! any code in this crate runs.
//!
//! # Initialization
//!
//! Before using any type in this crate, call [`init`] with the name of the Mono module as it
//! appears in the host process. Common values:
//!
//! - `"mono.dll"` — Unity 2017 and earlier (legacy Mono)
//! - `"mono-2.0-bdwgc.dll"` — Unity 2018+ (Boehm GC)
//! - `"mono-2.0.dll"` — standalone Mono installations
//!
//! [`init`] uses `GetModuleHandleW` internally, so the DLL must already be loaded; it does not
//! call `LoadLibrary`.
//!
//! # Threading model
//!
//! Mono requires every thread that calls into the runtime to be registered with the garbage
//! collector. Use [`MonoThreadGuard::attach`] to register the current thread before making any
//! Mono API calls. The guard automatically deregisters the thread when dropped.
//!
//! All handle types (`MonoClass`, `MonoObject`, …) are `!Send + !Sync`. They are bound to the
//! thread on which they were obtained and cannot be moved to another thread without explicit
//! `unsafe` code. This mirrors the per-thread attachment requirement: a handle is only valid on
//! an attached thread, and the compiler prevents it from silently crossing that boundary.
//!
//! See [`MonoThreadGuard`] for the full contract.
//!
//! # Usage
//!
//! ```no_run
//! use mono_rt::prelude::*;
//!
//! // resolve exports from the already-loaded Mono DLL
//! mono_rt::init("mono-2.0-bdwgc.dll")?;
//!
//! // attach the current thread — keep the guard live for the duration of all Mono work
//! let _guard = unsafe { MonoThreadGuard::attach()? };
//!
//! // navigate the assembly graph
//! let image = MonoImage::find("Assembly-CSharp")?.expect("assembly not loaded");
//! let class = image.class_from_name("", "Player")?.expect("class not found");
//!
//! // enumerate all fields and print their names and types
//! for field in class.fields()? {
//! let name = field.name()?;
//! let kind = field.mono_type()?.and_then(|t| t.kind().ok());
//! println!("{name}: {kind:?}");
//! }
//!
//! // read a field value directly from a live object
//! let hp_field = class.field("m_health")?.expect("field not found");
//! let offset = hp_field.offset()?;
//! // obj_ptr is a *mut c_void obtained from a previous MonoObject::as_ptr() call
//! // let hp: f32 = unsafe { mono_rt::read_field(obj_ptr, offset) };
//! # Ok::<(), MonoError>(())
//! ```
use ;
use ;
use MonoBindings;
pub use ;
pub use MonoThreadGuard;
pub use ;
/// Commonly used types, re-exported as a single glob import.
///
/// ```rust,no_run
/// use mono_rt::prelude::*;
/// ```
static BINDINGS: = new;
/// Resolves the Mono API by locating exports in the named module.
///
/// The module must already be loaded in the current process — this function calls
/// `GetModuleHandleW`, not `LoadLibraryW`. Call this once, as early as possible in your
/// injected code, before any thread attaches or any handle is created.
///
/// # Errors
///
/// - [`MonoError::DllNotFound`] if no module with `module_name` is currently loaded.
/// - [`MonoError::FnNotFound`] if a required export is missing from the module (unexpected for
/// standard Mono builds).
/// - [`MonoError::AlreadyInitialized`] if `init` has already been called successfully.
/// Returns the resolved Mono API, or [`MonoError::Uninitialized`] if [`init`] has not been
/// called.
///
/// This is an internal function called by every Mono operation. It is not exposed publicly so
/// that callers cannot bypass the thread-attachment requirement.
pub
/// Reads a field of type `T` from a Mono object at the given byte offset.
///
/// Use [`MonoClassField::offset`](crate::MonoClassField::offset) to obtain the correct offset for
/// a field. The read uses `read_unaligned` because Mono's field layout does not guarantee that
/// fields are aligned to their natural boundary.
///
/// # Safety
///
/// - `obj` must be a valid, non-null `MonoObject*` whose class declares a field of type `T` at
/// `offset`.
/// - The current thread must be attached to the Mono runtime via [`MonoThreadGuard`] — the GC
/// must not relocate `obj` while this function is executing.
/// - `T` must have the same size and representation as the Mono field type (e.g. `f32` for a
/// `System.Single` field).
pub unsafe
/// Writes a value of type `T` into a field of a Mono object at the given byte offset.
///
/// The mirror of [`read_field`]. Use [`MonoClassField::offset`](crate::MonoClassField::offset) to
/// obtain the correct offset.
///
/// # Safety
///
/// Same requirements as [`read_field`]. Additionally, writing a reference-type field (e.g. a
/// field whose [`TypeKind`] is [`Class`](TypeKind::Class) or [`Object`](TypeKind::Object))
/// bypasses the GC write barrier and will cause memory corruption if the GC uses a generational
/// or incremental collection scheme. For reference fields, prefer invoking a managed setter via
/// [`MonoMethod::invoke`](crate::MonoMethod::invoke).
pub unsafe