一文说清:windows下C++动态库的封装和调用
一 引言
有时候,需要在一个程序中,调用dll动态库方式实现某些功能,并且最好能够调试。
下面的操作,就可以一步一步地实现封装一个动态库,以及完成动态库的调试工作。
本文中的环境和IDE工具,使用Win7 + VS2012。
二 动态库的封装
1 构建dll工程
注意:应用程序类型,选择DLL。
2 创建动态库导出类
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来查看变量。
五 小结
通过如上例子,讲述了生成一个动态库、以及调试动态库的过程。
希望对您有用!
项目完整工程和代码,如下,想用的可以下载:
原文地址:https://blog.csdn.net/troglodyte/article/details/143816714
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!