c++相关函数和类的dll导入及导出(VScode)

函数的dll导入导出 

以sayHello()函数为例,如何把一个函数导出一个动态库

 目录文件结构如下

C++test1
    ├─HelloWorldApp
    │  ├─bin
    │  ├─build
    │  │  ├─bin
    │  │  └─CMakeFiles 
    │  └─src
    └─HelloWorldLib
        ├─bin
        ├─build
        │  └─CMakeFiles    
        ├─cmake
        │  ├─include
        │  └─lib
        ├─include
        └─src

在 HelloWorldLib下

编写cmakelist文件,头文件及src中HelloWorld.cpp源文件

此头文件处理来动态链接库(DLL)中的符号导出问题,同时确保了跨语言的兼容性。

#pragma once #确保此头文件只包含一次

#ifndef HELLOWORLD_EXPORTS#如果没定义
#define HELLOWORLD __declspec(dllimport)#就定义 从动态库导入进
#else#否则
#define HELLOWORLD __declspec(dllexport)#就定义 导出到动态库
#endif

extern "C"{
    HELLOWORLD void sayHelloWorld();
}

HELLOWORLD_EXPORTS 是一个宏,用于区分当前编译的是 DLL 本身(导出符号)还是使用 DLL 的其他项目(导入符号):如果没定义,就告诉编译器这个符号是从 DLL 导入的;如果定义了 HELLOWORLD_EXPORTS(通常是在构建 DLL 时),就告诉编译器这个符号应该被导出到 DLL。

C++ 中存在名称修饰,而C 编译器不会。如果库打算导出符号,就需要在链接时使用 extern "C" 来确保导出的符号不会被名称修饰。确保函数名在不同语言间保持一致,方便调用。这样,当你构建 DLL 时,相关的符号会被正确导出,而当其他项目链接这个 DLL 时,符号会被正确导入。 

此源文件引用函数并打印helloworld 

#include "HelloWorld.h"
#include <iostream>

HELLOWORLD void sayHelloWorld() {
    std::cout << "Hello World!" << std::endl;
}

 此cmakelist文件定义了如何使用 CMake 构建一个名为 "HelloWorldLibrary" 的动态链接库

cmake_minimum_required(VERSION 3.10)

# 项目名称和版本
project(HelloWorldLibrary)

# 指定自定义的库文件输出路径,该cmakelist文件构建一个dll动态库,这里指定输出路径存放的变量
#set(CUSTOM_LIB_PATH "C:\\Users\\Admin\\Desktop\\zhangshuhan\\LearnC++\\C++test1\\HelloWorldLib\\bin")

# 输出路径 LIBRARY_OUTPUT_PATH为生成库文件路径,为cmake内置变量

set(LIBRARY_OUTPUT_PATH "D:\\VSC\\C++test1\\C++test1\\HelloWorldLib\\cmake\\lib")  # 修改库的输出路径,dll放这里面

# 启用 C++11 支持
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 定义源文件
set(HELLO_WORLD_SOURCES src/HelloWorld.cpp)

# 包含头文件的路径的变量设置
set(HELLO_WORLD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)

# 包含头文件,这里输入头文件查找路径即可
include_directories(${HELLO_WORLD_INCLUDE_DIR})

add_definitions(-DHELLOWORLD_EXPORTS)

# 创建动态库,动态库名为Helloworld
add_library(Helloworld SHARED ${HELLO_WORLD_SOURCES})



# 消息输出
message(STATUS "HELLO_WORLD_INCLUDE_DIR: ${HELLO_WORLD_INCLUDE_DIR}")
message(STATUS "LIBRARY_OUTPUT_PATH: ${LIBRARY_OUTPUT_PATH}")

cmakelist关键部分可以概括如下:指定版本,设置项目名,设置dll输出路径( 生成的dll文件放在lib目录下),设置编译标准,设定一个变量包含源文件路径,设定头文件搜索路径,添加头文件的宏定义区分导入和导出,创建名为 "Helloworld" 的dll并用此前的变量指定源文件

在HelloWorldApp下

主函数导入dll库调用函数

#include <iostream>
#include "HelloWorld.h"

int main() {
    sayHelloWorld(); 
    return 0;
}

此cmakelist确保了项目可以被 CMake 成功构建,包括编译主文件、链接库文件,并生成最终的可执行文件。

# 指定最小 CMake 版本
cmake_minimum_required(VERSION 3.10)

# 项目名称和版本
project(HelloWorldApp)

set(CMAKE_C_COMPILER "gcc") # 需要引号
set(CMAKE_CXX_COMPILER "g++") # 需要引号

# 设置HelloWorld动态库的查找路径
set(HelloWorld_DIR "D:\\VSC\\C++test1\\C++test1\\HelloWorldLib\\cmake")
# 查找并加载HelloWorl动态库的配置
find_package(HelloWorld REQUIRED)

# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

# 启用 C++11 支持
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 定义源文件
set(HELLO_WORLD_SOURCES src/main.cpp)

# 查找头文件
# set(HELLO_WORLD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)

# 包含头文件的路径
include_directories(${HELLO_WORLD_INCLUDE_DIR})

message(STATUS "HELLO_WORLD_INCLUDE_DIR: ${HELLO_WORLD_INCLUDE_DIR}")

#link_directories命令用于添加目录使链接器能在其查找库
link_directories(${HELLO_WORLD_LIBRARY_DIR})

# 添加可执行文件
add_executable(HelloWorldApp ${HELLO_WORLD_SOURCES})

