在开发中可能会碰到这样的情况:有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包中。

2 条评论

欢迎留言