c++加载多个c#编译的dll
加载成功后 我调用接口的时候总是改变另外一个对象的地址信息
mono_mgr.h
#pragma once
#include "apptimer.h"
#include "callback_host.h"
#include "concurrency_queue.h"
#include "future.h"
#include "toolkit/singletontemplate.h"
#include <map>
#include <memory>
namespace protomsg
{
class CombatCheckData;
}
class MonoObj;
class MonoMgr
{
public:
MonoMgr();
~MonoMgr();
bool Init();
void Release();
void TestcallFunc();
void TestcallFunc2();
bool Check3Xiao(protomsg::CombatCheckData check_data);
public:
void PMSetCheckFlag(bool is_check);
private:
void getAllFiles(std::string path, std::vector<std::string>& files);
private:
MonoImage* image_ptr_;
MonoDomain* domain_ptr_;
//MonoObject* replay_data_obj_ptr_; // 对应c#的类对象
MonoObject* replay_verify_handler_obj_ptr_;
MonoObject* replay_driver_obj_ptr_;
bool is_check_;
std::map<std::string, std::shared_ptr<MonoObj>> map_mono_;
};
using MonoMgrInst = ChaSingleton<MonoMgr>;
mono_mgr.cpp
#include "stdafx.h"
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <corecrt_io.h>
#include "check3xiao_server_config.h"
#include "google/protobuf/util/json_util.h"
#include "mono_mgr.h"
#include "msg_common_combat_check.pb.h"
#include "mono_obj.h"
MonoMgr::MonoMgr()
: image_ptr_(nullptr)
, domain_ptr_(nullptr)
//, replay_data_obj_ptr_(nullptr)
, replay_verify_handler_obj_ptr_(nullptr)
, replay_driver_obj_ptr_(nullptr)
, is_check_(true)
{
}
MonoMgr::~MonoMgr()
{
mono_jit_cleanup(domain_ptr_);
image_ptr_ = nullptr;
domain_ptr_ = nullptr;
//replay_data_obj_ptr_ = nullptr;
replay_verify_handler_obj_ptr_ = nullptr;
replay_driver_obj_ptr_ = nullptr;
}
void MonoMgr::getAllFiles(std::string path, std::vector<std::string>& files)
{
//文件句柄
intptr_t hFile = 0;
struct _finddata_t fileinfo;
std::string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
//判断是否为文件夹
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
files.push_back(p.assign(path).append("/").append(fileinfo.name)); //保存文件夹名字
}
}
}
while (_findnext(hFile, &fileinfo) == 0);
//寻找下一个,成功返回0,否则-1
_findclose(hFile);
}
}
bool MonoMgr::Init()
{
mono_set_dirs("./", "./");
// 是否需要三消检测
is_check_ = ServerConfigInst::Singleton()->GetCheckFlag();
LOGINFO("init sucess, is_check:{}", is_check_);
std::vector<std::string> vec_dirs;
getAllFiles("./Dll", vec_dirs);
MonoDomain * domain_ptr_ = mono_jit_init("test");
//MonoDomain* child = mono_domain_create_appdomain("ddddd", NULL);
for (auto it: vec_dirs)
{
int64_t nPos = it.find_last_of('/');
std::string key = it.substr(nPos+1);
auto mono_obj = std::make_shared<MonoObj>(it);
mono_obj->Init(nullptr, key);
map_mono_[key] = mono_obj;
}
return true;
}
void MonoMgr::Release() {}
// 测试调用函数
void MonoMgr::TestcallFunc()
{
//MonoClass* replay_verify_handler_class = mono_object_get_class(replay_verify_handler_obj_ptr_);
//CHECK(replay_verify_handler_class);
//protomsg::CombatCheckData aaa;
//aaa.set_stage_id(10101);
//aaa.set_seed(324);
//aaa.set_stage_type(protomsg::CAMPAIGN);
//aaa.set_combat_type(protomsg::PVE);
//aaa.set_board_x(1);
//aaa.set_board_y(1);
//std::string szTest= "222";
//aaa.SerializeToString(&szTest);
//void* args[1];
//MonoArray* data = mono_array_new(domain_ptr_, mono_get_byte_class(), szTest.size());
//for (auto i = 0; i < szTest.size(); i++)
//{
// mono_array_set(data, uint8_t, i, szTest.at(i));
//}
//args[0] = data;
//MonoMethod* handler_method = mono_class_get_method_from_name(replay_verify_handler_class, "BytesToReplayData", 1);
//CHECK(handler_method);
//MonoObject* ret_mono_obj = mono_runtime_invoke(handler_method, replay_verify_handler_obj_ptr_, args, nullptr);
//CHECK(ret_mono_obj);
//MonoClass* ret_mono_class = mono_object_get_class(ret_mono_obj);
//CHECK(ret_mono_class);
////ret_mono_class
//MonoMethod* replay_driver_method = mono_class_get_method_from_name(replay_verify_handler_class, "IsSuccess", 2);
//CHECK(replay_driver_method);
//bool ret = false;
//void* p2[2];
//p2[0] = ret_mono_obj;
//p2[1] = &ret;
//mono_runtime_invoke(replay_driver_method, replay_verify_handler_obj_ptr_, p2, nullptr);
}
// 测试调用2
void MonoMgr::TestcallFunc2()
{
//MonoClass* mono_class = mono_object_get_class(replay_data_obj_ptr_);
//bool e = false;
//float f = 93.4f;
//void* p[2];
//p[0] = &e;
//p[1] = &f;
//MonoMethod* mono_method = mono_class_get_method_from_name(mono_class, "TestBaseTypeParameter", 2);
//if (mono_method != nullptr)
//{
// mono_runtime_invoke(mono_method, replay_data_obj_ptr_, p, nullptr);
//}
}
// 三消检测
bool MonoMgr::Check3Xiao(protomsg::CombatCheckData check_data)
{
LOGINFO("begin check3xiao, is_check:{}", is_check_);
// 如果不需要检测直接返回true
if (!is_check_)
{
LOGINFO("set check 3xiao is false, so return true!");
return true;
}
std::string key = check_data.logic_version() + "_" + check_data.conf_version();
auto mono_it = map_mono_.find(key);
if (mono_it == map_mono_.end())
{
LOGERR("cant find this mono obj, key:{}", key.c_str());
return false;
}
auto mono_ptr = mono_it->second;
if (mono_ptr == nullptr)
{
LOGERR("mono ptr is nil, key name:{}", key.c_str());
return false;
}
return mono_ptr->Check3Xiao(check_data);
}
void MonoMgr::PMSetCheckFlag(bool is_check)
{
is_check_ = is_check;
}
mono_obj.h
#pragma once
namespace protomsg
{
class CombatCheckData;
}
class MonoObj
{
public:
MonoObj(std::string path);
~MonoObj();
bool Init(MonoDomain* domain_ptr_, std::string dll_name);
bool Check3Xiao(protomsg::CombatCheckData check_data);
private:
bool LoadMonoClass();
MonoObject* CreateMonoClass(std::string space_name, std::string class_name);
void LoadClientConf();
private:
std::string dir_path_;
MonoDomain* domain_ptr_;
MonoImage* image_ptr_;
MonoObject* replay_verify_handler_obj_ptr_;
MonoAssembly* assembly_;
};
mono_obj.cpp
#include "stdafx.h"
#include "mono_obj.h"
#include "check3xiao_server_config.h"
#include "msg_common_combat_check.pb.h"
#include <mono/metadata/threads.h>
MonoObj::MonoObj(std::string path)
: dir_path_(path)
, domain_ptr_(nullptr)
, image_ptr_(nullptr)
, replay_verify_handler_obj_ptr_(nullptr)
{
}
MonoObj ::~MonoObj()
{
mono_jit_cleanup(domain_ptr_);
domain_ptr_ = nullptr;
image_ptr_ = nullptr;
replay_verify_handler_obj_ptr_ = nullptr;
}
bool MonoObj ::Init(MonoDomain* domain_ptr, std::string dll_name)
{
//auto mono_domain_ptr = mono_domain_create();
domain_ptr_ = mono_domain_create_appdomain(dir_path_.data(), NULL);
mono_domain_set(domain_ptr_, false);
mono_thread_attach(domain_ptr_);
//mono_domain_set(domain_ptr_, true);
//domain_ptr_ = mono_domain_create();
std::string lib_dll_name = dir_path_ + "/PuzzleRpg.dll";
assembly_ = mono_domain_assembly_open(domain_ptr_, lib_dll_name.c_str());
if (assembly_ == nullptr)
{
LOGERR("open c# dll failed dll name:{}", lib_dll_name.c_str());
return false;
}
image_ptr_ = mono_assembly_get_image(assembly_);
if (image_ptr_ == nullptr)
{
LOGERR("mono_assembly_get_image falied!");
return false;
}
// 映射c#的类
CHECKF(LoadMonoClass());
// 加载c#格式的配置信息
LoadClientConf();
return true;
}
bool MonoObj::Check3Xiao(protomsg::CombatCheckData check_data)
{
//replay_verify_handler_obj_ptr_ = CreateMonoClass("PuzzleRpg.Driver", "ReplayVerifyHandler");
//mono_domain_get_id
MonoClass* replay_verify_handler_class = mono_object_get_class(replay_verify_handler_obj_ptr_);
CHECKF(replay_verify_handler_class);
std::string serial_data = "";
check_data.SerializeToString(&serial_data);
void* args[1];
MonoArray* data = mono_array_new(domain_ptr_, mono_get_byte_class(), serial_data.size());
for (auto i = 0; i < serial_data.size(); i++)
{
mono_array_set(data, uint8_t, i, serial_data.at(i));
}
args[0] = data;
MonoMethod* handler_method = mono_class_get_method_from_name(replay_verify_handler_class, "BytesToReplayData", 1);
CHECKF(handler_method);
MonoObject* ret_mono_obj = mono_runtime_invoke(handler_method, replay_verify_handler_obj_ptr_, args, nullptr);
CHECKF(ret_mono_obj);
MonoClass* ret_mono_class = mono_object_get_class(ret_mono_obj);
CHECKF(ret_mono_class);
MonoMethod* replay_driver_method = mono_class_get_method_from_name(replay_verify_handler_class, "IsSuccess", 2);
CHECKF(replay_driver_method);
bool ret = false;
void* args2[2];
args2[0] = ret_mono_obj;
args2[1] = &ret;
mono_runtime_invoke(replay_driver_method, replay_verify_handler_obj_ptr_, args2, nullptr);
LOGINFO("check 3xiao res:{}", ret);
return ret;
}
bool MonoObj::LoadMonoClass()
{
CHECKF(domain_ptr_);
CHECKF(image_ptr_);
// 获取c#类对象都要先这样处理一下
replay_verify_handler_obj_ptr_ = CreateMonoClass("PuzzleRpg.Driver", "ReplayVerifyHandler");
CHECKF(replay_verify_handler_obj_ptr_);
return true;
}
// 创建类对象
MonoObject* MonoObj::CreateMonoClass(std::string space_name, std::string class_name)
{
CHECKN(domain_ptr_);
CHECKN(image_ptr_);
MonoClass* testcsharp_ptr = mono_class_from_name(image_ptr_, space_name.c_str(), class_name.c_str());
CHECKN(testcsharp_ptr, space_name.c_str(), class_name.c_str());
MonoObject* obj = mono_object_new(domain_ptr_, testcsharp_ptr);
CHECKN(obj);
mono_runtime_object_init(obj);
return obj;
}
// 加载客户端配置
void MonoObj::LoadClientConf()
{
//mono_domain_set(domain_ptr_, false);
MonoClass* replay_verify_handler_class = mono_object_get_class(replay_verify_handler_obj_ptr_);
CHECK(replay_verify_handler_class);
MonoMethod* replay_verify_handler_method = mono_class_get_method_from_name(replay_verify_handler_class, "Init", 2);
CHECK(replay_verify_handler_method);
void* p[2];
std::string conf_path = dir_path_ + "/config";
std::string log_path = dir_path_ + "/log";
//std::string log_path = ServerConfigInst::Singleton()->GetClientLogPath();
p[0] = mono_string_new(domain_ptr_, conf_path.c_str());
p[1] = mono_string_new(domain_ptr_, log_path.c_str());
mono_runtime_invoke(replay_verify_handler_method, replay_verify_handler_obj_ptr_, p, nullptr);
}
运行结果: 创建第二个对象的时候改变了第一个对象成员变量的信息
第一个元素信息:
第二个元素初始化完毕时第一个元素内的成员变量的地址变更了
我自己查到是执行这一句的时候导致的
我想要的结果是 每个对象之间不会有关联, 对象调用接口的时候不会影响到另外一个对象的地址信息
不要做A语言代码修改为B语言代码的无用功。
也不要做用A语言代码直接调用B语言代码库这样复杂、这样容易出错的傻事。
只需让A、B语言代码的输入输出重定向到文本文件,或修改A、B语言代码让其通过文本文件输入输出。
即可很方便地让A、B两种语言之间协调工作。
比如:
A将请求数据写到文件a.txt,写完后改名为aa.txt
B发现aa.txt存在时,读取其内容,调用相应功能,将结果写到文件b.txt,写完后删除aa.txt,再将b.txt改名为bb.txt
A发现bb.txt存在时,读取其内容,读完后删除bb.txt
以上A可以替换为任何一种开发语言或开发环境,B可以替换为任何一种与A不同的开发语言或开发环境。
除非A或B不支持判断文件是否存在、文件读写和文件更名。
但是谁又能举出不支持判断文件是否存在、文件读写和文件更名的开发语言或开发环境呢?
可以将临时文件放在RamDisk上提高效率减少磨损磁盘。
数据的结构很复杂的话,文本文件的格式问题可参考json或xml
共享临时文本文件这种进程之间的通讯方法相比其它方法的优点有很多,下面仅列出我现在能想到的:
·进程之间松耦合
·进程可在同一台机器上,也可跨机,跨操作系统,跨硬件平台,甚至跨国。
·方便调试和监视,只需让第三方或人工查看该临时文本文件即可。
·方便在线开关服务,只需删除或创建该临时文本文件即可。
·方便实现分布式和负载均衡。
·方便队列化提供服务,而且几乎不可能发生队列满的情况(除非硬盘空间满)
·……
“跨语言、跨机,跨操作系统,跨硬件平台,跨国,跨*.*的”苦海无边,
回头是“使用共享纯文本文件进行信息交流”的岸!