# 将HelloWorld库链接到可执行文件上
target_link_libraries(HelloWorldApp ${HELLO_WORLD_LIBRARIES})
message(STATUS "HELLO_WORLD_dll_DIR:D:\\VSC\\C++test1\\C++test1\\HelloWorldLib\\cmake\\lib\\libHelloworld.dll")
# 使用下面的命令使用g++编译器及gcc编译器组件
#cmake -G "Unix Makefiles" ..

cmakelist关键部分可以概括如下:指定版本,设置项目名,设置编译器,设定一个变量包含HelloWorldLib的cmake文件路径,查找库,设置生成可执行文件输出路径,设置编译标准,设定一个变量包含主文件路径,设定头文件搜索路径,链接库目录,添加可执行文件,链接库,打印出关键路径的状态信息...

 将dll文件复制到bin目录内,最终生成的可执行文件.exe在bin目录

 

 

注意事项

1. 一致性:
   确保在 DLL 的导出和导入声明中使用完全一致的函数签名,包括函数名、参数列表和返回类型。

2. 名称修饰:
   C++ 编译器会对函数名进行修饰(Name Mangling),这可能会影响符号的链接。使用 `extern "C"` 可以避免 C++ 名称修饰,确保与 C 代码的兼容性。

3.编译器和平台,宏定义:
   不同的编译器和平台可能有不同的导出和导入语法,确保使用的语法与目标平台和编译器兼容。通常使用宏来定义 DLL 接口,这样可以根据编译时是否定义了宏来决定是导出还是导入,使用 __declspec 关键字来指定符号的导入或导出属性。
 

 类的dll导入导出

与函数类似,这里就不赘述了 ,直接上代码

C++test2:
├─HelloWorldApp
│  ├─bin
│  ├─build
│  │  ├─bin
│  │  └─CMakeFiles 
│  └─src
└─HelloWorldLib
    ├─bin
    ├─build
    │  └─CMakes    
    ├─cmake
    │  ├─include
    │  └─lib
    ├─include
    └─src

#头文件
#pragma once

#ifndef HELLOWORLD_EXPORTS
#define HELLOWORLD __declspec(dllimport)
#else
#define HELLOWORLD __declspec(dllexport)
#endif

class HELLOWORLD HelloWorld {
public:
HelloWorld(){};
~HelloWorld(){};
void sayHelloWorld();
};

 类的构造函数和析构函数需要特别关注,因为它们可能不会随着类的其他成员函数一起被自动导出或导入。需要确保这些特殊成员函数也使用 __declspec(dllexport) 或 __declspec(dllimport) 属性

#dll的cmakelist
cmake_minimum_required(VERSION 3.10)

# 项目名称和版本
project(HelloWorldLibrary)

# 输出路径 LIBRARY_OUTPUT_PATH为生成库文件路径,为cmake内置变量

set(LIBRARY_OUTPUT_PATH "D:\\VSC\\C++test2\\C++test2\\HelloWorldLib\\cmake\\lib")  # 修改库的输出路径,dll放这里面

# 启用 C++11 支持
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 定义源文件
set(HELLO_WORLD_SOURCES src/HelloWorld.cpp )

# 包含头文件的路径的变量设置
set(HELLO_WORLD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)

# 包含头文件,这里输入头文件查找路径即可
include_directories(${HELLO_WORLD_INCLUDE_DIR})

add_definitions(-DHELLOWORLD_EXPORTS)

# 创建动态库,动态库名为Helloworld
add_library(Helloworld SHARED ${HELLO_WORLD_SOURCES})


# 消息输出
message(STATUS "print: ${HELLO_WORLD_SOURCES}")
# message(STATUS "LIBRARY_OUTPUT_PATH: ${LIBRARY_OUTPUT_PATH}")
#类的源文件
#include "HelloWorld.h"
#include <iostream>

HELLOWORLD void HelloWorld::sayHelloWorld() {
    std::cout << "Hello World!" << std::endl;
}
#HelloWorldApp的cmakelist
# 指定最小 CMake 版本
cmake_minimum_required(VERSION 3.10)

# 项目名称和版本
project(HelloWorldApp)

set(CMAKE_C_COMPILER "gcc") # 需要引号
set(CMAKE_CXX_COMPILER "g++") # 需要引号

# 设置HelloWorld动态库的查找路径
set(HelloWorld_DIR "D:\\VSC\\C++test2\\C++test2\\HelloWorldLib\\cmake")
# 查找并加载HelloWorl动态库的配置
find_package(HelloWorld REQUIRED)

# 设置可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

# 启用 C++11 支持
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 定义源文件
set(HELLO_WORLD_SOURCES src/main.cpp)

# 包含头文件的路径
include_directories(${HELLO_WORLD_INCLUDE_DIR})

message(STATUS "HELLO_WORLD_INCLUDE_DIR: ${HELLO_WORLD_INCLUDE_DIR}")

#link_directories命令用于添加目录使链接器能在其查找库
link_directories(${HELLO_WORLD_LIBRARY_DIR})

# 添加可执行文件
add_executable(HelloWorldApp ${HELLO_WORLD_SOURCES})

# 将HelloWorld库链接到可执行文件上
target_link_libraries(HelloWorldApp ${HELLO_WORLD_LIBRARIES})
message(STATUS "HELLO_WORLD_dll_DIR: D:\\VSC\\C++test2\\C++test2\\HelloWorldLib\\cmake\\lib\\libHelloworld.dll")
# 使用下面的命令使用g++编译器及gcc编译器组件
#cmake -G "Unix Makefiles" ..
#主函数
#include <iostream>
#include "HelloWorld.h"

int main() {
    HelloWorld HelloWorld;
    HelloWorld.sayHelloWorld();
    return 0;
}

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值