注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

从C开始

 
 
 

日志

 
 

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

2010-11-07 16:03:13|  分类: 《Windows核心编 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

.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工具查看导出函数:

《Windows核心编程》学习笔记(21)– DLL基础(隐式调用DLL) - Fly - y1生倾于晴
 

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文件运行结果:

《Windows核心编程》学习笔记(21)– DLL基础(隐式调用DLL) - Fly - y1生倾于晴
 


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

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

  评论这张
 
阅读(488)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018