C++ dll 学习记录

来源:互联网 发布:php能开发什么 编辑:程序博客网 时间:2024/06/06 01:28

一直做伸手党,有问题基本就是google和度娘,平时也很懒惰,水平一般,是时候静心,学学知识,写写博客,当然,菜鸟写不出什么东西,权当自己的日记了。万一写错了,看到的朋友切勿人参公鸡啊。写出来的东西,全部是平时遇到的问题,当然都是google或者度娘来的,但是文字或者代码都是
自己撸出来的,唉,还有自己的表达能力太差了,有时候心里明白的东西,到嘴边了,就是说不清楚,废话不说了。。。

参加工作后,第一份开发的工作是windows方面的开发,首先接触到的是COM组件的开发,那最基本的还是要学习DLL的基本知识。就是DLL里的函数如果要被外部调用,都要声明为导出函数。当然还可以导出类,外部用户调用这个类,这个类也是需要声明导出类.现在我的动态库里是这样一种情况,dll里导出一个接口,getCar用来获得dll内部类的一个指针(实例代码,且暂不考虑dll里指针内存释放的问题),内部有一个CCar的类,封装了很多功能,通过导出函数来获取这个类的一个实例,但是这个内部类也要申明为导出类,否则回报链接错误,实例代码如下:

dll里的头文件

// MYDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的// 符号视为是被导出的。#ifdef MYDLL_EXPORTS#define MYDLL_API __declspec(dllexport)#else#define MYDLL_API __declspec(dllimport)#endif//dll 内部定义的一个类,封装实现功能class CCar{public:    CCar();    ~CCar();    void Run();};//导出的函数(接口),让用户获得CCar类的一个实例的指针MYDLL_API CCar* getCar(void);
dll里的一个实现文件
// MyDll.cpp : 定义 DLL 应用程序的导出函数。//#include "stdafx.h"#include "MyDll.h"#include <iostream>// 这是导出函数的一个示例。MYDLL_API CCar* getCar(void){    std::cout << __FUNCTION__ << std::endl;    CCar* car = new CCar;    return car;}//CCar类的实现CCar::CCar(){}CCar::~CCar(){}void CCar::Run(){    std::cout << "car run" << std::endl;}

另外一个测试程序,通过隐式加载使用上面的dll,上面的dll已经顺利编译通过,并且生成lib和dll文件。

// DllTest.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include "../MyDll/Mydll.h"#include "iostream"int _tmain(int argc, _TCHAR* argv[]){    CCar *car = getCar();//获取CCar的一个实例指针,但是编译阶段就会报错    car->Run();return 0;}
错误如下

错误 2error LNK1120: 1 个无法解析的外部命令F:\ME\project\vs2013\DllTest\Debug\DllTest.exeDllTest
错误 1 error LNK2019: 无法解析的外部符号 "public: void __thiscall CCar::Run(void)" (?Run@CCar@@QAEXXZ),该符号在函数 _wmain 中被引用F:\ME\project\vs2013\DllTest\DllTest\DllTest.objDllTest

就是CCar的方法没有导出,导致链接出错,当然修改这个问题,只要在dll类的头文件里把CCar声明为导出类即可,如下

//导出的类 声明class MYDLL_API CCar{public:    CCar();    ~CCar();    void Run();};
重现编译dll,再重新编译运行外部程序,即可成功调用。运行结果如图所示:


当然这不是重点,有时候并不想把类导出,我只想通过我导出的接口让客户来使用,以前学习的时候有这样一种方法,不知道正不正规啊,但是有点类似COM组件的实现,通过虚函数来实现,再定义何以抽象类,让原来的CCar继承这个抽象类,那里面的类都不用声明为导出类,编译可以通过,并且正常使用。代码片段如下:

#define MYDLL_API __declspec(dllexport)#else#define MYDLL_API __declspec(dllimport)#endif//声明一个抽象类class ICarBase{public:    ICarBase(){}    ~ICarBase(){}    virtual void Run() = 0;};//导出的类 声明class CCar : public ICarBase{public:    CCar();    ~CCar();    void Run();};//导出的函数(接口)MYDLL_API CCar* getCar(void);
上面的代码就增加了一个基类,把类中让用户想调用的函数声明为虚函数,那用户就可以调用这个类的函数(接口)了,同时类中声明的时候不用加上这个宏了,MYDLL_API,把这个宏从类声明中去掉。其余代码都不变。

这样就利用了虚函数动态绑定的原理,外部调用动态库中的类方法时,不用在编译阶段确定方法的地址,那就不用把类声明为导出类了,根据以前学的COM组件的一点小知识,COM组件用C++估计也是这样实现的把,没有深入研究过,自己的猜测而已。

回头看了看自己的第一篇博客,唉。。。。好难看啊,都表达不清楚。。。









1 0