VC++DLL动态链接库程序

最近查找了一下VC++中关于编写DLL动态库的资料,主要是导出函数和导出类的编写。因为在实际项目开发中有时需要使用C++编写好DLL接口,控制设备,提供给其他语言如Nodejs、C#等使用。

C++ DLL 导出函数

使用VS2017等IDE生成dll程序,示例如下:

DllDemo

DllDemo.h

// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLLDEMO_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// DLLDEMO_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif // 此类是从 DllDemo.dll 导出的
class DLLDEMO_API CDllDemo {
public:
CDllDemo(void);
// TODO: 在此添加您的方法。
}; extern DLLDEMO_API int nDllDemo; // 加上extern "C"表示是一个C函数,不重载
extern "C" DLLDEMO_API int fnDllDemo(void);

DllDemo.cpp

// DllDemo.cpp : 定义 DLL 应用程序的导出函数。
// #include "stdafx.h"
#include "DllDemo.h" #include <iostream>
using namespace std; // 这是导出变量的一个示例
DLLDEMO_API int nDllDemo=0; // 这是导出函数的一个示例。
DLLDEMO_API int fnDllDemo(void)
{
cout << "fnDllDemo(void) called" << endl; return 42;
} // 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DllDemo.h
CDllDemo::CDllDemo()
{
cout << "CDllDemo::CDllDemo() called" << endl; return;
}

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

DLL的测试程序

使用VS2017创建一个基于Win32的控制台应用程序testDllDemo:

testDllDemo.cpp

// testDllDemo.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h" #include <stdio.h>
#include <Windows.h> typedef int(*dllpFun)(void); int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hDll = LoadLibrary(L"DllDemo.dll"); if (!hDll)
{
return -1;
} dllpFun pFunc = (dllpFun)GetProcAddress(hDll, "fnDllDemo"); pFunc(); FreeLibrary(hDll); getchar(); return 0;
}

C++ DLL 导出类

1.导出类中第一种方法:简单导出类(不推荐使用)

简单导出类的示例程序

NaiveApproach.h

//2011.10.6
//cswuyg
//dll导出类,比较差劲的方法
#pragma once
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the NAIVEAPPROACH_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// NAIVEAPPROACH_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef NAIVEAPPROACH_EXPORTS
#define NAIVEAPPROACH_API __declspec(dllexport)
#else
#define NAIVEAPPROACH_API __declspec(dllimport)
#endif //基类也必须导出,否则警告:
class NAIVEAPPROACH_API CBase
{
public:
void Test();
private:
int m_j;
}; //也必须导出
class NAIVEAPPROACH_API CDate
{
public:
void Test2();
private:
int m_k;
}; class NAIVEAPPROACH_API CNaiveApproach : public CBase
{
public:
CNaiveApproach(int i = 0);
// TODO: add your methods here.
void Func();
private:
int m_iwuyg;
CDate m_dobj;
};

NaiveApproach.cpp

// NaiveApproach.cpp : Defines the entry point for the DLL application.
// #include "stdafx.h"
#include "NaiveApproach.h" #ifdef _MANAGED
#pragma managed(push, off)
#endif BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
} #ifdef _MANAGED
#pragma managed(pop)
#endif // This is the constructor of a class that has been exported.
// see NaiveApproach.h for the class definition
CNaiveApproach::CNaiveApproach(int i) : m_iwuyg(i)
{
} void CNaiveApproach::Func()
{
wcout << L" cswuyg test dll , i = " << m_iwuyg << endl;
system("pause");
} void CBase::Test()
{
wcout << L"Just a Test" << endl;
system("pause");
} void CDate::Test2()
{
wcout << L"Test 2" << endl;
system("pause");
}

UserDll.cpp

// User.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "NaiveApproach.h"
#pragma comment(lib, "../debug/NaiveApproach.lib") int _tmain(int argc, _TCHAR* argv[])
{
//////////////////////////////////////////////////////////////////////////
CNaiveApproach obj(10);
obj.Test();
obj.Func();
//////////////////////////////////////////////////////////////////////////
return 0;
}

简单导出类的缺点

这种简单导出类的方式,除了导出的东西太多、使用者对类的实现依赖太多以外,还有其他问题:必须保证使用同一种编译器。导出类的本质是导出类里的函数,因为语法上直接导出了类,没有对函数的调用方式、重命名进行设置,导致了产生的dll并不通用。

2.导出类的一种通用方法(推荐):使用接口

