【cmake实战六】如何使用编译的库(动态库dll)——windows系统

一、文件目录

在这里插入图片描述

1、main.cpp

#include<iostream>
#include<windows.h>
using namespace std;
void loadDll(string dllname, HMODULE& handle)
{
	//handle = (handle_t)::LoadLibrary(dllname.c_str());
	handle = (HMODULE)::LoadLibrary(dllname.c_str());
}

bool freeDll(HMODULE& handle)
{
	bool re = FreeLibrary(handle);
	return re;
}
int main()
{
	cout<<"hello world"<<endl;
	//handle_t handle = nullptr;
	HMODULE handle = nullptr;
	loadDll("C:\\Users\\jx\\Desktop\\test06\\lib\\Debug\\haha.dll", handle);
	if (nullptr == handle)
	{
		cout << "fail to load dll" << endl;
	}
	
	cout << "sucess to load dll" << endl;

	typedef void (*print)();

	print hahafunc=(print)GetProcAddress(handle, "haha");
	hahafunc();

	bool re = freeDll(handle);
	if (re)
	{
		cout << "success to unload dll" << endl;
	}

	return 0;
}

2、CmakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

PROJECT(NEWHELLO)

ADD_EXECUTABLE(hello main.cpp)

ADD_SUBDIRECTORY(haha)

SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

3、haha

  • haha.h
#ifndef HAHA_H
#define HAHA_H

#include<iostream>

extern "C" __declspec(dllexport) void haha();
#endif

  • haha.cpp
#include "haha.h"
using namespace std;
void haha()
{
	cout<<"haha"<<endl;
}
  • CmakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

SET(TARGET "haha")

#ADD_LIBRARY(haha STATIC haha.cpp)
ADD_LIBRARY(haha SHARED haha.cpp)

SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

二、构建、编译、运行

1、构建(新建build目录,在build目录下执行)

cmake ..

2、编译

devenv.com NEWHELLO.sln /Build "Debug|x64"

3、运行

.\hello.exe

在这里插入图片描述

三、知识讲解

1、LoadLibrary

2、FreeLibrary

  • 当不再需要 DLL 模块时,显式链接到 DLL 的进程会调用 FreeLibrary 函数。 此函数使模块的引用计数递减。 而且,如果引用计数为零,则从进程的地址空间取消映射。
  • FreeLibrary微软提供,linux下不可用,同linux下的dlclose

参考- https://docs.microsoft.com/zh-cn/cpp/build/freelibrary-and-afxfreelibrary?view=msvc-170

3、GetProcAddress

  • 显式链接到 DLL 的进程会调用 GetProcAddress,以获取 DLL 中导出函数的地址。 可使用返回的函数指针调用 DLL 函数。 GetProcAddress 采用 DLL 模块句柄(由 LoadLibrary返回)作为参数,并采用要调用的函数的名称或函数的导出序号。
  • 微软提供,linux下无法使用,同linux下的dlsym

参考:https://docs.microsoft.com/zh-cn/cpp/build/getprocaddress?view=msvc-170
https://www.cnblogs.com/avexer/p/3258291.html

4、__declspec

  • dllexportdllimport存储类属性是特定于 C 和 C++ 语言的扩展。 可以使用它们从 DLL 中导出或向其中导入函数、数据和对象。
  • 微软提供的,linux下无法使用,同linux下的__attribute__ ((visibility(“default”)))

参考:https://docs.microsoft.com/zh-cn/cpp/cpp/dllexport-dllimport?view=msvc-170
https://blog.csdn.net/yu704645129/article/details/53171315

5、extern “C”

四、问题

1、去掉extern “C" 会发生什么???

  • GetProcAddress(handle, “haha”);会失败

2、是否有办法去掉extern “C"

  • 结论,如果导出的c++的接口,不可以
  • 结论,或者,只需要把haha更改为c语言就可以了,如下
  • haha.h
#ifndef HAHA_H
#define HAHA_H

#include<stdio.h>

//extern "C" __declspec(dllexport) void haha();
__declspec(dllexport) void haha();
#endif


  • haha.c
#include "haha.h"

void haha()
{
	printf("haha\n");
}
  • CmakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

SET(TARGET "haha")

#ADD_LIBRARY(haha STATIC haha.cpp)
ADD_LIBRARY(haha SHARED haha.c)

SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

3、为啥c++就需要extern “C”,在

