正文

由MessageBox透视Win API的调用2008-12-07 21:56:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/xman/39825.html

分享到:

   下面我们来看看Windows平台下应用程序是怎么调用Windows提供的底层API服务运行的。

我们编写Win32SDK程序时,需要弹出对话框以作出友好的选择,MessageBox这个API函数就可以实现该功能。在开头要添加<windows.h>因为其包含了众多的API函数声明头文件。为了探究这个小小的MessageBox是怎么弹出来的,我们右击MessageBox,选择“Go to definition of MessageBox(转到定义) 将打开<winuser.h>中的#define MessageBox  MessageBoxW定义行,我们继续对MessageBoxW右击Go to definition of MessageBox(转到定义) 将转到MessageBoxW的函数原型声明处:

int

WINAPI

MessageBoxW(

    __in_opt HWND hWnd,

    __in_opt LPCWSTR lpText,

    __in_opt LPCWSTR lpCaption,

__in UINT uType);

我们在使用Windows窗口操作系统时,经常会蹦出大大小小的窗口, MessageBox只是Windows作为提示的对话框窗口单元。那么,MessageBoxW这个API函数到底在哪里实现的呢?应用程序是如何调用系统接口函数的呢?

动态链接库(.dll

实际上Windows API函数是定义在一些DLL中的, DLL 实现了代码封装,从这个角度来看DLL才是真正意义上的API函数包,它是非开源Windows操作系统提供给我们的底层接口。DLL的编制与具体的编程语言及编译器无关。

动态链接库dll文件(Linux中与之对应的是的.so存放在C:\WINDOWS\system目录和C:\WINDOWS\system32目录下,它在被应用程序调用时才同程序相链接。

其中最重要的DLLUser32.dllGdi32.dllKernel32.dll这三个库文件。这三个库文件中的API函数都在Windows.h头文件中进行了声明。从功能上进行分类,User32.dllWindows XP USER API Client DLL)定义了窗口管理函数,包括窗口的创建、显示、设置和移动等;Gdi32.dllGDI Client DLL)定义了图形设备函数(GDI),实现与设备无关的绘图功能;Kernel32.dllWindows NT BASE API Client DLL)定义了系统服务函数,包括诸如内存调度、进程管理等与操作系统有关的底层功能。我们可以通过VC6自带的Depends工具或Dll函数查看器来一窥内幕,见下图。

DLL文件包括了具体实现的代码编译后的结果(二进制的机器码),而头文件就是打开.dll库文件的钥匙。所以我们若要使用MessageBoxW只需#include <winuser.h>(已被<windows.h>包含)。

VS编译器自带的标准函数库<stdio.h><stdlib.h><string.h><math.h>中声明的函数可以到C:\Program Files\Microsoft Visual Studio 8\VC\crt\src中查看相关实现源代码。例如strcat.c中实现了strcatstrcpy函数。有些函数的实现还是要调用底层API,例如C标准库函数create用于创建文件,但它是靠调用CreateFilekernel32.dll函数来完成创建文件功能的;beginthread(process.h,thread.c)需要调用 CreateThreadkernel32.dll函数。

Windows将遵循下面的搜索顺序来定位 DLL 包含EXE文件的目录>进程的当前工作目录>Windows系统目录>Windows目录>列在 Path 环境变量中的一系列目录.

如果你在本机编写一个Windows应用程序,移植到其他机子上(当然也是Windows操作系统),有可能因为缺少相关DLL文件而无法执行。因为DLL是动态链接,就是随用随加载,这就是为什么我们玩3D游戏时经常弹出缺少d3dx9***.dll的错误提示。如果启动的程序调用了一个过期的DLL文件或不匹配的DLL文件,则会出现“未定义的动态链接调用”消息。

动态链接库除了实现代码的共享外,其模块封装特性使得应用程序在调用一个DLL的不同版本时,只要导出的函数名相同就不必进行重新编译链接。这样,软件产品在更新或升级时,客户程序不必进行改动。在开发软件产品时,对于通用功能的函数,一般以DLL的形式来实现。Windows设备驱动程序就是体现上述特点的动态链接库。

动态链接库的调用方式又分为隐式调用(也称静态调用,需要.lib文件)和显式调用(也称动态调用,LoadLibrary->GetProcAddress->FreeLibrary)。

静态链接库(.lib

在早期库的组织形式相对简单,里面的目标代码只能够进行静态链接,所以我们称为静态库,静态库的结构比较简单,其实就是把原来的目标代码放在一起,链接程序LINK根据每一份目标代码的符号表查找相应的符号(函数和变量的名字),找到的话就把该函数里面需要定位的进行定位,然后将整块函数代码放进可执行文件里,若是找不到需要的函数就报错退出。标准Turbo C2.0中的C库函数,例如scanfprintfmemcpystrcpy等,就是使用的静态库技术。

以下是C程序的编译链接过程:(1)执行cl /c main.c;cl /c lib1.c;cl /c lib2.c生成了main.obj lib1.obj lib2.obj三个文件;(2)执行link /lib lib1.obj;link /lib lib2.obj#生成了2个文件lib1.lib lib2.lib;(3)执行link main.obj lib1.lib lib2.lib生成main.exe.
   
静态链接libLinux中与之对应的是的.a的两个特点:
    #1
链接后产生的可执行文件包含了所有需要调用的函数的代码,因此占用磁盘空间较大。
    #2
如果有多个(调用相同库函数的)进程在内存中同时运行,内存中就存有多份相同的库函数代码,因此占用内存空间较多。

我们可以用记事本打开C:\Program Files\Microsoft Visual Studio\VC98\Lib中的USER32.LIB文件,其中有

__imp__MessageBoxA@16 _MessageBoxW@16  //    这里16为参数的字节数

? _MessageBoxW@16 USER32.dll USER32.dll/     889206797

静态链接库lib文件中存放的是接口函数申明的入口地址,dll中存放的是函数实体!当我们隐式调用dll时,需要在Link选项指明其对应的lib库。lib告诉编译器你的dll都导出了什么函数,以及这些函数的地址名称,运行的时候就根据这些信息到dll里面去找。

实际上用VC6.0新建一个Win32 Console Application时,我们查看Project Settings>Link>Object/library modules中发现VC默认已连接了kernel32.libuser32.libgdi32.lib等常用的lib文件。如果需要显式设置的话,比如在网络编程中需要添加WS2_32.LIB库,则可以在文件的开头使用 #pragma  comment(lib,"WS2_32.LIB")命令

在编写MFC项目时,我们打开Project Settings>GeneralMicrosoft Foundation Classes,里面有两种链接方式:Use MFC in a Static Library Use MFC in a Shared Library。对应在Visual Studio 2005中项目属性—>配置属性—>常规—>MFC的使用中设置链接方式。

如果选择Use MFC in a Shared Library的话,你编译后的程序中不包含MFC库,所以文件会比较小,但是如果你的程序直接移到一个没有安装过MFC的机器上时,可能会导致找不到MFCDLL,故发布时要带MFCDLL文件。如果选择Use MFC in a Static Library,那么编译后的程序就直接包含了调用MFC的部分的库,文件可能会大一些,但是可以直接移到其他机器上运行,即发布时不用带MFCDLL文件。

   参考:动态链接库DLL的创建和使用

阅读(5373) | 评论(1)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册