通过 Visual Studio 制作 c++ dll 动态库

制作c++ dll 动态库(vs 2019)

  • 现有如下文件(做了个简单的例子)
    在这里插入图片描述

  • 打开visual studio 2019 创建 new c++ DLL
    通过 Visual Studio 制作 c++ dll 动态库_第1张图片

  • 为了过程简单将pch文件删了(注意修改设置)
    通过 Visual Studio 制作 c++ dll 动态库_第2张图片

  • 创建TestDll.h,TestDLL.cpp,并添加Base.h,BaseTool.dll, BaseTool.lib 文件进入工程通过 Visual Studio 制作 c++ dll 动态库_第3张图片

  • Base.h

    typedef int* BaseObj;
    typedef BaseObj DataA;
    typedef BaseObj DataB;
    
    void createDataA(DataA* dataA);  //  **dataA = 10
    void createDataB(DataB* dataB);  //  **dataB = 0
    void deleteObj(BaseObj data);   // delete x
    
    void operationA(DataA* dataA);  //  **dataA += 5
    void operationB(DataB* dataB);  // **dataB += 5
    void operationAB(DataA* dataA, DataB* dataB);  // **dataA += 5; **dataB +=5;
    

    为了演示结果简单写的一个例子。

  • TestDLL.h

    #include "Base.h"
    
    class ToolManager {
    public:
        ToolManager();   // a = 10; b =5;
      	~ToolManager();
    
    	void opA();  // a += 5;  b += 10;
        void opB();  // b += 10; a += 5 
    	int getA();  // return a
    	int getB();  // return b
    
    private:
        DataA m_dataA;
    	DataB m_dataB;
    };
    
  • TestDLL.cpp

    #include "TestDll.h"
    
    ToolManager::ToolManager(){
        createDataA(&m_dataA);
    	createDataB(&m_dataB);
    }
    ToolManager::~ToolManager()
    {
    	deleteObj(m_dataA);
    	deleteObj(m_dataB);
    }
    void ToolManager::opA(){
    	operationA(&m_dataA);
    	operationAB(&m_dataA, &m_dataB);
    }
    void ToolManager::opB() {
    	operationB(&m_dataB);
    	operationAB(&m_dataA, &m_dataB);
    }
    int ToolManager::getA() {
    	return *m_dataA;
    }
    int ToolManager::getB() {
    	return *m_dataB;
    }
    

    功能部分已经组装完成了,之后需要将dll动态库中的方法导出让C#进行调用。

  • 创建DLLExport.h 和 DLLExport.cpp 文件写导出方法。
    通过 Visual Studio 制作 c++ dll 动态库_第4张图片

  • DLLExport.h

    #ifdef BUILD_DLL
    #define EXPORT_DLL extern "C" _declspec(dllexport)
    #else
    #define EXPORT_DLL extern "C" _declspec(dllimport)
    #endif //BUILD_DLL
    
    EXPORT_DLL void __stdcall createTestDllCall(void*& tdCall);
    EXPORT_DLL void __stdcall cleanTestDllCall(void*& tdCall);
    EXPORT_DLL void __stdcall opA(void*& tdCall, int* a, int* b);
    EXPORT_DLL void __stdcall opB(void*& tdCall, int* a, int* b);
    
  • DLLExport.cpp

    #include "DLLExport.h"
    #include "TestDLL.h"
    
    void __stdcall createTestDllCall(void*& tdCall) {
        tdCall = new ToolManager();
    }
    
    void __stdcall cleanTestDllCall(void*& tdCall) {
        delete tdCall;
    }
    
    void __stdcall opA(void*& tdCall, int* a, int* b) {
        ToolManager* td = (ToolManager*)tdCall;
        td->opA();
        *a = td->getA();
        *b = td->getB();
    }
    
    void __stdcall opB(void*& tdCall, int* a, int* b) {
        ToolManager* td = (ToolManager*)tdCall;
        td->opB();
        *a = td->getA();
        *b = td->getB();
    }
    

    _declspec(dllexport) 声明一个导出函数,是说这个函数要从本DLL导出。
    _declspec(dllimport) 声明一个导入函数,说明这个函数是从其他DLL导入的。
    extern "C" 告诉编译器按照C语言的风格编译。如果不加,可能会使导出函数名出现乱码的情况。
    __stdcall 前面导出函数用__stdcall进行了声明,指明了函数调用的规则,C#中默认采用的就是这种方式(CallingConvention=CallingConvention.StdCall),使用了P/Invoke调用方法,所以最好在DLL导出函数中显式声明,或者在C#中显式修改调用规则。

    #ifdef BUILD_DLL
    #define EXPORT_DLL extern "C" _declspec(dllexport)
    #else
    #define EXPORT_DLL extern "C" _declspec(dllimport)
    #endif //BUILD_DLL
    

    定义宏标记导出函数和导入函数。记得将BUILD_DLL添加至预处理定义
    通过 Visual Studio 制作 c++ dll 动态库_第5张图片

    • Build 之后生成.dll文件

至此c++ dll动态库已经制作完成,在中间遇到了一些问题总结了一些经验,如果有错误或者更好的方案欢迎交流。
注意:

  1. dll 导出函数尽量使用c语言类型 不要用std::string, vector。
    不同模块参数传递内存管理会对程序的维护带来额外的负担,如std::string 和 vector。友情提示:字符串的传递用char**不要用string!
  2. dll 不要导出c++的类
    如果需要用到c++内部的类,建议使用void**或者void*&将类对象指针导出。在需要调用类中方法的导出函数中将类作为参数传入实现(void*)。在C#通过IntPtr进行接收。
  3. 内存管理
    同一模块c++dll内部的资源管理内存管理应在内部实现,不要丢到外部。
  4. 关于结构体
    结构体可以作为传出参数,通过结构体指针实现。但是过于复杂的情况不建议使用结构体。例如结构体嵌套结构体,结构体指针数组,结构体包含多维数组。在导出的时候会造成很大的麻烦。与其想着导出结构体。不如修改数据结构。
  5. 数组导出问题
    C#不能接收数组,但是可以将数组的首位指针和数组长度传入,在dll内部通过指针后移赋值。但是可能会存在问题。提供一种方案,如果需要输出一个数组或者多维数组。在dll内部生成这个数组,提供一个函数去访问这个数组。(void getArr(int x, int y, void* value);)
  6. 多个dll嵌套的
    如果需要调用一个dll,需要把这个dll依赖的所有dll都引入。

你可能感兴趣的:(.net)