前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VC++DLL动态链接库程序

VC++DLL动态链接库程序

作者头像
ccf19881030
发布2019-07-10 16:00:09
1.4K0
发布2019-07-10 16:00:09
举报
文章被收录于专栏:ccf19881030的博客

VC++DLL动态链接库程序

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

C++ DLL 导出函数

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

DllDemo DllDemo.h

代码语言:javascript
复制
// 下列 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

代码语言:javascript
复制
// 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

代码语言:javascript
复制
// 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

代码语言:javascript
复制
// 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

代码语言:javascript
复制
//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

代码语言:javascript
复制
// 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

代码语言:javascript
复制
// 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

代码语言:javascript
复制
//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

代码语言:javascript
复制
//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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
LIBRARY	"MatureApproach"
EXPORTS
CreateExportObj @ 1
DestroyExportObj @ 2

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

代码语言:javascript
复制
// 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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年07月07日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • VC++DLL动态链接库程序
  • C++ DLL 导出函数
    • 使用VS2017等IDE生成dll程序,示例如下:
    • C++ DLL 导出类
      • 1.导出类中第一种方法:简单导出类(不推荐使用)
        • 简单导出类的示例程序
        • 简单导出类的缺点
      • 2.导出类的一种通用方法(推荐):使用接口
        • 导出类的较好方式 DLL示例程序
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档