4-14 5,238
在开发中可能会碰到这样的情况:有c/c++源文件,需要在c#中调用c/c++相关接口,但在c#中没法直接调用c/c++代码,我们可以先把c/c++代码编译成dll或so库文件,供c#代码调用。
1. 生成dll文件(windows下使用),下面是一个gid和uid相互转换的demo,需要把这两个接口导出给c#调用。
#ifdef __cplusplus extern "C" { #endif __declspec(dllexport) char* gidToUid(unsigned long long ullGID); __declspec(dllexport) unsigned long long uidToGid(const char* uid); #ifdef __cplusplus } #endif
然后使用vs2013导出dll库文件,右键项目-属性-配置属性-常规-项目默认值-配置类型,选择动态库(.dll),重新生成解决方案,会在Debug目录下生成需要的dll文件。
2. 生成so文件(Android使用)
#ifdef __cplusplus extern "C" { #endif char* gidToUid(unsigned long long ullGID); unsigned long long uidToGid(const char* uid); #ifdef __cplusplus } #endif
首先需要在一个android项目下创建jni目录,然后把c代码拷贝到该目录下。然后创建编译c代码用的配置文件Android.mk和Application.mk:
Android.mk
LOCAL_PATH := $(call my-dir) #定义函数,递归返回给定目录下所有目录 all_directory = \ $(eval item_all = $(wildcard $1/*))\ $(eval item_all = $(item_all:%.def=))\ $(eval item_all = $(item_all:%.dsp=))\ $(eval item_all = $(item_all:%.dsw=))\ $(eval item_all = $(item_all:%.exe=))\ $(eval item_all = $(item_all:%.opt=))\ $(eval item_all = $(item_all:%.plg=))\ $(eval item_all = $(item_all:%.rc=))\ $(eval item_all = $(item_all:%.rc2=))\ $(eval item_all = $(item_all:%.bat=))\ $(eval item_all = $(item_all:%.aps=))\ $(eval item_all = $(item_all:%.clw=))\ $(eval item_all = $(item_all:%.vcproj=))\ $(eval item_all = $(item_all:%.ico=))\ $(eval item_all = $(item_all:%.user=))\ $(eval item_all = $(item_all:%.bak=))\ $(eval item_all = $(item_all:%.plist=))\ $(eval item_all = $(item_all:%.pch=))\ $(eval item_all = $(item_all:%.mm=))\ $(eval item_all = $(item_all:%.m=))\ $(eval item_all = $(item_all:%.strings=))\ $(eval item_all = $(item_all:%.sln=))\ $(eval item_all = $(item_all:%.cmd=))\ $(eval item_all = $(item_all:%.suo=))\ $(eval item_all = $(item_all:%.prj=))\ $(eval item_all = $(item_all:%.sln=))\ $(eval item_all = $(item_all:%.psess=))\ $(eval item_all = $(item_all:%.msc=))\ $(eval item_all = $(item_all:%.spc=))\ $(eval item_all = $(item_all:%.in=))\ $(eval item_all = $(item_all:%.m4=))\ $(eval item_all = $(item_all:%.cxx=))\ $(eval item_all = $(item_all:%.xml=))\ $(eval item_all = $(item_all:%.cpp.pre=))\ $(eval item_all = $(item_all:%.c.pre=))\ $(eval item_all = $(item_all:%.h.pre=))\ $(eval item_all = $(item_all:%.H=))\ $(eval item_all = $(item_all:%.inl=))\ $(eval item_all = $(item_all:%.ini=))\ $(eval item_all = $(item_all:%.xmlcpy=))\ $(eval item_all = $(item_all:%.xmlcp=))\ $(eval item_all = $(item_all:%.inc=))\ $(eval item_all = $(item_all:%.tli=))\ \ $(eval item_all = $(item_all:%.cpp=))\ $(eval item_all = $(item_all:%.mk=))\ $(eval item_all = $(item_all:%.a=))\ $(eval item_all = $(item_all:%.so=))\ $(eval item_all = $(item_all:%.o=))\ $(eval item_all = $(item_all:%.d=))\ $(eval item_all = $(item_all:%.obj=))\ $(eval item_all = $(item_all:%.txt=))\ $(eval item_all = $(item_all:%.c=))\ $(eval item_all = $(item_all:%.cmake=))\ $(eval item_all = $(item_all:%.tlog=))\ $(eval item_all = $(item_all:%.log=))\ $(eval item_all = $(item_all:%.idb=))\ $(eval item_all = $(item_all:%.pdb=))\ $(eval item_all = $(item_all:%.html=))\ $(eval item_all = $(item_all:%.jce=))\ $(eval item_all = $(item_all:%.mm=))\ $(eval item_all = $(item_all:%.m=))\ $(eval item_all = $(item_all:%.lib=))\ $(eval item_all = $(item_all:%.h=))$1/ $(item_all)\ $(foreach item, $(item_all) $(),\ $(call all_directory, $(item))\ ) #定义函数,递归返回给定目录下所有C++源文件 all_cpp_files_recursively = \ $(eval src_files = $(wildcard $1/*.cpp)) \ $(eval src_files = $(src_files:$(LOCAL_PATH)/%=%))$(src_files) \ $(eval item_all = $(wildcard $1/*)) \ $(foreach item, $(item_all) $(),\ $(eval item := $(item:%.cpp=%)) \ $(call all_cpp_files_recursively, $(item))\ ) #定义函数,递归返回给定目录下所有C源文件 all_c_files_recursively = \ $(eval src_files = $(wildcard $1/*.c)) \ $(eval src_files = $(src_files:$(LOCAL_PATH)/%=%))$(src_files) \ $(eval item_all = $(wildcard $1/*)) \ $(foreach item, $(item_all) $(),\ $(eval item := $(item:%.c=%)) \ $(call all_c_files_recursively, $(item))\ ) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := uid_tool LOCAL_MODULE_FILENAME := libuidtool LOCAL_SRC_FILES := $(call all_c_files_recursively,$(LOCAL_PATH)/test) LOCAL_SRC_FILES += $(call all_cpp_files_recursively,$(LOCAL_PATH)/test) LOCAL_C_INCLUDES := $(LOCAL_PATH)/test LOCAL_LDFLAGS+= -Xlinker --allow-multiple-definition include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_STL := gnustl_static
关于Android.mk可以参考http://codingnow.cn/android/1623.html
打开命令提示符窗口,输入ndk-build,会在项目的libs/armeabi目录下生成libuidtool.so文件。
到此为止,库文件就准备好了,接下在c#中调用,调用代码比较简单,主要是c#和c++参数类型的匹配,比如这里c++中的unsigned long long,对应c#的ulong。c++中char*输入对应c#的StringBuilder,输出对应IntPtr。网上有很多资料可以详细的了解。
using System.Runtime.InteropServices; using System.Text; //在类中添加下面代码: //GID转换成UID [DllImport("uidtool", EntryPoint = "gidToUid")] extern static System.IntPtr gidToUid(ulong ullGID); //UID转换成GID [DllImport("uidtool", EntryPoint = "uidToGid")] extern static ulong uidToGid(StringBuilder uid); //调用: System.IntPtr intPtr = gidToUid(gID); string uid = Marshal.PtrToStringAnsi(intPtr); UIDLabel.text = uid; StringBuilder uidStr = new StringBuilder(uid); ulong gid = uidToGid(uidStr);
ps:
1. 需要把dll文件拷贝到Unity项目根目录下(跟Assets同级),否则会找不到该dll。
2. 把so文件拷贝到Assets\Plugins\Android目录下,如果是拷贝到Andorid目录下的自定义目录(Andorid/test/libs/xx.so),需要在test目录下添加project.properties,project.properties内容:android.library=true,表示这是一个Android库文件,unity打包的时候才会把test/libs中的库文件打包到apk包中。
受教了!呵呵!
http://www.lqmt8.com