定义一个抽象类(都是纯虚函数),调用者跟dll共用一个抽象类的头文件,dll中实现此抽象类的派生类,dll最少只需要提供一个用于获取抽象类对象指针的接口。

写一个基类,方法都为虚函数,这样使用dll的exe使用都没问题。

dll的类从基类 派生,然后返回基类接口即可。

面向抽象设计优点:这种方式利用了C++类的虚函数,类似COM思想,采用接口跟实现分离,可以使得工程的结构更清晰,使用者只需要知道接口,而无需知道具体实现,产生的DLL通用没有特定环境限制。

注意事项:调用者跟DLL共用一个抽象类的头文件,调用者依赖于DLL的东西很少,只需要知道抽象类的接口,以及获取对象指针的导出函数,对象内存空间的申请和释放都在DLL模块中完成

导出类的较好方式 DLL示例程序

ExportClassImpl.h

//2011.10.6
//cswuyg
//dll导出类
// 实现类
#pragma once #include "MatureApproach.h" class ExportImpl : public IExport
{
public:
virtual void Hi();
virtual void Test();
virtual void Release();
~ExportImpl();
private:
};

MatureApproach.h

//2011.10.6
//cswuyg
//dll导出类
//dll跟其使用者共用的头文件
#pragma once
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the MATUREAPPROACH_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// MATUREAPPROACH_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef MATUREAPPROACH_EXPORTS
#define MATUREAPPROACH_API __declspec(dllexport)
#else
#define MATUREAPPROACH_API __declspec(dllimport)
#endif class IExport
{
public:
virtual void Hi() = 0;
virtual void Test() = 0;
virtual void Release() = 0;
}; extern "C" MATUREAPPROACH_API IExport* _stdcall CreateExportObj();
extern "C" MATUREAPPROACH_API void _stdcall DestroyExportObj(IExport* pExport);

ExportClassImpl.cpp

#include "stdafx.h"
#include "ExportClassImpl.h" void ExportImpl::Hi()
{
wcout << L"Hello World" << endl;
} void ExportImpl::Test()
{
wcout << L"Hi cswuyg" << endl;
} void ExportImpl::Release()
{
delete this;
} ExportImpl::~ExportImpl()
{
cout << "Release OK" << endl;
}

MatureApproach.cpp

#include "stdafx.h"
#include "ExportClassImpl.h" void ExportImpl::Hi()
{
wcout << L"Hello World" << endl;
} void ExportImpl::Test()
{
wcout << L"Hi cswuyg" << endl;
} void ExportImpl::Release()
{
delete this;
} ExportImpl::~ExportImpl()
{
cout << "Release OK" << endl;
}

MatureApproach.def

LIBRARY	"MatureApproach"
EXPORTS
CreateExportObj @ 1
DestroyExportObj @ 2

测试调用DLL的Win32控制台程序 UserDll.cpp

// User.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "MatureApproach.h" #pragma comment(lib, "../debug/MatureApproach.lib") int _tmain(int argc, _TCHAR* argv[])
{
IExport* pExport = CreateExportObj();
pExport->Hi();
pExport->Test();
DestroyExportObj(pExport);
system("pause");
return 0;
}

参考资料

参考了相关的资料网址:

[HowTo: Export C++ classes from a DLL]: https://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL

[Explicitly Linking to Classes in DLL’s ]: http://www.codeguru.com/Cpp/W-P/dll/importexportissues/article.php/c123

[C++ DLL导出类 知识大全]: https://www.cnblogs.com/lidabo/p/7121745.html

[怎样从一个DLL中导出一个C++类]: https://blog.csdn.net/liubing8609/article/details/82156067

[DLL导出C++类]: https://blog.csdn.net/qq_33757398/article/details/82229325

VC++DLL动态链接库编程-网易云课堂视频基础教程

