• Unity调用C++ dll的那些坑


    目录

    一、项目背景

    二、C++SDK的封装

    1.第一版SDK

    2.第二版SDK

    (1)修改SDKCaller.h

    (2)AvatarDriver.h

    (3)AvatarDriver.cpp

    三、Unity调用DLL


    一、项目背景

            最近在做一款虚拟人驱动项目,算法数据是以C++ SDK的形式封装起来的,那么这就涉及到了unity调用C++ dll。

    二、C++SDK的封装

            这个项目之前本人是有C++ SDK封装经验的,所以一些细节以及遵循的原则就不记录了。

    1.第一版SDK

            在和算法老师联调的过程中,封装的第一版dll的头文件SDKCaller.h是这样的:

    1. #pragma once
    2. #include
    3. #ifdef WIN32
    4. #ifdef DLL_EXPORTS
    5. #define EXPORT_CLASS __declspec(dllexport)
    6. #define EXPORT_API extern "C" __declspec(dllexport)
    7. #define EXPORT_CLASS_API
    8. #else
    9. #define EXPORT_CLASS __declspec(dllimport)
    10. #define EXPORT_API extern "C" __declspec(dllimport)
    11. #define EXPORT_CLASS_API
    12. #endif
    13. #else
    14. #define EXPORT_CLASS
    15. #define EXPORT_API extern "C" __attribute__((visibility("default")))
    16. #define EXPORT_CLASS_API __attribute__((visibility("default")))
    17. #endif
    18. class EXPORT_CLASS SDKCaller
    19. {
    20. public:
    21. EXPORT_CLASS_API SDKCaller(const char* config_path);
    22. public:
    23. // 初始化SDK
    24. EXPORT_CLASS_API int InitSDKBody(const char* model_path_det, const char* model_path_body);
    25. EXPORT_CLASS_API int InitSDKFace(const char* FaceDetect_modelpath, const char* FaceLandmarks_modelpath, const char* MouthLandmarks_modelpath, const char* FaceReconstruct_modelpath);
    26. EXPORT_CLASS_API int InitSDKHand(const char* det_model_path, const char* hand_mesh_model_path, int max_batch);
    27. EXPORT_CLASS_API int InitSDKSMPLX(const char* smplx_path);
    28. // 调用SDK
    29. EXPORT_CLASS_API int CallSDK(const unsigned char* buffer, const int height, const int width, bool start, bool end, int userinfo, char ** outputjson, int &len);
    30. EXPORT_CLASS_API void Destory(char* p);
    31. };

            根据我的经验,这个头文件及dll是没毛病的。但是给unity调用的话,这个类SDKCaller怎么映射到Unity中?第二版dll诞生~

    2.第二版SDK

            在Unity中调用C++写的类,dll直接按上面的方式导出是不行的。我们需要使用函数来代替类,也就是说在上述的类的基础上再封装一层,用函数将类包装起来。直接看源码吧:

    (1)修改SDKCaller.h

            SDKCaller.h这个头文件要被封装,那它就不对外提供了,所以也不需要加_declspec(dllexport)字段。

    1. #pragma once
    2. #include
    3. class SDKCaller
    4. {
    5. public:
    6. SDKCaller(const char* config_path);
    7. public:
    8. int InitSDKBody(const char* model_path_det, const char* model_path_body);
    9. int InitSDKFace(const char* FaceDetect_modelpath, const char* FaceLandmarks_modelpath, const char* MouthLandmarks_modelpath, const char* FaceReconstruct_modelpath);
    10. int InitSDKHand(const char* det_model_path, const char* hand_mesh_model_path, int max_batch);
    11. int InitSDKSMPLX(const char* smplx_path);
    12. int CallSDK(const unsigned char* buffer, const int height, const int width, bool start, bool end, int userinfo, char ** outputjson, int &len);
    13. void Destory(char* p);
    14. };

    (2)AvatarDriver.h

            AvatarDriver.h对上述的SDKCaller类进行了封装,这个头文件要对外暴露,所以需要加_declspec(dllexport)字段。

    1. #pragma once
    2. #ifdef WIN32
    3. #ifdef DLL_EXPORTS
    4. #define EXPORT_CLASS __declspec(dllexport)
    5. #define EXPORT_API extern "C" __declspec(dllexport)
    6. #define EXPORT_CLASS_API
    7. #else
    8. #define EXPORT_CLASS __declspec(dllimport)
    9. #define EXPORT_API extern "C" __declspec(dllimport)
    10. #define EXPORT_CLASS_API
    11. #endif
    12. #else
    13. #define EXPORT_CLASS
    14. #define EXPORT_API extern "C" __attribute__((visibility("default")))
    15. #define EXPORT_CLASS_API __attribute__((visibility("default")))
    16. #endif
    17. extern "C" {
    18. struct SDKCaller;
    19. EXPORT_CLASS_API SDKCaller* GenerateAISDK(const char* config_path);
    20. EXPORT_CLASS_API int AIInitSDKBody(SDKCaller* sdk,const char* model_path_det, const char* model_path_body);
    21. EXPORT_CLASS_API int AIInitSDKFace(SDKCaller* sdk, const char* FaceDetect_modelpath, const char* FaceLandmarks_modelpath, const char* MouthLandmarks_modelpath, const char* FaceReconstruct_modelpath);
    22. EXPORT_CLASS_API int AIInitSDKHand(SDKCaller* sdk, const char* det_model_path, const char* hand_mesh_model_path, int max_batch);
    23. EXPORT_CLASS_API int AIInitSDKSMPLX(SDKCaller* sdk, const char* smplx_path);
    24. EXPORT_CLASS_API int AICallSDK(SDKCaller* sdk, const unsigned char* buffer, const int height, const int width, bool start, bool end, int userinfo, char** outputjson, int& len);
    25. EXPORT_CLASS_API void AIDestory(SDKCaller* sdk, char* p);
    26. EXPORT_CLASS_API void AIReleaseAISDK(SDKCaller* sdk);
    27. }

    (3)AvatarDriver.cpp

            AvatarDriver.h只是函数声明,AvatarDriver.cpp才是函数实现,函数内部是对SDKCaller类的成员函数的调用。

    1. #include "SDKCaller.h"
    2. #include "AvatarDriver.h"
    3. SDKCaller* GenerateAISDK(const char* config_path)
    4. {
    5. return new SDKCaller(config_path);
    6. }
    7. inline int AIInitSDKBody(SDKCaller* sdk, const char* model_path_det, const char* model_path_body)
    8. {
    9. return sdk->InitSDKBody(model_path_det, model_path_body);
    10. }
    11. inline int AIInitSDKFace(SDKCaller* sdk, const char* FaceDetect_modelpath, const char* FaceLandmarks_modelpath, const char* MouthLandmarks_modelpath, const char* FaceReconstruct_modelpath)
    12. {
    13. return sdk->InitSDKFace(FaceDetect_modelpath, FaceLandmarks_modelpath, MouthLandmarks_modelpath, FaceReconstruct_modelpath);
    14. }
    15. inline int AIInitSDKHand(SDKCaller* sdk, const char* det_model_path, const char* hand_mesh_model_path, int max_batch)
    16. {
    17. return sdk->InitSDKHand(det_model_path, hand_mesh_model_path, max_batch);
    18. }
    19. inline int AIInitSDKSMPLX(SDKCaller* sdk, const char* smplx_path)
    20. {
    21. return sdk->InitSDKSMPLX(smplx_path);
    22. }
    23. inline int AICallSDK(SDKCaller* sdk, const unsigned char* buffer, const int height, const int width, bool start, bool end, int userinfo, char** outputjson, int& len)
    24. {
    25. return sdk->CallSDK(buffer, height, width, start, end, userinfo, outputjson, len);
    26. }
    27. inline void AIDestory(SDKCaller* sdk, char* p)
    28. {
    29. sdk->Destory(p);
    30. }
    31. inline void AIReleaseAISDK(SDKCaller* sdk)
    32. {
    33. delete sdk;
    34. }

    三、Unity调用DLL

            我们需要将dll(及其依赖的dll)放入到Unity项目Plugins文件夹下,这个熟悉Unity的人都知道~俺就不细说了。那C++与C#如何映射呢?我们需要用到DllImport命令引入dll(该SDK名称为Avatar_driver.dll),并将AvatarDriver.h以C#语言的方式进行声明。

    C++C#
    SDKCaller* (自定义类的指针)IntPtr
    const char*IntPtr
    const unsigned char*IntPtr
    作为出参的char**ref IntPtr
    int&ref int
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Runtime.InteropServices;
    5. using UnityEngine;
    6. public class AIDLL : MonoBehaviour
    7. {
    8. [DllImport("avatar_driver")]
    9. public static extern IntPtr GenerateAISDK(IntPtr config_path);
    10. [DllImport("avatar_driver")]
    11. public static extern int AIInitSDKBody(IntPtr sdk, IntPtr model_path_det, IntPtr model_path_body);
    12. [DllImport("avatar_driver")]
    13. public static extern int AIInitSDKFace(IntPtr sdk, IntPtr FaceDetect_modelpath, IntPtr FaceLandmarks_modelpath, IntPtr MouthLandmarks_modelpath, IntPtr FaceReconstruct_modelpath);
    14. [DllImport("avatar_driver")]
    15. public static extern int AIInitSDKHand(IntPtr sdk, IntPtr det_model_path, IntPtr hand_mesh_model_path, int max_batch);
    16. [DllImport("avatar_driver")]
    17. public static extern int AIInitSDKSMPLX(IntPtr sdk, IntPtr smplx_path);
    18. [DllImport("avatar_driver")]
    19. public static extern int AICallSDK(IntPtr sdk, IntPtr buffer, int height, int width, bool start, bool end, int userinfo, ref IntPtr outputjson, ref int len);
    20. [DllImport("avatar_driver")]
    21. public static extern void AIDestory(IntPtr sdk, IntPtr p);
    22. [DllImport("avatar_driver")]
    23. public static extern void AIReleaseAISDK(IntPtr sdk);
    24. }

            接下来我们就能在unity中进行C++接口的调用咯~

    • 字符串转IntPtr,需要用到函数Marshal.StringToHGlobalAnsi()
    • IntPtr转字符串,需要用到函数Marshal.PtrToStringAnsi()
    1. IntPtr configPath = Marshal.StringToHGlobalAnsi(Application.streamingAssetsPath + "/config_test.ini");
    2. IntPtr sdk = AIDLL.GenerateAISDK(configPath);
    1. System.IntPtr result = new System.IntPtr();
    2. AIDLL.AICallSDK(GameManager.sdk, pixelPointer, webCamTexture.height, webCamTexture.width, false, false, userInfoSet, ref result, ref len);
    3. string jsonStr = Marshal.PtrToStringAnsi(result);

  • 相关阅读:
    每日一题:连续子数组的最大和(动态规划)
    论文阅读:ECAPA-TDNN
    uniapp小程序接入腾讯云【增强版人脸核身接入】
    JVM(一):jvm中的数据结构(内存模型):Java Virtual Machine Specification Runtime Data Areas
    代理IP与Socks5代理的技术奇妙之旅
    纳米软件介绍什么是LABVIEW?
    国庆中秋特辑(三)使用生成对抗网络(GAN)生成具有节日氛围的画作,深度学习框架 TensorFlow 和 Keras 来实现
    ky10 server arm 在线编译安装openssl3.1.4
    C++优先队列的使用
    kdxf speex
  • 原文地址:https://blog.csdn.net/weixin_39766005/article/details/126369571