找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 3751|回复: 0

《Windows核心编程》学习笔记(21)– DLL基础(隐式调用DLL)

[复制链接]
发表于 2012-3-17 23:02:04 | 显示全部楼层 |阅读模式

.DLL和进程的地址空间
    创建DLL通常比创建应用程序容易,因为DLL通常由一组可供任何应用程序使用的独立函数组成。在DLL中,通常没有用来处理消息循环或创建窗口的代码。 DLL只不过是一组源代码模块,每个模块包含一些可供应用程序(可执行文件)或其他DLL调用的函数。

在应用程序(或其他DLL)能够调用一个DLL中的函数之前,必须将该DLL的文件映像映射到调用进程的地址空间中。我们可以通过两种方法来达到这一目的:隐式载入时连接 (implicit load-time linking)或显示运行时链接 (explicit run-time linking)。
    一旦系统将一个DLL的文件映像映射到调用进程的地址空间中之后,进程中的所有线程就可以调用该DLL种的函数了。事实上,该DLL几乎完全丧失了它的DLL身份:对进程中的线程来说,该DLL中的代码和数据就像是一些附加的代码和数据,碰巧被放在进程地址空间中。


注意:当一个模块提供一个内存分配函数的时候,它必须同时提供另一个用来释放内存的函数。

例如:

VOID EXEFunc()

{

         PVOID pv = DLLFunc();

         ……

         DLLFreeFunc(pv);

}


PVOID DLLFunc()

{

         PVOID pv = malloc(100);

         return (pv);

}


BOOL DLLFreeFunc(PVOID pv)

{

         return(free(pv));

}



构建DLL需要的步骤

(1)必须先创建一个头文件,在其中包含我们想要在DLL中导出的函数原型、结构以及符号。在构建可执行文件的时候需要用到同一个头文件。
    (2)创建C/C++源文件来实现想要在DLL模块中导出的函数和变量。由于在构建可执行模块的时候不需要这些源文件,因此创建该DLL得公司可以将这些源代码作为公司的机密。
     (3)在构建该DLL模块的时候,编译器会对每个源文件进行处理并产生一个.obj模块(每个源文件对应一个.obj模块)。
     (4)当所有.obj模块都创建完毕后,连接器会将所有.obj模块的内容合并起来,产生一个单独的DLL映像文件。这个映像文件包含DLL中所有的二进制代码以及全局静态变量。为了执行可执行模块,这个文件时必须的。
     (5)如果链接器检测到DLL的源文件输出了至少一个函数或变量,那么链接器还会产生一个.lib文件。这个.lib文件非常小,这是因为它并不包含任何函数或变量。它只是列出了所有被导出的函数和变量的符号名。为了构建可执行模块,这个文件时必须的。


一旦创建了DLL模块,我们将可以通过下列步骤来构建可执行模块:
1)在所有引用了导出的函数、变量、数据结构或符号的源文件中,必须包含由DLL的开发人员所创建的头文件。
     (2)创建C/C++源文件来实现想要包含在可执行模块中的函数和变量。当然,代码可以引用在DLL的头文件中定义的函数和变量。
     (3)在构建可执行模块的时候,编译器会对每个源文件进行处理并产生一个.obj模块(每个源文件对应一个.obj模块)。
     (4)链接器将所有.obj模块的内容合并起来,产生一个单独的可执行映像文件。这个映像文件包含了可执行文件中所有的二进制代码以及全局/静态变量。该可执行模块还包含一个导入段(import section),其中列出了所有它需要的DLL模块的名称。


     一旦DLL和可执行模块都已构建完毕,进程就可以执行了。当我们试图运行可执行模块的时候,操作系统的加载程序会执行下面的步骤。
     (5)加载程序先为新的进程创建一个虚拟地址空间,并将可执行模块映射到新进程的地址空间中。加载程序接着解析可执行模块的导入段。对导入段中列出的每个DLL,加载程序会在用户的系统中对该DLL模块进行定位,并将该DLL映射到进程的地址空间中。注意,由于DLL模块可以从其它DLL模块中导入函数和变量,因此DLL模块可能有自己的导入段并需要将它所需的DLL模块映射到进程的地址空间中。


隐式调用DLL

代码如下:

1.       DLL文件:

/*********************************************************************

Mylib.h

*************************************************************************/

#ifdef _EXPORTDLL

#define MYLIBAPI extern "C" __declspec(dllexport)  

#else

#define MYLIBAPI extern "C" __declspec(dllimport)

#endif


MYLIBAPI int g_nResult;

MYLIBAPI int Add(int a, int b);



/*********************************************************************

Mylib.cpp

*************************************************************************/


#include <windows.h>

#define _EXPORTDLL //需要在#include "Mylib.h" 前面定义

#include "Mylib.h"


int g_nResult;

int Add(int a, int b)

{

        g_nResult = a + b;

        return g_nResult;

}


DLL文件编译完成后可以利用VS的DUMPBIN工具查看导出函数:




2.       EXE文件(必须把DLL工程的.h, .lib, .dll文件放在EXE工程文件夹下):


#include "stdafx.h"

#include <windows.h>

#include <stdio.h>

#include "Mylib.h" //包含Dll的头文件

#pragma comment(lib,"Mylib.lib") //链接.lib文件


int main(void)

{

        int a = Add(1,2);

        printf("Add() = %d\n", a); //可以像平常一样调用函数和变量

        printf("g_nResult = %d\n", g_nResult);

        Sleep(10000);

}


EXE文件运行结果:




由于导入段只包含DLL的名称,不包含DLL的路径,因此加载程序必须在用户磁盘上搜索DLL。下面是夹在程序的搜索顺序:



  • 包含可执行文件的目录;
  • Windows的系统目录,该目录可以通过GetSystemDirectory得到;
  • 16位的系统目录,即Windows目录中的System子目录;
  • Windows目录,该目录可以通过GetWindowsDirectory得到;
  • 进程的当前目录;
  • PATH环境变量中所列出的目录。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?用户注册

×
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-3-29 18:34 , Processed in 0.016274 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表