C++的编译方式考虑了函数重载,所以对函数名进行了新的修饰,产生了所谓的破坏性命名。

  • 这样实际上,我们在exe中调用的函数名字就是已经被修饰过的,所以我们直接按照原来的函数名自然就找不到了!为什么GetProcAddress返回值总为0?[解决方案]
  • c++代码,有extern “C”,使用depend查看haha.dll,函数名字为haha

在这里插入图片描述

  • c++代码,无extern “C”,使用depend查看haha.dll,函数名字为?haha@@YAXXZ

在这里插入图片描述

4、参考问题3的答案,问题2的另外一种方法

	print hahafunc=(print)GetProcAddress(handle, "haha");

更改为

	print hahafunc=(print)GetProcAddress(handle, "?haha@@YAXXZ");

1.使用LoadLibrary函数来加载dll库
2.C/C++中extern关键字详解
3.

### 使用 CMake 和 g++ 在 Windows 上创建动态库 #### 准备工作 为了在 Windows 平台上利用 CMake 和 g++ 创建动态链接DLL),需先确认已安装 MinGW 或者 MSYS2 环境,因为这些环境提供了适用于 Windows 的 GNU 编译器集合(GCC)。确保 `gcc` 和 `g++` 命令可以在命令提示符中正常运行。 #### 配置项目结构 建立如下所示的基础文件夹和文件: - **CMakeLists.txt**: 定义项目的配置以及如何构建目标。 - **src/hello.dll.cpp**: 动态库实现代码。 - **include/hello_dll.h**: 头文件定义接口函数原型。 - **test/test_main.cpp**: 测试程序用于验证 DLL 是否能被正确加载和调用。 #### 编写 CMake 文件 编辑 `CMakeLists.txt` 来指定要生成的目标类型为共享,并设置必要的属性以便于导出符号。对于 Windows 下的 DLL 构建来说,还需要特别注意区分导入/导出宏定义[^3]。 ```cmake # 设置最低版本需求 cmake_minimum_required(VERSION 3.10) # 设定工程名称 project(DLLExample VERSION 1.0 LANGUAGES CXX) # 添加头文件路径 include_directories(${PROJECT_SOURCE_DIR}/include) # 指明源码位置 add_library(hello SHARED src/hello_dll.cpp) # 对于 Windows, 导入/导出宏处理 if(MSVC OR MINGW) target_compile_definitions(hello PRIVATE "HELLO_DLL_EXPORTS") endif() # 可选:设定输出目录 set_target_properties(hello PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") # 如果有测试应用,则添加可执行文件部分 add_executable(test_app test/test_main.cpp) target_link_libraries(test_app hello) ``` #### 实现动态库功能 编写简单的 “Hello World” 类型的功能作为示例来展示基本操作。这通常涉及两个主要组件:一个是实际逻辑所在的 `.cpp` 文件;另一个是用来声明 API 接口的 `.h` 文件。 ##### hello_dll.h ```c++ #ifndef HELLO_DLL_H_ #define HELLO_DLL_H_ #ifdef WIN32 #ifdef HELLO_DLL_EXPORTS #define HELLO_API __declspec(dllexport) #else #define HELLO_API __declspec(dllimport) #endif #else #define HELLO_API #endif extern "C" { HELLO_API void say_hello(); } #endif // !HELLO_DLL_H_ ``` ##### hello_dll.cpp ```cpp #include <iostream> #include "../include/hello_dll.h" void say_hello() { std::cout << "Hello from DLL!" << std::endl; } ``` #### 编写测试应用程序 最后一步是创建一个小的应用程序去检验新创建的 DLL 能否按预期工作。 ##### test_main.cpp ```cpp #include "../include/hello_dll.h" int main(int argc, char* argv[]) { say_hello(); // 应该会打印 Hello from DLL! return 0; } ``` #### 执行构建过程 完成上述准备工作之后,在终端窗口内导航到包含 `CMakeLists.txt` 的根目录下,按照下面指令依次执行以启动整个流程: 1. 创建一个新的子目录用来放置所有的中间产物 (`mkdir build && cd build`) 2. 运行 CMake 工具初始化项目并生成 Makefile 或其他所需脚本(`cmake .. -G "MinGW Makefiles"`). 3. 开始编译(`mingw32-make`)使用更现代的方式(`cmake --build .`). 成功完成后应该会在对应的输出目录找到生成好的 `.dll`,`.exe` 文件以及其他关联资源。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郑同学的笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值