I am relatively new to C++, and have copied some code from a live stream to get started with embedding C# mono into my game engine.
I have created a C# script:
using System;
namespace Neutron {
public class Main {
public float FloatVar { get; set; }
public Main() {
Console.WriteLine("Main constructor");
}
public void PrintMessage() {
Console.WriteLine("Hello World from C#!");
}
public void PrintCustomMessage(string message) {
Console.WriteLine($"C# says: {message}");
}
}
}
and built it to
../Sandbox/Sandbox/bin/Debug/Sandbox.dll
(which i have verified within the c++ program that I am embedding it into)
when i call mono_runtime_object_init
passing the MonoObject* created from calling mono_object_new
(I have verified that it doesnt return a nullptr)
The following error occurs:
* Assertion at object.c:116, condition `is_ok (error)' not met, function:mono_runtime_object_init, (null) assembly:/usr/lib/mono/4.5/mscorlib.dll type:TypeInitializationException member:(null)
Followed by a long stack trace.
Here are my relavent files:
ScriptingEngine.cpp
(See the InitMono function towards the bottom)
//
// Created by aw1lt on 05/12/22.
//
#include "ScriptingEngine.h"
#include "Logger.h"
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <fstream>
namespace Neutron {
struct ScriptEngineData {
MonoDomain* RootDomain = nullptr;
MonoDomain* AppDomain = nullptr;
MonoAssembly* CoreAssembly = nullptr;
};
char* ReadBytes(const std::string& filepath, uint32_t* outSize) {
std::ifstream stream(filepath, std::ios::binary | std::ios::ate);
if (!stream) {
// Failed to open the file
return nullptr;
}
std::streampos end = stream.tellg();
stream.seekg(0, std::ios::beg);
uint32_t size = end - stream.tellg();
if (size == 0) {
// File is empty
return nullptr;
}
char* buffer = new char[size];
stream.read((char*)buffer, size);
stream.close();
*outSize = size;
return buffer;
}
MonoAssembly* LoadCSharpAssembly(const std::string& assemblyPath) {
uint32_t fileSize = 0;
char* fileData = ReadBytes(assemblyPath, &fileSize);
// NOTE: We can't use this image for anything other than loading the assembly because this image doesn't have a reference to the assembly
MonoImageOpenStatus status;
MonoImage* image = mono_image_open_from_data_full(fileData, fileSize, 1, &status, 0);
if (status != MONO_IMAGE_OK) {
const char* errorMessage = mono_image_strerror(status);
Logger::Crit(errorMessage);
return nullptr;
}
MonoAssembly* assembly = mono_assembly_load_from_full(image, assemblyPath.c_str(), &status, 0);
mono_image_close(image);
// Don't forget to free the file data
delete[] fileData;
return assembly;
}
void PrintAssemblyTypes(MonoAssembly* assembly) {
MonoImage* image = mono_assembly_get_image(assembly);
const MonoTableInfo* typeDefinitionsTable = mono_image_get_table_info(image, MONO_TABLE_TYPEDEF);
int32_t numTypes = mono_table_info_get_rows(typeDefinitionsTable);
for (int32_t i = 0; i < numTypes; i++) {
uint32_t cols[MONO_TYPEDEF_SIZE];
mono_metadata_decode_row(typeDefinitionsTable, i, cols, MONO_TYPEDEF_SIZE);
const char* nameSpace = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAMESPACE]);
const char* name = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAME]);
Logger::Log(std::string(nameSpace) + "." + name);
}
}
static ScriptEngineData* s_Data;
void ScriptingEngine::Init() {
s_Data = new ScriptEngineData();
InitMono();
}
void ScriptingEngine::Shutdown() {
delete s_Data;
}
void ScriptingEngine::InitMono(std::string path) {
MonoDomain* rootDomain = mono_jit_init("NeutronJITRuntime");
Logger::Assert(rootDomain != nullptr);
//system(("cat " + path).c_str());
// Store the root domain pointer
s_Data->RootDomain = rootDomain;
// Create an App Domain
s_Data->AppDomain = mono_domain_create_appdomain("NeutronScriptRuntime", nullptr);
mono_domain_set(s_Data->AppDomain, true);
s_Data->CoreAssembly = LoadCSharpAssembly(path);
Logger::Assert(s_Data->CoreAssembly != nullptr);
MonoImage* assemblyImage = mono_assembly_get_image(s_Data->CoreAssembly);
MonoClass* monoClass = mono_class_from_name(assemblyImage, "Neutron", "Main");
Logger::Assert(monoClass != nullptr);
PrintAssemblyTypes(s_Data->CoreAssembly);
MonoObject* instance = mono_object_new(s_Data->AppDomain, monoClass);
Logger::Assert(instance != nullptr);
mono_runtime_object_init(instance); // << ERROR HAPPENS HERE
}
void ScriptingEngine::ShutdownMono() {
}
} // Neutron
- my header file,
ScriptingEngine.h
//
// Created by aw1lt on 05/12/22.
//
#ifndef NEUTRONENGINE_SCRIPTINGENGINE_H
#define NEUTRONENGINE_SCRIPTINGENGINE_H
#include <string>
namespace Neutron {
class ScriptingEngine {
public:
static void Init();
static void Shutdown();
private:
static void InitMono(std::string path = "../Sandbox/Sandbox/bin/Debug/Sandbox.dll");
static void ShutdownMono();
};
} // Neutron
#endif //NEUTRONENGINE_SCRIPTINGENGINE_H
I also tried the answers on this page.
Thank you, and sorry if this answer is a mess.