Skip to content

Instantly share code, notes, and snippets.

@jpcy
Last active October 26, 2022 21:13
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpcy/8646147 to your computer and use it in GitHub Desktop.
Save jpcy/8646147 to your computer and use it in GitHub Desktop.
Notes on embedding Mono on Windows

Missing mono-2.0.dll

The current (3.2.3) windows binary distribution is missing mono-2.0.dll, so it must be build from source.

Get Mono source

Using Cygwin, follow these instructions

Possible errors

"configure: error: msgfmt not found. You need to install the 'gettext' package, or pass --enable-nls=no to configure." Need cygwin package gettext-devel and probably gettext too.

PEXECUTION_STATE redefined with different type. Change typedef PVOID PEXECUTION_STATE; to typedef PDWORD PEXECUTION_STATE; in C:\cygwin\usr\i686-pc-mingw32\sys-root\mingw\include\ddk\ntapi.h

machine.config permission error. Change the permission to read/write.

Output location

The missing "mono-2.0.dll" will be in "mono\mini.libs", rename either libmonosgen-2.0.dll or libmonoboehm-2.0.dll, depending on which GC you want.

Getting exception information

Error checking omitted for briefness.

static const char *GetStringProperty(const char *propertyName, MonoClass *classType, MonoObject *classObject)
{
	MonoProperty *messageProperty;
	MonoMethod *messageGetter;
	MonoString *messageString;

	messageProperty = mono_class_get_property_from_name(classType, propertyName);
	messageGetter = mono_property_get_get_method(messageProperty);
	messageString = (MonoString *)mono_runtime_invoke(messageGetter, classObject, NULL, NULL);
	return mono_string_to_utf8(messageString);
}
MonoObject *exceptionObject = NULL;

returnObject = mono_runtime_invoke(method, classObject, args, &exceptionObject);

if (exceptionObject)
{
	MonoClass *exceptionClass;
	MonoType *exceptionType;
	const char *typeName, *message, *source, *stackTrace;

	exceptionClass = mono_object_get_class(exceptionObject);
	exceptionType = mono_class_get_type(exceptionClass);
	typeName = mono_type_get_name(exceptionType);
	message = GetStringProperty("Message", exceptionClass, exceptionObject);
	source = GetStringProperty("Source", exceptionClass, exceptionObject);
	stackTrace = GetStringProperty("StackTrace", exceptionClass, exceptionObject);
}

Or just use the _MonoException struct in metadata/object-internals.h

If using unmanaged thunks, cast MonoException * to MonoObject * to call mono_object_get_class.

Correct source files and line numbers

Call mono_debug_init(MONO_DEBUG_FORMAT_MONO);

If building with Visual Studio, pdb files must to converted to mdb (Mono format). See here. This is easiest done as a post build step. If Mono is in the PATH environment variable, it's as simple as pdb2mdb "$(TargetPath)". Note that pdb2mdb takes the assembly name as a parameter, expects assembly.pdb to be in the same directory, and outputs assembly.dll.mdb not assembly.mdb.

Unmanaged thunks

The best documentation is in mono/metadata/object.c

Misc.

To access raw struct/class data from a MonoObject *:

typedef struct
{
	int id
	MonoString *name;
	MonoBoolean enabled;
}
MyStruct;

static void MyInternalCallFunction(MonoObject *object)
{
	const MyStruct *myStruct = (const MyStruct *)((uint8_t *)object + sizeof(MonoObject));
}

C# classes must use [StructLayout(LayoutKind.Sequential)], data structures must match etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment