您的位置:首页 > 编程语言 > C语言/C++

Android 平台C# 与C++之间的互相调用方法

2016-11-13 01:31 891 查看
Java与C++的互相调用很容易做到,有好几种方式实现,如 JNI , JNA 方式 ,而C# 与C++之间的相互调用就有点麻烦了,一般情况下是C#调用C++ ,C++却无法调用C# 。本人就是为了解决C++ 调用C#的问题,也就是可以实现C#调用C++,然后C++再回调C# 。本人搜索了大量的网络文章,却没有找到有效方法,偶然间在Steamwork.Net的源码里发现了
可行的方法,并经项目实际使用验证可行 。

具体实现例子请看下面的代码

C#端,回调实现:

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace IPInvoke
{
public class CallbackInvoke
{
static CallbackInvoke ()
{
}

internal const string NativeLibraryName = "NativeTest";

[DllImport(NativeLibraryName, EntryPoint = "RegisterCallback")]
public static extern void  RegCallback(IntPtr cbPtr);

[DllImport(NativeLibraryName, EntryPoint = "UnregisterCallback")]
public static extern void  UnregisterCallback();

[DllImport(NativeLibraryName, EntryPoint = "GetUser")]
public static extern void GetUser(int uid);

[DllImport(NativeLibraryName, EntryPoint = "RequestGameInfo")]
public static extern void RequestGameInfo (int gameId);

private static Dictionary<Type,IDisposable> mDisposes = new Dictionary<Type,IDisposable>();

public delegate void DispatchDelegate<T>(T param);
public static void RegisterCallback<T>(DispatchDelegate<T>  callback){

Type t = typeof(T);
IDisposable  r=new Callback<T>(callback);
if (mDisposes.ContainsKey (t)) {
IDisposable d=mDisposes [t];
if(d!=null)d.Dispose();
}
mDisposes [t] = r;

}

public sealed class Callback<T> :IDisposable {
private CCallbackBaseVTable VTable;
private IntPtr m_pVTable = IntPtr.Zero;
private GCHandle m_pCCallbackBase;
private CCallbackBase m_CCallbackBase;
private event DispatchDelegate<T> m_Func;

public Callback(DispatchDelegate<T> func) {
if (func == null) {
throw new Exception("Callback function must not be null.");
}

BuildCCallbackBase();

m_Func = func;
RegCallback(m_pCCallbackBase.AddrOfPinnedObject()); //传递被“钉住”的地址到C++
}

~Callback() {
Dispose();
}
public void Dispose(){
if (m_pVTable != IntPtr.Zero) {
Marshal.FreeHGlobal(m_pVTable);
m_pVTable=IntPtr.Zero;
}

if (m_pCCallbackBase.IsAllocated) {
m_pCCallbackBase.Free();
}
}

private void OnCallback(

IntPtr thisptr,

IntPtr pvParam) {
try {
m_Func((T)Marshal.PtrToStructure(pvParam, typeof(T)));
}
catch (Exception e) {

}
}

private void BuildCCallbackBase() {
VTable = new CCallbackBaseVTable() {
m_RunCallback = OnCallback,
};
m_pVTable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CCallbackBaseVTable)));
Marshal.StructureToPtr(VTable, m_pVTable, false);
m_CCallbackBase = new CCallbackBase() {
m_vfptr = m_pVTable,
m_iCallback = CallbackIdentities.GetCallbackIdentity(typeof(T))
};

m_pCCallbackBase = GCHandle.Alloc(m_CCallbackBase, GCHandleType.Pinned);  //手动进行内存管理,防止被GC ,不用的时候一定要进行释放
}
}

[StructLayout(LayoutKind.Sequential)]
private class CCallbackBase {
public IntPtr m_vfptr;	//对应对C++回调类中的成员函数
public int m_iCallback; //对应于C++回调类中的成员,标识回调的ID
};

[StructLayout(LayoutKind.Sequential)]
internal class CCallbackBaseVTable {

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void RunCBDel(IntPtr thisptr, IntPtr pvParam); //第一个参数thisptr 必须有,不然回调结果不对 ,不知为什么

[NonSerialized]
[MarshalAs(UnmanagedType.FunctionPtr)]
public RunCBDel m_RunCallback;

}

}
internal class CallbackIdentities {
public static int GetCallbackIdentity(Type callbackStruct) {
foreach (CallbackIdentityAttribute attribute in callbackStruct.GetCustomAttributes(typeof(CallbackIdentityAttribute), false)) {
return attribute.Identity;
}
throw new Exception("Callback number not found for struct " + callbackStruct);
}
}

[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
internal class CallbackIdentityAttribute : System.Attribute {
public int Identity { get; set; }
public CallbackIdentityAttribute(int callbackNum) {
Identity = callbackNum;
}
}
}


实现调用 如下:

using System;
using System.Runtime.InteropServices;
namespace IPInvoke
{
public class MainClass
{
public const  int  CALLBACK_TYPE_USER=1;
public const  int  CALLBACK_TYPE_GAME=2;

[StructLayout(LayoutKind.Sequential)]
[CallbackIdentity(CALLBACK_TYPE_USER)]
public struct User_t {
public uint userid;
public uint gid;
}
[StructLayout(LayoutKind.Sequential)]
[CallbackIdentity(CALLBACK_TYPE_GAME)]
public struct Game_t {
public uint id;
public uint price;
}

public static void onReceive(User_t u){
Console.WriteLine ("onReceive user id " + u.userid+", gid : "+u.gid);
}
public static void onReceiveGame(Game_t u){
Console.WriteLine ("onReceive Game id " + u.id+", price : "+u.price);
}

public static void Main (string[] args)
{

CallbackInvoke.RegisterCallback<User_t>(onReceive);
CallbackInvoke.RegisterCallback<Game_t>(onReceiveGame);

Console.WriteLine ("Hello World!");

CallbackInvoke.RequestGameInfo (100);
CallbackInvoke.GetUser (10000);

CallbackInvoke.UnregisterCallback ();
Console.WriteLine ("UnregisterCallback");

while (true)
;
}
}
}


C++端代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <thread>
#include <unistd.h>
using namespace std;

#define CALLBACK_MAX 20

#define  CALLBACK_TYPE_USER 1
#define  CALLBACK_TYPE_GAME 2

class ICallback
{
public:
virtual void OnCallback(void* data)=0;
int i_mCallback;
};

typedef struct _User_t{
uint32_t uid;
uint32_t gid;
}User_t;

typedef struct _Game_t{
uint32_t id;
uint32_t price;
}Game_t;

class ICallback  *mCallback[CALLBACK_MAX];

extern "C" void RegisterCallback(ICallback *intf)
{
printf("RegisterCallback i_mCallback %d \n",intf->i_mCallback);
mCallback[intf->i_mCallback]=intf;
}

extern "C" void UnregisterCallback()
{
for(int i=0;i<CALLBACK_MAX;i++){
mCallback[i]=NULL;
}

}

extern "C"  void RunCallbacks(){

}
extern "C" void GetUser(int uid){

printf("GetUser uid %d \n",uid);

if(mCallback[CALLBACK_TYPE_USER]!=NULL){
User_t user ;
user.uid=uid+1200;
user.gid=16;
mCallback[CALLBACK_TYPE_USER]->OnCallback(&user);

}
}
extern "C" void RequestGameInfo(int gameId){

printf("RequestGameInfo uid %d \n",gameId);

std::thread t= std::thread([=](){
sleep(1);
if(mCallback[CALLBACK_TYPE_GAME]!=NULL){
Game_t game ;
game.id=gameId;
game.price=150;
mCallback[CALLBACK_TYPE_GAME]->OnCallback(&game);

}
});
t.join();

}


编译可以用命令: g++ --std=c++11 InvokeTest.cpp -fPIC -shared -o libNativeTest.so

以上代码在linux下编译运行通过

Linux平台下安装 mono , 编译完成后,拷贝libNativeTest.so到C#编译后的exe文件目录下,然后命令运行 : mono
xxxx.exe

Windows还没测试。。。。。。

以上只是例子,实现使用还需要完善
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c# C++ mono 回调