VC++DLL动态链接库程序的更多相关文章

  1. VC++ DLL 3 动态链接库

    前面先介绍了静态链接库的方式提供了函数结构的方法,现在就来说下,如果用非MFC的动态链接库要怎么实现,这个过程稍微复杂一点点,但是基本也都是一个套路下来. 1.新建一个工程: 2.编写cpp文件和头文 ...

  2. VC 使用msxml6.dll动态链接库中的函数读写XML文件

    VC 使用msxml6.dll动态链接库中的函数读写XML文件 目录 1 引言 2 .dll使用方法 3 常用函数总结 4 实例应用 5 运行效果预览 6 补充说明 7 不足之处 8 更新   引言: ...

  3. lib静态链接库,dll动态链接库,h文件

    最近在弄摄像头,发现我在调用摄像头自带的函数的时候,库没连接上,于是经过高人指点,学习了一下lib静态链接库,dll动态链接库来补充一下自己的基础知识. 一.首先我们来介绍一下lib静态链接库. li ...

  4. C++编写DLL动态链接库的步骤与实现方法

    原文:http://www.jb51.net/article/90111.htm 本文实例讲述了C++编写DLL动态链接库的步骤与实现方法.分享给大家供大家参考,具体如下: 在写C++程序时,时常需要 ...

  5. [Windows][VC]开机自动启动程序的几种方法

    原文:[Windows][VC]开机自动启动程序的几种方法 很多监控软件要求软件能够在系统重新启动后不用用户去点击图标启动项目,而是直接能够启动运行,方法是写注册表Software\\Microsof ...

  6. VC++ DLL 发布到生产环境过程

    最近项目中用到了VC++ DLL,在本机调试时无任何问题,但是发布出来后,COPY到另外的机器就报错,说找不到DLL,由于自身工作接触这方面比较少,经过一番折腾后,终于解决,以下为解决步骤 一,平台工 ...

  7. C# 与 VC Dll 传输信息

    考虑: 使用string类型传送: 在VC Dll中解析字符: 使用 string 类型将解析的类型传送到C#程序中: 建立VC解析的函数,提高代码可重用性

  8. C#调用VC dll输出参数

    最近做项目需要把以前Vc写的程序整合到VS2005来,用c#写的,以前VC的动态连接库写好了,重写比较麻烦,让C#调用VC dll就好了.但碰到了一个问题:VC中作为输出参数的char*类型的变量应该 ...

  9. .h头文件、 .lib库文件、 .dll动态链接库文件之间的关系

    转自.h头文件. .lib库文件. .dll动态链接库文件之间的关系 h头文件作用:声明函数接口 dll动态链接库作用:含有函数的可执行代码 lib库有两种: (1)静态链接库(Static Liba ...

随机推荐

  1. python------生产者消费者模型 和 管道

    一.为什么要使用生产者和消费者? 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程,在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才 ...

  2. javascript:变量声明&&赋值的提升和函数声明&&定义的提升在不同情况下的表现

    console.log(a); //undefined console.log(show); //函数的定义 show();         //aaa123 var a = 1; function ...

  3. MySQL 授权用户 ; 存储过程的DEFINER; 命令分隔符DELIMITER

    最近项目中遇到有人使用DEFINER这样的关键字,找了半天没有怎么理解这个意思.以为是限制谁使用这个存储过程,后来测试发现并不是这样. 搜索网上发现很多说法都不正确.看到一篇博客,做了如下介绍,才有所 ...

  4. 【知识强化】第三章 存储系统 3.4 主存储器与CPU的连接

    我们这节课来看一下关于主存的一些知识.我们将要讲解主存的简单的模型和主存与CPU连接的连接原理. 我们之前呢在第一章已经学过了存储器的构成,包括了存储体.MAR(也就是地址寄存器).MDR(也就是数据 ...

  5. H5手机端底部菜单覆盖中间部分内容的解决办法

    一.第一种Js动态计算中间内容的高度. 二.第二种给底部上面写个<div style="底部的高度"></div> 三.第三种给中间部分写一个margin- ...

  6. Sass-@for

    在制作网格系统的时候,大家应该对 .col1~.col12 这样的印象较深.在 CSS 中你需要一个一个去书写,但在 Sass 中,可以使用 @for 循环来完成.在 Sass 的 @for 循环中有 ...

  7. 2.xml约束技术----------dtd约束

    1.xml的约束 (1)为什么需要定义约束了 比如现在定义一个person的xml文件,只想要这个文件里面保存人的信息,比如name age等,但是如果在xml文件中写了一个元素<猫>,发 ...

  8. CDH6.3.1安装hue 报错

    x 一.查看日志server运行日志 /var/log/cloudera-scm-server/cloudera-scm-server.log 2019-12-11 17:28:34,201 INFO ...

  9. boost intrusive

    1. the advantages of intrusive container (1) Intrusive containers don't allocate memory dynamically. ...

  10. netty-Selector

    上图中加入一句: socketChannel.configureBlocking(false);//设置为非阻塞的 keyIterator.clear(); 每连接一个SocketChannel 都会 ...