函数的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;
}