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.
Related
I have the following C++ API
#ifndef AGUR_H
#define AGUR_H
#define AGUR_EXPORT __attribute__((visibility("default")))
#include <cstdint>
extern "C"
{
AGUR_EXPORT void sphericalRelativePoseSolveFromFile(
const char* filePathStatic,
const char* filePathDynamic,
const char* solverConfigJson,
char** solverSolutionJson);
AGUR_EXPORT void sphericalRelativePoseSolve(
const char* rawImageStatic,
const char* rawImageDynamic,
int imageWidth,
int imageHeight,
const char* solverConfigJson,
char** solverSolutionJson);
}
#endif // AGUR_H
My implementation for the first method above (and the one I am concerned with) is
void sphericalRelativePoseSolveFromFile(
const char* filePathStatic,
const char* filePathDynamic,
const char* solverConfigJson,
char** solverSolutionJson)
{
json cj = json::parse(solverConfigJson);
types::SolverConfiguration solverConfig = cj.get<types::SolverConfiguration>();
std::string sfp = std::string(filePathStatic);
std::string dfp = std::string(filePathDynamic);
agur::sfm::SphericalRelativePoseSolver sphericalRelativePoseSolver(solverConfig);
types::SolverSolution solverSolution = sphericalRelativePoseSolver.solve(sfp, dfp);
const json sj{ solverSolution };
std::string jsonString = sj[0].dump();
char* tmp = (char*)jsonString.c_str();
size_t len = std::strlen(tmp) + 1;
*solverSolutionJson = (char*)malloc(len);
std::memcpy((void*)*solverSolutionJson, tmp, len);
}
I call this API from C++ as followws and the results are what I expect and want.
std::string ss = "/Alignment/Theta_R0010732_2048.jpg";
const char* staticFilePath = ss.c_str();
std::string ds = "/Alignment/Theta_R0010733_2048.jpg";
const char* dynamicFilePath = ds.c_str();
const char* solverConfigJson =
"{"
"\"acRansacMaxThreshold\": 4.0,"
"\"outputEssentialGeometry\": true,"
"\"outputFeatures\": true,"
"\"outputInliers\": true,"
"\"outputMatches\": false,"
"\"unstableSolutionInlierThreshold\": 60,"
"\"useUprightRelativePoseSolver\": true"
"}";
char solverSolutionJson[] = "";
char* ptrSolverSolutionJson = solverSolutionJson;
sphericalRelativePoseSolveFromFile(staticFilePath, dynamicFilePath, solverConfigJson, &ptrSolverSolutionJson);
std::cout << ptrSolverSolutionJson << std::endl;
This prints out the expected solver results as jsomn - great. However, I now want to call the API from a Unity project. The Unity code so far is
using System;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Networking;
public class AgurIvocation : MonoBehaviour
{
private string _status = string.Empty;
#if UNITY_IOS || UNITY_TVOS
// On iOS plugins are statically linked into
// the executable, so we have to use __Internal as the
// library name.
private const string Import = "__Internal";
#else
// Other platforms load plugins dynamically, so pass the
private const string Import = "agur";
#endif
[DllImport(Import)]
private static unsafe extern void sphericalRelativePoseSolveFromFile(
string filePathStatic,
string filePathDynamic,
string solverConfigJson,
out string[] solverSolutionJson);
[DllImport(Import)]
private static unsafe extern void sphericalRelativePoseSolve(
string rawImageStatic,
string rawImageDynamic,
int imageWidth,
int imageHeight,
string solverConfigJson,
out string[] solverSolutionJson);
void Start()
{
try
{
var filePathStatic = "/Alignment/Theta_R0010732_2048.jpg";
var filePathDynamic = "/Alignment/Theta_R0010733_2048.jpg";
var solverConfigJson =
"{" +
"\"acRansacMaxThreshold\": 4.0," +
"\"outputEssentialGeometry\": true," +
"\"outputFeatures\": true," +
"\"outputInliers\": true," +
"\"outputMatches\": false," +
"\"unstableSolutionInlierThreshold\": 60," +
"\"useUprightRelativePoseSolver\": true" +
"}";
var solverSolutionJson = new string[] { };
sphericalRelativePoseSolveFromFile(filePathStatic, filePathDynamic, solverConfigJson, out solverSolutionJson);
Console.WriteLine(solverSolutionJson[0]);
}
catch (Exception e)
{
_status = e.ToString();
}
}
// Update is called once per frame
void Update()
{
}
private string UnpackAsset(string path)
{
#if !UNITY_ANDROID
return Path.Combine(Application.streamingAssetsPath, path);
#endif
var inputFilePath = Path.Combine(Application.streamingAssetsPath, path);
var outputFilePath = Path.Combine(Application.persistentDataPath, path);
var loadingRequest = UnityWebRequest.Get(inputFilePath);
loadingRequest.SendWebRequest();
while (!loadingRequest.isDone)
{
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
break;
}
}
if (!loadingRequest.isNetworkError && !loadingRequest.isHttpError)
{
File.WriteAllBytes(outputFilePath, loadingRequest.downloadHandler.data);
return outputFilePath;
}
string errorMessage = $"Unpacking asset failed: {path}, reason: {loadingRequest.error}";
throw new Exception(errorMessage);
}
}
But I am getting nothing out of the solverSolutionJson variable. Simply, this is not working - can someone advise are to how this should be done from Unity/Mono please?
Question
I am trying to send a signal in linux to a thread in C# in dotnet core in Linux. My test code works with SIGARAM but does not work with a custom signal mapping to SIGRTMIN#. sigwait on a thread never returns when a main thread raises a custom signal.
C++ version works well. C++ and C# version are pretty much the same. Can anyone figure out why it does not work?
My ultimate goal is to catch a signal from a device driver.
C++ version
//https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/sigwaiti.htm
//g++ -Wall -O2 example.cpp -o example -pthread
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <thread>
void catcher( int sig ) {
printf( "Signal catcher called for signal %d\n", sig );
}
void timestamp( const char *str ) {
time_t t;
time( &t );
printf( "The time %s is %s\n", str, ctime(&t) );
}
void on_sigusr1(int sig)
{
// Note: Normally, it's not safe to call almost all library functions in a
// signal handler, since the signal may have been received in a middle of a
// call to that function.
printf("SIGUSR1 received!\n");
}
#define SIG_SYSTEM (SIGRTMIN+7)
void run()
{
struct sigaction sigact;
sigset_t waitset;
siginfo_t info;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction( SIG_SYSTEM, &sigact, NULL );
sigemptyset( &waitset );
sigaddset( &waitset, SIG_SYSTEM );
sigprocmask( SIG_BLOCK, &waitset, NULL );
timestamp( "before sigwaitinfo()" );
// int result = sigwaitinfo( &waitset, &info );
int sig;
int result = sigwait( &waitset, &sig );
if( sig == SIG_SYSTEM )
printf( "sigwaitinfo() returned for signal %d\n",
info.si_signo );
else {
printf( "sigwait() sig %d\n", sig );
printf( "sigwait() returned code %d\n", result );
printf( "sigwait() returned error number %d\n", errno );
perror( "sigwait() function failed\n" );
}
timestamp( "after sigwaitinfo()" );
}
int main( int argc, char *argv[] ) {
int result = 0;
signal(SIG_SYSTEM, &on_sigusr1);
sigset_t waitset;
sigemptyset( &waitset );
sigaddset( &waitset, SIG_SYSTEM );
sigprocmask( SIG_BLOCK, &waitset, NULL );
std::thread tx(run);
sleep(5);
//alarm( 5 );
//result = raise(SIG_SYSTEM);
result = kill(getpid(), SIG_SYSTEM);
printf( "kill(%d) %d\n", SIG_SYSTEM, result);
tx.join();
return( result );
}
C# version
However a test program in C# does not work.
using System;
using static Tmds.Linux.LibC;
using Tmds.Linux;
using System.Runtime.InteropServices;
delegate void SignalCallback(int sig);
namespace example
{
class Program
{
static void catcher(int sig)
{
Console.WriteLine($"Signal catcher called for signal {sig}");
}
static void timestamp(string str)
{
Console.WriteLine($"The time {str} is {DateTime.Now}");
}
static System.Threading.Thread Thread;
static int SIG_SYSTEM = SIGRTMIN + 7;
static unsafe void Run()
{
int result = 0;
sigaction sigact = new sigaction();
sigset_t waitset = new sigset_t();
siginfo_t info = new siginfo_t();
sigset_t* ptr = &sigact.sa_mask;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
SignalCallback callback = new SignalCallback(catcher);
sigact.sa_handler = (void*)Marshal.GetFunctionPointerForDelegate(callback);
sigaction(SIG_SYSTEM, &sigact, null);
sigemptyset(&waitset);
sigaddset(&waitset, SIG_SYSTEM);
sigprocmask(SIG_BLOCK, &waitset, null);
timestamp("before sigwaitinfo()");
//result = sigwaitinfo(&waitset, &info);
int sig;
result = sigwait(&waitset, &sig);
// after it's last usage by unmanaged code
GC.KeepAlive(callback);
if (sig == SIG_SYSTEM)
Console.WriteLine($"sigwaitinfo() returned for signal {info.si_signo}");
else
{
Console.WriteLine($"sigwait() sig {sig}");
Console.WriteLine($"sigwait() returned code {result}");
Console.WriteLine($"sigwait() returned error number {errno}");
Console.WriteLine("sigwait() function failed");
}
timestamp("after sigwaitinfo()");
}
static unsafe void Main(string[] args)
{
sigset_t waitset = new sigset_t();
sigemptyset(&waitset);
sigaddset(&waitset, SIG_SYSTEM);
sigprocmask(SIG_BLOCK, &waitset, null);
Thread = new System.Threading.Thread(Run);
Thread.Start();
//alarm(10);
sleep(5);
int result = kill(getpid(), SIG_SYSTEM);
Console.WriteLine($"kill({SIG_SYSTEM}) {result}");
Thread.Join();
}
}
}
Environment
VisualStudio Professional 2019 16.9.4
dotnet core SDK 3.1
Ubuntu on WSL
Tmds.Linux 0.5.0
I was able to get a signal writing a small .so file.
static void (*_callback)(int, int);
void catcher(int sig, siginfo_t* info, void* context) {
int* sival_int = &info->si_int;
_callback(sig, sival_int[1]);
}
extern "C" void setaction(int sig, void *callback)
{
_callback = (void(*)(int, int))callback;
struct sigaction sigact;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = catcher;
sigaction(sig, &sigact, NULL);
}
delegate void SignalCallback(int sig, int info);
static SignalCallback cb = Callback;
[DllImport("signalhandle.so")]
static extern void setaction(int sig, SignalCallback callback);
//Call this on the main thread.
public static void Start()
{
setaction(SIG_SYSTEM, cb);
}
private static void Callback(int sig, int info)
{
}
c# code:
var nativeAutomation = new UIAutomationClient.CUIAutomation8();
nativeAutomation.AddPropertyChangedEventHandler(ele, UIA.TreeScope.TreeScope_Element, null, new handler(), pidarray);
the handler used in AddPropertyChangedEventHandler
class handler : UIAutomationClient.IUIAutomationPropertyChangedEventHandler
{
public void HandlePropertyChangedEvent(UIA.IUIAutomationElement src, int propertyId, object newValue)
{
UIA.IUIAutomationElement sourceElement = src as UIA.IUIAutomationElement;
Console.WriteLine(propertyId + ":" + newValue);
}
}
it works very well
but when i using c++:
#include "stdafx.h"
#include <Windows.h>
#include "test.h"
#include "cbt.h"
#include <UIAutomationClient.h>
#include <iostream>
#include <string>
#pragma comment(lib,"testDll.lib")
class A :public IUIAutomationPropertyChangedEventHandler {
ULONG m_ref;
public:
A() :m_ref(0)
{}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID &id, void** p) override {
// return IUnknown::QueryInterface(id,p);
REFIID d = { 0x0000001b,0x0000,0x0000, {0xC0,00,00,00,00,00,00,0x46} };// IdentityUnmarshal.
if (id == d) {
return E_NOINTERFACE;
}
*p = this;
return S_OK;
//return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef() override {
++m_ref;
//return IUnknown::AddRef();
return m_ref;
}
virtual ULONG STDMETHODCALLTYPE Release() override {
// return IUnknown::Release();
--m_ref;
if (!m_ref)
delete this;
return m_ref;
}
virtual HRESULT STDMETHODCALLTYPE HandlePropertyChangedEvent(
/* [in] */ __RPC__in_opt IUIAutomationElement *sender,
/* [in] */ PROPERTYID propertyId,
/* [in] */ VARIANT newValue) {
printf("dsdsdsdsddsd\n");
return S_OK;
};
};
int main()
{
// cbt::Instance();
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IUIAutomation* am = NULL;
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation8), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&am);
if (S_OK != hr)
hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&am);
A* a = new A;
std::string hx;
std::getline(std::cin, hx);
char* s = NULL;
HWND h = (HWND)strtol(hx.c_str(), &s, 16);
IUIAutomationElement* ele = NULL;
am->ElementFromHandle(h, &ele);
/*SAFEARRAY* sa = SafeArrayCreateVector(VT_I4, 0, 4);
LONG i = 0;
long pid = UIA_AutomationIdPropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 1;
pid = UIA_BoundingRectanglePropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 2;
pid = UIA_ClassNamePropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 3;
pid = UIA_NamePropertyId;
SafeArrayPutElement(sa, &i, &pid);
am->AddPropertyChangedEventHandler(ele, TreeScope_Element, NULL, p,sa );
SafeArrayDestroy(sa);*/
PROPERTYID *pids = new PROPERTYID[4];
pids[0] = UIA_AutomationIdPropertyId;
pids[1] = UIA_BoundingRectanglePropertyId;
pids[2] = UIA_ClassNamePropertyId;
pids[3] = UIA_NamePropertyId;
am->AddPropertyChangedEventHandlerNativeArray(ele, TreeScope_Element, NULL, a, pids, 4);
getchar();
CoUninitialize();
return 0;
}
so,it is very easy in c#.
but,with c++,i need to override Addref(), Release(), QueryInterface().
error occurs when call
am->AddPropertyChangedEventHandlerNativeArray(ele, TreeScope_Element, NULL, a, pids, 4);
looks like i should return a IMarshal object in the QueryInterface().
i think it needs a otherThread to loop events.
guys , how to code this IMarshal object?
ok,i got the answer at https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-howto-implement-event-handlers
my internet unstable these days.didn't see the page
I am attempting to stream data from a c++ application to a C# application using shared memory. Based on example I found, I have:
c++ (sending)
struct Pair {
int length;
float data[3];
};
#include <windows.h>
#include <stdio.h>
struct Pair* p;
HANDLE handle;
float dataSend[3]{ 22,33,44 };
bool startShare()
{
try
{
handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(Pair), L"DataSend");
p = (struct Pair*) MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(Pair));
return true;
}
catch(...)
{
return false;
}
}
int main()
{
if (startShare() == true)
{
while (true)
{
if (p != 0) {
//dataSend[0] += 1; // here the value doesn't refresh
for (int h = 0; h < 3; h++)
{
p->data[h] = dataSend[h];
}
//dataSend[0] += 1; // here it does
}
else
puts("create shared memory error");
}
}
if (handle != NULL)
CloseHandle(handle);
return 0;
}
C# (receiving)
namespace sharedMemoryGET
{
class Program
{
public static float[] data = new float[3];
public static MemoryMappedFile mmf;
public static MemoryMappedViewStream mmfvs;
static public bool MemOpen()
{
try {
mmf = MemoryMappedFile.OpenExisting("DataSend");
mmfvs = mmf.CreateViewStream();
return true;
}
catch
{
return false;
}
}
public static void Main(string[] args)
{
while (true)
{
if (MemOpen())
{
byte[] blen = new byte[4];
mmfvs.Read(blen, 0, 4);
int len = blen[0] + blen[1] * 256 + blen[2] * 65536 + blen[2] * 16777216;
byte[] bPosition = new byte[12];
mmfvs.Read(bPosition, 0, 12);
Buffer.BlockCopy(bPosition, 0, data, 0, bPosition.Length);
Console.WriteLine(data[0]);
}
}
}
}
}
The c++ side never updates the variable, making me think i have missed something in my if-loop. Additionally, is a always-running loop the best way to go here? Is there a way to 'request' the data somehow from the C# side, to make this a more efficient system? Thank you.
..Actually this is working, I had the update for the variable in the wrong place. I have edited and will leave the code for others.
I currently have a DLL written and there's a struct that I declared that'd be used by both the C# and C++ applications to store information in the memory map files.
#pragma pack(1)
typedef struct SHAREDMEMORY_API Data
{
public:
int getKind() {
return kind;
}
int getCountry() {
return country;
}
int getExtra() {
return extra;
}
char* getName(){
printf("Get name value in struct: %s\n", name);
return name;
}
void setKind(int sKind) {
kind = sKind;
}
void setCountry(int scountry) {
country = scountry;
}
void setName(char* sname){
name = new char[strlen(sname)];
strcpy(name, sname);
}
void deletePointer(){
delete[] name;
delete[] type;
delete[] band;
}
private:
int kind;
int country;
int extra;
char* name;
};
These are the methods in the DLL that I exposed so that my C++ exe and C# exe can call.
extern "C" void SetData(
int kind,
int country,
int extra,
char* name)
{
CMutex mutex;
int size = 1;
Data* dynamicArray = new Data[size];
if(m_pViewMMFFile)
{
Data* record = (Data*) m_pViewMMFFile; // Maps the memory mapped file as an Data object array
for (int i = 0; i < size; i++)
{
dynamicArray[i].setKind(kind);
printf("setKind: %i\n", kind);
dynamicArray[i].setCountry(country);
printf("setCountry: %i\n", country);
dynamicArray[i].setExtra(extra);
printf("setExtra: %i\n", extra);
dynamicArray[i].setName(name);
printf("setName: %s\n", name);
record[i] = dynamicArray[i];
}
record = dynamicArray;
delete[] dynamicArray;
}
mutex.~CMutex();
}
In my C++ main method, I tried to set the data and also tried to retrieve the information and there wasn't any problem in getting back the data that is has set previously. Here's the codes:
typedef Data* (*PGETDATAFUN)();
typedef void (*PSETDATAFUN)(int kind,
int country,
int extra,
char* name);
char buffer[4096];
int main(int argc, char** argv)
{
bool doneSetting = false;
HMODULE hModule = LoadLibrary
("MemoryMapDLL.dll");
PGETDATAFUN getfuncptr = NULL;
PSETDATAFUN setentityfuncptr = NULL;
if (hModule)
{
getfuncptr = (PGETDATAFUN) GetProcAddress
(hModule, "GetData");
setfuncptr = (PSETDATAFUN) GetProcAddress
(hModule, "SetData");
}
for(;;){
UDPServer::MyUDP myudp;
myudp.getUDP(buffer);
if (buffer[0] != 0) {
if (str == "Play") {
memset(&buffer[0], 0, sizeof(buffer));
try {
if (!getfuncptr)
{
cout << "Error retrieving entity data. \n";
}
else
{
if (!doneSetting){
setfuncptr(1000, 2000, 3000, "Woohyun");
doneSetting = true;
}
Sleep(1000);
Data* ed = (Data*)(getfuncptr());
printf("Address of Data: %i\n", ed);
printf("Size: %i\n", sizeof(Data));
printf("Kind of Remote Control: %i\n", ed->kind);
printf("Country of Remote Control: %i\n", ed->country);
printf("Extra of Remote Control: %i\n", ed->extra);
printf("Name of Remote Control: %s\n", ed->name);
}
} catch (exception& ex){
ofstream file;
file.open("D:\\log.txt");
file << "Exception found:" << "\n";
file << ex.what() << "\n";
file.close();
}
}
}
}
FreeLibrary(hModule);
return 0;
}
This is my corresponding struct in C# which is marshaled as a class.
using System;
using System.Collections.Generic;
class MarshalClass
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Data
{
public Int32 kind;
public Int32 country;
public Int32 extra;
public IntPtr name;
}
}
}
This is where I tried to do the marshalling in C#.
class Program
{
[DllImport("MemoryMapDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void SetData(
[MarshalAs(UnmanagedType.I4)] int kind,
[MarshalAs(UnmanagedType.I4)] int country,
[MarshalAs(UnmanagedType.I4)] int extra,
[MarshalAs(UnmanagedType.LPStr)] string name
);
[DllImport("MemoryMapDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static IntPtr GetData();
private void UdpMessageSending(string inputMessage)
{
UdpClient udpClient = new UdpClient(11000);
udpClient.Connect("127.0.0.1", 11000);
Console.WriteLine("Sending UDP Message: " + inputMessage);
// Sends a message to the host to which you have connected.
Byte[] sendBytes = Encoding.ASCII.GetBytes(inputMessage);
udpClient.Send(sendBytes, sendBytes.Length);
udpClient.Close();
}
static void Main(string[] args)
{
try
{
for (; ;)
{
string x = Console.ReadLine();
Program p = new Program();
p.UdpMessageSending("Play");
//SetData(1, 2, 225, 2, type, 4, 1, 0, name, 1, 4, band);
Thread.Sleep(1000);
IntPtr ptr = GetData();
Console.WriteLine("Pointer: " + ptr.ToString());
MarshalClass.Data data = (MarshalClass.Data)Marshal.PtrToStructure(ptr, typeof(MarshalClass.Data));
Console.WriteLine("Data Size in C#: " + Marshal.SizeOf(typeof(MarshalClass.Data)));
Console.WriteLine("Kind: " + data.kind);
Console.WriteLine("Country: " + data.country);
Console.WriteLine("Extra: " + data.extra);
IntPtr nameIntPtr = data.name;
Console.WriteLine("NameIntPtr: " + nameIntPtr);
string name = Marshal.PtrToStringAnsi(nameIntPtr);
if (name != null)
{
Console.WriteLine("Name: " + name);
}
}
}
catch (Exception exception)
{
Console.WriteLine("Error");
Console.WriteLine("===============================================");
Console.WriteLine(exception.ToString());
}
}
}
}
The problem came when I tried to retrieve the information on the C# side. It wasn't able to retrieve the char* name attribute (this happened when it tried to call the GetData method in the DLL where it'd throw a AccessViolationException error and crashes my application). It was able to retrieve the other attributes that are of integer types, however, without any problems. The same would happen if I tried to set the data in C# and C# would be able to retrieve it back perfectly but when my C++ application tried to retrieve the char* name, the application would crash. However, in my DLL, I was able to print out the address of the name data being stored, hence eliminating the possibility of it being null. So, what could be the possible cause for this? Does anyone know what's the solution for this?