1.构建DLL模块
1.我们应该首先创建一个头文件来包含想要导出的变量(类型和名称)和函数(原型和名称)。这个头文件还必须定义导出的函数或变量所用到的任何符号和数据结构,DLL链接库的所有源文件都应该包含这个头文件。
动态链接库的头文件和源文件的定义
Module:Mylib.h
#ifdef MYLIBAPI
<span style="background-color: rgb(0, 153, 0);"><span style="color:#33cc00;">
</span>//MYLIBAPI应该在所有的动态链接库的源文件中被定义
//在这个头文件之前代码模块被包含
//所有将要被导出的函数和变量</span>
#else
<span style="background-color: rgb(0, 153, 0);">
//这个头文件被由一个可执行源代码模块所包含。
//声明所有将要被导入的变量和函数</span>
#define MYLIBAPI extrern "C" __declspec(dllimport) //这样编译器就知道该可执行文件的源文件要从DLL模块中导入一些变量和函数(下面有解释)
#endif
//
<span style="background-color: rgb(51, 204, 0);">//在这定义一些数据结构和符号。</span>
/
<span style="background-color: rgb(51, 204, 0);">//在这定义被导出的变量</span>
MYLIBAPI int g_nResult;
<span style="background-color: rgb(0, 153, 0);">//定义被导出的函数原型声明在这里。</span>
MYLIBAPI int Add(int nLeft, int nRight);
</pre><pre name="code" class="cpp">
</pre><pre name="code" class="cpp">Module: MyLibFile1.cpp
在每个需要引用到导入符号的源文件中,必须包含该头文件:
</pre><pre name="code" class="cpp"><pre name="code" class="cpp">//包含一些标准的windows和C运行时头文件在这。
#include<windows.h>
//这个动态链接库源代码文件被导出的函数和变量。
#define MYLIBAPI extren "C" __declspec(dllexport)
//在这包含所有被导入的数据结构,符号,函数和变量。
#include"MyLib.h"
//在这填写动态链接库代码
int g_nResult;
int Add(int nLeft,int nRight)
{
g_nResult = nLeft + nRight;
return(g_nResult);
}
可执行文件的源文件如何使用这个头文件
可执行文件不应该在包含这个头文件之前定义MYLIBAPI。由于MYLIBAPI未定义,因此头文件会将MYLIBAPI定义为_declspec(dllimport),这样编译器就知道该可执行文件的源文件要从DLL模块中导入一些变量和函数。__declspec(dllimport)的作用。
像如下代码
int main()
{
func()
return 0;
}
编译器生成像下面的代码
call func1
连接器传入像下面的代码
call 0x4000000 ; 函数func的地址
如果func在DLL中,连接器就无法得到func的直接地址;在32位系统下,连接器将生成一个thunk包含func的调用地址
0x40000000: jmp DWORD PTR __imp_func
__imp_func事func在.exe导入函数表中的地址,这个地址传入连接器进行连接
这样生成的代码比较大而且速度慢;
而使用__declspec(dllimport) 则直接告诉连接器直接调用DLL中func的地址,不要使用thunk
__declspec(dllimport) void func(void);
void main(void)
{
func1();
}
生成如下代码
call DWORD PTR __imp_func
这样的代码比使用thunk代码小而且要快 出处:http://bbs.csdn.net/topics/20025338
在函数声明上使用 __declspec(dllimport) 是可选操作,但如果使用此关键字,编译器将生成更有效的代码。但是,为使导入的可执行文件能够访问 DLL 的公共数据符号和对象,必须使用 __declspec(dllimport)。请注意,DLL 的用户仍然需要与导入库链接。
对 DLL 和客户端应用程序可以使用相同的头文件。为此,请使用特殊的预处理器符号来指示是生成 DLL 还是生成客户端应用程序。例如:
复制代码
#ifdef _EXPORTING
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif 出处:http://bbs.csdn.net/topics/330169671
对 DLL 和客户端应用程序可以使用相同的头文件。为此,请使用特殊的预处理器符号来指示是生成 DLL 还是生成客户端应用程序。例如:
复制代码
#ifdef _EXPORTING
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif 出处:http://bbs.csdn.net/topics/330169671
你写dll的时候,使用的是dllexport来导出接口,
而别人使用你的dll的时候,相对的来说,是导入接口,即dllimport
使用宏的好处是,只需要一个定义,既可以在你写dll时使用,给别人头文件时也不需要修改相应的定义。
而别人使用你的dll的时候,相对的来说,是导入接口,即dllimport
使用宏的好处是,只需要一个定义,既可以在你写dll时使用,给别人头文件时也不需要修改相应的定义。
创建可执行模块
Module:MyExeFile1.cpp
#include <windows.h>
#include <stdlib.h>
#include <strsafe.h>
#include "Mylib.h"
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
int nLeft = 10,nRrght = 25;
TCHAR sz[100];
StringCchPrintf(sz, _countfo(sz), TEXT("%d + %d = %d"), nLeft, nRight, Add(nLeft,nRight));
<span style="white-space:pre"> </span> MessageBox(NULL,sz,TEXT("Calculation"),MB_OK);
StringCchPrintf(sz, _countof(sz), TEXT("The result from the last Add is:%d"), g_nResult );
MessageBox(NULL, sz, TEXT("Last Result"), MB_OK);
return 0;
}
导入段:当连接器在解决导入符号的时候,会在生成的可执行模块中嵌入一个特殊的段,他的名字叫导入段(import section)。导入段列出了该模块所需的DLL模块,以及他从每个DLL模块中引用的符号。
摘自《Windows 核心编程》一书