这篇先介绍用VS建立和使用DLL的方法。

  1. 建立DLL 新建一个空的Win32 Console Application,类型选DLL。也可以建立后在Project -> Properties -> Configuration Properties -> General -> Configuration Type中改。本例中工程名为MyDll,只包含一个文件:MyDll.cpp:
//MyDll.cpp

#include <stdio.h>

__declspec(dllexport) int k = 3;

__declspec(dllexport) void helloWorld(void)
{
	printf("Hello World\n");
}

编译之后会产生需要的MyDll.lib和MyDll.dll。

  1. 静态链接DLL 新建一个空的Win32 Console Application,类型选通常的Console Application,命名为MyProject,包含两个文件:MyDll.h和MyProject.cpp:
//MyDll.h

__declspec(dllimport) extern int k;
__declspec(dllimport) void helloWorld(void);

//MyProject.cpp

#include <iostream>
#include "MyDll.h"

using namespace std;

int main()
{
	cout << k << endl;
	helloWorld();
}

接下来在Project -> Properties -> Configuration Properties -> Linker -> Input -> Additional Dependencies中填入刚才生成的MyDll.lib的路径(或在代码中加入一行#pragma comment(lib,”MyDll.lib”)(此处假定MyDll.lib在工作目录下),编译。 运行时,需把MyDll.dll置于MyProject.exe可以找到的目录下,DLL的查找顺序为:应用程序所在目录→当前目录→Windows SYSTEM目录→Windows目录→PATH环境变量。 Note. 一般而言,MyDll.h包含了该DLL模块对外开放的接口,应该随MyDll.lib,MyDll.dll一同由DLL的作者提供。

  1. 动态链接DLL 新建一个空的Win32 Console Application,类型选通常的Console Application,命名为MyProject,包含一个文件:MyDynamicProject.cpp:
//MyDynamicProject.cpp

#include <iostream>
#include <windows.h>

using namespace std;

int main()
{
	typedef void(FUNC)(void);

	HINSTANCE hInstance = ::LoadLibrary("D:\\MyDll.dll");
	int* pInt = (int*)::GetProcAddress(hInstance,"k");
	FUNC* pFunc = (FUNC*)::GetProcAddress(hInstance,"helloWorld");

	cout << *pInt << endl;
	(*pFunc)();

	::FreeLibrary(hInstance);

	return 0;
}

这里假定我们需要的DLL为D:\MyDll.dll。编译这个程序,运行一下,崩了。 这是因为C++编译器为了支持重载,使用了name mangling,生成 的符号名称并不与函数名称一致(请参考http://www.cs.indiana.edu/~welu/notes/node36.html)。令其使用C编译 方式即可解决问题。修改MyDll工程中的MyDll.cpp为:

//MyDll.cpp

#include <stdio.h>

extern "C"
{
	__declspec(dllexport) int k = 3;

	__declspec(dllexport) void helloWorld(void)
	{
		printf("Hello World\n");
	}
}

重新编译DLL,即可顺利执行。 Note. 细心的读者一定注意到这样的话2中静态编译的那个工程就不work了,因为它期望找到一个C++方式的函数符号,而DL L却是用C方式编译的。解决的办法也很简单,把MyDll.h改为:

//MyDll.h

extern "C"
{
	__declspec(dllimport) extern int k;
	__declspec(dllimport) void helloWorld(void);
}

重新编译,就可以了。 Note. 事实上,如果DLL的作者想要用户以显式链接的方式使用DLL,应该使用extern “C”编译,否则用户不知道正确的函数名。

  1. DllMain DllMain是DLL的入口函数。DllMain在被进程、线程加载、卸载时调用。前面的例子里没有这个函数,DLL也能正常工作,因为系统找不到DllMain函数时,将引入一个什么也不做的DllMain。将工程MyDll的MyDll.cpp改为以下代码来查看DllMain的作用。
//MyDll.cpp

#include <stdio.h>
#include <windows.h>

extern "C"
{
	__declspec(dllexport) int k = 3;

	__declspec(dllexport) void helloWorld(void)
	{
		printf("Hello World\n");
	}
}

BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		printf("DLL_PROCESS_ATTACH\n");
		break;
	case DLL_PROCESS_DETACH:
		printf("DLL_PROCESS_DETACH\n");
		break;
	case DLL_THREAD_ATTACH:
		printf("DLL_THREAD_ATTACH\n");
		break;
	case DLL_THREAD_DETACH:
		printf("DLL_THREAD_DETACH\n");
		break;
	}
	return TRUE;
}

Note. DLL工程中,若变量或函数的第一个声明包含了__declspec(dllexport),则此后的声明和定义可以不加这个标志。但是,若第一个声明没 有加__declspec(dllexport),而此后的声明和定义中出现了__declspec(dllexport),则编译不能通过,因为编译器不确定这个变 量或函数是否要导出。

基于这个特性,开发DLL时可以先不管导出的事。工程完成后,对于需要导出的变量和函数,将其声明加上__declspec(dllexport)写在一个特定的头文 件里,并把这个头文件置于每个源文件的开头。对于需要导出的类,在其定义前加上__declspec(dllexport)即可,因为即便类的声明没有__d eclspec(dllexport)而类的定义有,也不影响编译。

将上述特定的头文件存一个副本,把需要导出的类定义提取出来合并到这个副本中,最后将所有__declspec(dllexport)改为__declspec(dl limport),即是给用户的.h文件。

想知道更具体的可以看一下这里