C++创建一个动态链接库

时间:2021-12-28 15:49:44
C++创建一个动态链接库
动态链接库是一个模块,它包含了一些函数和数据,能够被其他模块进行调用(其他程序或DLL)。

下面演示如何将函数,数据和类导出供其他可执行文件调用。

导出的数据,函数,和类如下

// Global Data
int g_nVal1
int g_nVal2


// Ordinary Functions
int __cdecl GetStringLength1(PCWSTR pszString);
int __stdcall GetStringLength2(PCWSTR pszString);


// Callback Function
int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc)


// Class
class CSimpleObject
{
public:
CSimpleObject(void); // Constructor
virtual ~CSimpleObject(void); // Destructor

// Property
float get_FloatProperty(void);
void set_FloatProperty(float newVal);


// Method
HRESULT ToString(PWSTR pszBuffer, DWORD dwSize);


// Static method
static int GetStringLength(PCWSTR pszString);


private:
float m_fField;
};

有两种方法可以从DLL中导出数据
1.使用.DEF文件进行导出
模块定义文件(.DEF)是一个纯文本文件,包括了一个或多个模块的声明,用来描述DLL的属性。
2.使用导出符号__declspec(dllexport) 
A 使用.DEF导出的具体实现方法是如下

1.首先在头文件中声明导出数据和函数,然后在cpp中进行定义

    int g_nVal1;
int /*__cdecl*/ GetStringLength1(PCWSTR pszString)
int __stdcall GetStringLength1(PCWSTR pszString)
int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc)
2.添加一个.DEF文件,名称为CppDynamicLinkLibrary.def。文件的第一行必须是LIBRARY,然后紧跟要导出的DLL的名字,例如CppDynamicLinkLibrary 。接下来导出声明列出将要导出的数据或函数的名字。

   LIBRARY   CppDynamicLinkLibrary
EXPORTS
GetStringLength1 @1
CompareInts @2
g_nVal1 DATA
由于在连接过程中DLL工程会调用.def文件,所以我们应该在项目的属性对话框中的Linker/Input 页面将 模块定义文件的值设置为我们刚才创建的那个.def文件。

B 使用__declspec(dllexport)导出符号进行导出

首先在头文件中定义如下预编译块
这样做的目的是使得我们在导入导出的时候更方便,因为我们可以使用SYMBOL_DECLSPEC宏来替代看起来十分繁琐吓人的__declspec(dllexport)导出符号。

    #ifdef CPPDYNAMICLINKLIBRARY_EXPORTS
#define SYMBOL_DECLSPEC __declspec(dllexport)
#else
#define SYMBOL_DECLSPEC __declspec(dllimport)
#endif


在每个将要导出的数据或函数前都加上SYMBOL_DECLSPEC,这里我们额外添加EXTERN_C(extern “C”)的目的是为了支持我们的模块被C语言或其他动态链接的程序调用。添加这个的意思就是指明使用C的连接方式,在连接的时候函数名不会改变,而不是C++的安全命名法,会改变函数名称。

 
EXTERN_C SYMBOL_DECLSPEC int g_nVal2;EXTERN_C SYMBOL_DECLSPEC int /*__cdecl*/ GetStringLength2(PCWSTR pszString);EXTERN_C SYMBOL_DECLSPEC int __stdcall GetStringLength2(PCWSTR pszString);class SYMBOL_DECLSPEC CSimpleObject{    ...};

完整代码:

.h

#pragma once

#include <windows.h>

// 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 CPPDYNAMICLINKLIBRARY_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
// SYMBOL_DECLSPEC and SYMBOL_DEF functions as being imported from a DLL,
// whereas this DLL sees symbols defined with these macros as being exported.

#ifdef CPPDYNAMICLINKLIBRARY_EXPORTS
#define SYMBOL_DECLSPEC __declspec(dllexport)
#define SYMBOL_DEF
#else
#define SYMBOL_DECLSPEC __declspec(dllimport)
#define SYMBOL_DEF __declspec(dllimport)
#endif


#pragma region Global Data

EXTERN_C SYMBOL_DECLSPEC int g_nVal2;

#pragma endregion


#pragma region Ordinary Functions

SYMBOL_DEF int /*__cdecl*/ GetStringLength1(PCWSTR pszString);

EXTERN_C SYMBOL_DECLSPEC int __stdcall GetStringLength2(PCWSTR pszString);

#pragma endregion


#pragma region Callback Function

// Type-definition: 'PFN_COMPARE' now can be used as type
typedef int (CALLBACK *PFN_COMPARE)(int, int);

// An exported/imported stdcall function using a DEF file
// It requires a callback function as one of the arguments
// Sym: CompareInts
// See: CppDynamicLinkLibrary.cpp
SYMBOL_DEF int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc);

#pragma endregion


#pragma region Class

class SYMBOL_DECLSPEC CSimpleObject
{
public:

CSimpleObject(void); // Constructor
virtual ~CSimpleObject(void); // Destructor

// Property
float get_FloatProperty(void);
void set_FloatProperty(float newVal);

// Method
HRESULT ToString(PWSTR pszBuffer, DWORD dwSize);

// Static method
static int GetStringLength(PCWSTR pszString);

private:
float m_fField;
};

#pragma endregion
.cpp

#include "CppDynamicLinkLibrary.h"
#include <strsafe.h>

#pragma region DLLMain
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;
}
#pragma endregion


#pragma region Global Data

// An exported/imported global data using a DEF file
// Initialize it to be 1
int g_nVal1 = 1;


// An exported/imported global data using __declspec(dllexport/dllimport)
// Initialize it to be 2
SYMBOL_DECLSPEC int g_nVal2 = 2;

#pragma endregion


#pragma region Ordinary Functions


// An exported/imported cdecl(default) function using a DEF file
int /*__cdecl*/ GetStringLength1(PCWSTR pszString)
{
return static_cast<int>(wcslen(pszString));
}


// An exported/imported stdcall function using __declspec(dllexport/dllimport)
SYMBOL_DECLSPEC int __stdcall GetStringLength2(PCWSTR pszString)
{
return static_cast<int>(wcslen(pszString));
}

#pragma endregion


#pragma region Callback Function

// An exported/imported stdcall function using a DEF file
// It requires a callback function as one of the arguments
int __stdcall CompareInts(int a, int b, PFN_COMPARE cmpFunc)
{
// Make the callback to the comparison function

// If a is greater than b, return a;
// If b is greater than or equal to a, return b.
return ((*cmpFunc)(a, b) > 0) ? a : b;
}

#pragma endregion


#pragma region Class

// Constructor of the simple C++ class
CSimpleObject::CSimpleObject(void) : m_fField(0.0f)
{
}


// Destructor of the simple C++ class
CSimpleObject::~CSimpleObject(void)
{
}


float CSimpleObject::get_FloatProperty(void)
{
return this->m_fField;
}


void CSimpleObject::set_FloatProperty(float newVal)
{
this->m_fField = newVal;
}


HRESULT CSimpleObject::ToString(PWSTR pszBuffer, DWORD dwSize)
{
return StringCchPrintf(pszBuffer, dwSize, L"%.2f", this->m_fField);
}


int CSimpleObject::GetStringLength(PCWSTR pszString)
{
return static_cast<int>(wcslen(pszString));
}

#pragma endregion
.def

LIBRARY   CppDynamicLinkLibrary
EXPORTS
GetStringLength1 @1
CompareInts @2
g_nVal1 DATA