VS2017动态链接库(.dll)的生成与调用

一、DLL的生成

选择“具有导出项的(DLL)动态链接库”,vs会帮我们自动创建与项目同名的.cpp文件和.h文件,并在.h文件中定义好相关导出符号;如果选择“动态链接库(DLL)”则不会创建上述文件。

头文件中为什么要有下面的代码呢?

#ifdef YAKEDLL_EXPORTS
#define YAKEDLL_API __declspec(dllexport)
#else
#define YAKEDLL_API __declspec(dllimport)
#endif

 __declspec(dllexport)意思是导出符号到dll.
__declspec(dllimport)意思是从dll导入符号.
我们生成的时候,在项目属性中系统帮我们定义了YAKEDLL_EXPORTS宏,所以生成时是将符号导出到DLL中.

而我们在使用DLL的时候,没有定义到YAKEDLL_EXPORTS宏,所以使用的时候从DLL中导入符号到.exe文件,以供.exe文件使用.

接下来再头文件和cpp文件中添加想导出为dll的函数声明及实现。

yakeDLL.h头文件

// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 YAKEDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// YAKEDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef YAKEDLL_EXPORTS
#define YAKEDLL_API __declspec(dllexport)
#else
#define YAKEDLL_API __declspec(dllimport)
#endif

#include <iostream>
using namespace std;

// 此类是从 dll 导出的
class YAKEDLL_API CyakeDLL {
public:
	CyakeDLL(void);
	// TODO: 在此处添加其他方法。
	void Output(void);
};

// 导出变量符号到DLL中
extern YAKEDLL_API int nyakeDLL;

// 导出函数符号到DLL中
YAKEDLL_API int fnyakeDLL(void);

//导出函数(使用export)
YAKEDLL_API void fnMyDLLWithDllExport(ostream& os);

YAKEDLL_API int Add(int a, int b);

// 导出函数(C语言方式)
extern "C" 
{
	YAKEDLL_API void fnMyDLLWithExternC(ostream&os);
}

yakeDLL.cpp文件

// yakeDLL.cpp : 定义 DLL 的导出函数。
//

#include "pch.h"
#include "framework.h"
#include "yakeDLL.h"


// 这是导出变量的一个示例
YAKEDLL_API int nyakeDLL=0;

// 这是导出函数的一个示例。
YAKEDLL_API int fnyakeDLL(void)
{
    return 0;
}

YAKEDLL_API int Add(int a, int b)
{
	return a + b;
}

YAKEDLL_API void fnMyDLLWithDllExport(ostream& os)
{
	os << "this is my dll with dllexport." << endl;
}

YAKEDLL_API void fnMyDLLWithExternC(ostream& os)
{
	os << "this is my dll with extern C." << endl;
}

// 这是已导出类的构造函数。
CyakeDLL::CyakeDLL()
{
    return;
}

void CyakeDLL::Output(void)
{	
	std::cout << "this is CyakeDLL::Output function!" << std::endl;
	return;

}

然后编译,可以看到输出已经生成了想要的dll和lib文件。

.lib文件有两种,一种是静态库,是静态编译出来的,索引和实现都在其中。

另一种是与动态编译出来的dll文件对应的lib文件,一般是一些索引信息,具体的实现在dll文件中。(本例子的情况)

从开始菜单的Visual Studio 2017的开发人员命令提示符处启动DUMPBIN工具,执行dumpbin -exports path\to\dll命令分析生成的dll,查看编译器产生的函数名:

VC++编译器会针对C++函数使用名称修饰,而不会修饰以C方式声明的函数(其实也修饰了,在函数名前面加了一个下划线前缀(fnMyDLLWithExternC = @ILT+305(_fnMyDLLWithExternC)),这是C语言的函数调用约定默认为__cdecl所导致); 

二、DLL的引用

 调用dll有两种链接方式:隐式链接显式链接,无论哪种方式都要求将dll和exe放在同一目录下,或者设置环境变量,让系统搜索到。比较常用的是隐式链接。

1. 隐式链接

隐式链接需要三个文件:.h文件.lib文件 和 .dll文件。可以通过三种方式调用

  • 设置头文件包含目录和链接库目录。也就是.h文件 和 .lib文件,dll文件放在exe目录下或者设置环境变量,让系统搜索到。
  • 利用预编译指令#pragma comment

#include "../yakeDLL/yakeDLL.h"
#pragma comment(lib, "../Debug/yakeDLL.lib")

  • 如果DLL项目和控制台项目在同一解决方案下,可以采用直接添加引用的方式。

yakeEXE_DLLTest.cpp 

#include <iostream>

#include "../yakeDLL/yakeDLL.h"

int main()
{
	fnMyDLLWithDllExport(std::cout);
	fnMyDLLWithExternC(std::cout);

	int c = Add(1, 2);
	cout << c << endl;

	system("PAUSE");
}

2. 显式调用 

  • 显式链接只需要一个文件:.dll文件
  • 所谓显式链接,就是直接调用WIN32 API函数LoadLibraryGetProcAddressFreeLibrary显式地装载、卸载dll。
#include <Windows.h> // 必要头文件

#include <iostream>
using namespace std;

HINSTANCE hDLL;                     // Handle to DLL
void (*MyDLLExport)(ostream& os);
void (*MyDLLExportExternC)(ostream& os);
int(*MyAdd)(int , int ); 

int main()
{	
	hDLL = LoadLibrary(L"yakeDLL.dll");
	MyDLLExport = (void (*)(ostream&)) GetProcAddress(hDLL, "fnMyDLLWithDllExport");
	MyDLLExportExternC = (void (*)(ostream&)) GetProcAddress(hDLL, "fnMyDLLWithExternC");
	MyAdd = (int (*)(int , int )) GetProcAddress(hDLL, "Add");

	if (MyDLLExport == NULL || MyDLLExportExternC == NULL || MyAdd == NULL)
	{
		std::cout << "DLL load error.\n";
		return -1;
	}

	MyDLLExport(std::cout);
	MyDLLExportExternC(std::cout);

	int c = MyAdd(1, 2);
	cout << c << endl;

	system("PAUSE");

	FreeLibrary(hDLL);
	return 0;
}

2.2 C# 显式调用dll

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using System.Runtime.InteropServices; // DLL import

namespace DLLTest_CSharpConsole
{
    class Program
    {
        [DllImport("YAKEDLL.dll")]
        internal static extern int Add(int a, int b);

        static void Main(string[] args)
        {
            Console.WriteLine(Add(1, 2));
            Console.ReadLine();
        }
    }
}

[DllImport("YAKEDLL.dll", EntryPoint = "Add", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]

猜你喜欢

转载自blog.csdn.net/yaked/article/details/109025157