自学内容网 自学内容网

一文说清:windows下C++动态库的封装和调用

一 引言

有时候,需要在一个程序中,调用dll动态库方式实现某些功能,并且最好能够调试。

下面的操作,就可以一步一步地实现封装一个动态库,以及完成动态库的调试工作。

本文中的环境和IDE工具,使用Win7 + VS2012。

二 动态库的封装

1 构建dll工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:应用程序类型,选择DLL。

2 创建动态库导出类

在这里插入图片描述

![[Pasted image 20241116134850.png]]

在这里插入图片描述

3 定义接口函数

在头文件中,定义代码如下:

#pragma once

//定义DLL_IMPLEMENT宏,完成以下目标:
//1.在dll项目内部使用__declspec(dllexport)导出 
//2.在dll项目外部使用时,用__declspec(dllimport)导入 
//宏DLL_IMPLEMENT在simpledll.cpp中定义 
#ifdef DLL_IMPLEMENT 
#define DLL_API __declspec(dllexport) 
#else 
#define DLL_API __declspec(dllimport) 
#endif 

namespace ZDD
{ 
//导出类
class DLL_API SimleDll
{
public:
TestDll(void);
~TestDll(void);

//简单方法
int add(int x, int y);
};
}

这里,为何要定义命名空间namespace呢?这是为了防止函数名或类名定义重复。

从以上代码可以看出,如果是把类定义为动态库外部调用的接口,使用DLL_API来定义。

当然,实际上是定义的__declspec(dllimport)或者__declspec(dllexport)

接下来看完实现代码再分析。

4 编写cpp代码

代码如下:

//注意,此处的DLL_IMPLEMENT宏定义,需要写在#include "SimleDll.h"之前 
//以完成如下目标:
//1.在dll项目内部使用__declspec(dllexport)导出 
//2.在dll项目外部使用时,用__declspec(dllimport)导入 

#define DLL_IMPLEMENT 
#include "SimleDll.h"

namespace ZDD
{ 
SimleDll::SimleDll(void)
{
}

SimleDll::~SimleDll(void)
{
}

int SimleDll::add(int x, int y) 
{ 
return x+y; 
} 
}

在实现代码中,定义DLL_IMPLEMENT宏,并放到include头文件之前。
这样,在实现文件中,实际就是定义类

class __declspec(dllexport) SimleDll

表示该类为导出类。
在调用工程中,直接使用头文件,因为没有定义DLL_IMPLEMENT宏,因此类的定义相当于

class __declspec(dllimport) SimleDll

表示该类为导入类。

如此一来,同一个头文件,就在不同的工程中有了不同的用处。

5 编译

编译后,如果是默认的生成目录,会在Debug目录下生成一个dll文件和一个lib文件。

将来把头文件和这两个库文件拷贝到调用工程,具体参考下边。

三 动态库的调试

1 思路

实际上,虽然是调试动态库,但通常都是将静态库引入到调用工程中。
目前有两种方法,一是从项目属性中设置静态库的位置,二是直接把静态库导入到工程。

2 步骤

1、新建工程
在这里插入图片描述

2、设置头文件
在工程中建立h目录,将头文件SimleDll.h拷贝到该目录。并在工程中设置,如下:
在这里插入图片描述

3、方案一:设置库文件的位置
同样地,在工程中建立lib目录,把上述产生的静态库文件TestDll.lib拷贝到该目录,设置如下。
在这里插入图片描述

4、方案二:引入静态库到项目

5、调用函数,实现代码如下:

#include "SimleDll.h"
using namespace zdd;

int _tmain(int argc, _TCHAR* argv[])
{
int sum = 0;
SimleDll sd;

sum = sd.add(3, 6);
printf("sum = %d \n", sum);

return 0;
}

如上,包含头文件,使用命名空间,使用动态库中的类生成实例,调用接口。

6、查看动态库的接口
使用depends.exe工具,查看dll对外的接口。如下:
在这里插入图片描述

可能会提示有错误,但不影响使用。

四 问题

1 VS工程只生成dll不生成lib的解决

在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便输入个名字 -> 添加。现在编译就可生成.lib 文件了。

2 可以不定义和使用命名空间

命名空间并不是必要的。

3 在调用程序出现“无法解析的外部符号”错误

错误信息如下:

1>DataLinkLayer.obj : error LNK2019: 无法解析的外部符号 "public: __thiscall Active::Active(void)" (??0Active@@QAE@XZ),该符号在函数 "private: __thiscall DataLinkLayer::DataLinkLayer(void)" (??0DataLinkLayer@@AAE@XZ) 中被引用
1>GatewayMain.obj : error LNK2001: 无法解析的外部符号 "public: __thiscall Active::Active(void)" (??0Active@@QAE@XZ)
1>GeneralActive.obj : error LNK2001: 无法解析的外部符号 "public: __thiscall Active::Active(void)" (??0Active@@QAE@XZ)
1>ProcessCommBridge.obj : error LNK2001: 无法解析的外部符号 "public: __thiscall Active::Active(void)" (??0Active@@QAE@XZ)

原因是因为类前面没有声明为动态库引入,如下DLL_API。

#ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
//导出类
class DLL_API Active

注意:

当调试的DLL被映射到其他的应用程序(非TEST)进程空间并运行时,在该DLL中设置的断点无效,当然可以通过MessageBox来查看变量,若该DLL是MFC扩展DLL,则还可以用TRACE或afxDump来查看变量。

五 小结

通过如上例子,讲述了生成一个动态库、以及调试动态库的过程。

希望对您有用!

项目完整工程和代码,如下,想用的可以下载:

windows下C++动态库的封装和调用全部源码和工程


原文地址:https://blog.csdn.net/troglodyte/article/details/143816714

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!