`
sabolasi
  • 浏览: 928167 次
文章分类
社区版块
存档分类
最新评论

由MessageBox透视Win32 API的调用

 
阅读更多

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

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

// WINUSER.H

WINUSERAPI

int

WINAPI

MessageBoxA(

HWND hWnd ,

LPCSTR lpText,

LPCSTR lpCaption,

UINT uType);

WINUSERAPI

int

WINAPI

MessageBoxW(

HWND hWnd ,

LPCWSTR lpText,

LPCWSTR lpCaption,

UINT uType);

#ifdef UNICODE

#define MessageBox MessageBoxW

#else

#define MessageBox MessageBoxA

#endif // !UNICODE

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

动态链接库(.dll

初窥DLL

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

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

其中Windows系统最重要的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)定义了系统服务函数,包括诸如内存调度、进程管理等与操作系统有关的底层功能。我们可以通过三种方式来查看DLL文件中的导出函数信息:(1)使用Windows系统的dumpbin命令(在命令行中:C:/WINDOWS/system32>dumpbin ntdll.dll /exports);(2)使用VC自带的Depends工具(Microsoft Visual Studio 6.0 ToolsàDependsVisual Studio 2005命令行提示àdepends);(3Dll函数查看器ViewDllExeScope)。以下使用VC自带的Depends工具查看user32.dllMessageBox函数系列。

DLLAPI

正如Java的跨平台(Write Once, Run Everywhere)需要JVM的支持一样,C/C++成为跨平台的编程语言,依赖各个平台(包括操作系统和编译器)对C/C++标准函数库的具体平台实现。

VS编译器自带的标准C函数库<stdio.h><stdlib.h><string.h><math.h>中声明的函数可以到C:/Program Files/Microsoft Visual Studio/VC98/CRT/SRC(C:/Program Files/Microsoft Visual Studio 8/VC/crt/src)中查看相关实现源代码。strcat.c文件提供了strcatstrcpy函数的源码 ,Windows系统中,C:/WINDOWS/system32/ntdll.dll提供了strcatstrcpy这两个CRT API的底层实现。用户对C函数的调用最终通过调用底层API来完成真正的功能。例如C标准库函数create用于创建文件,但它是靠调用CreateFilekernel32.dll函数来完成创建文件功能的;beginthread(process.h,thread.c)需要调用 CreateThreadkernel32.dll函数来完成线程的创建。

DLL的移植升级

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

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

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

DLL的调用

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

那么,在我们编写的程序中,如何调用DLL中的API呢?既然用户对C函数的调用最终是通过调用底层API来完成真正的功能,那么,我们在编写第一个Hello World程序时就已经不知不觉地调用了DLL。实际上<stdio.h>中定义的printf函数由msvcrt.dll函数导出,而*printf系列函数在ntdll.dll中有具体底层实现。

DLL文件包括了具体实现的代码编译后的结果(二进制的机器码),而头文件中的代码主要是DLL库文件导出函数的原型声明。<winuser.h>即主要对user32.dll中导出的函数做声明索引,所以若要使用MessageBox(A/W)#include <winuser.h>(已被<windows.h>包含)。正如调用printf函数需要#include <stdio.h>

在调用printf函数或MessageBox函数时,我们仅仅包含了声明头文件,没有显式LoadLibraryàGetProcAddress,那我们是如何是隐式调用(定位)DLL中的API的呢?实际上我们在利用VC向导生成一个Win32 Console Application时,向导已经为项目设置了link项(Project Settings àLinkàInputàObject/library modules),其中默认链接kernel32.libuser32.libgdi32.lib等。

静态链接库(.lib

目标代码集静态链接库

在早期库的组织形式相对简单,里面的目标代码只能够进行静态链接,所以我们称为“静态库”,静态库的结构比较简单,其实就是把原来的目标代码(*.obj)集合在一起,链接程序LINKER根据每一份目标代码的符号表查找相应的符号(函数和变量的名字),找到的话就把该函数里面需要定位的进行定位,然后将整块函数代码放进可执行文件里,若是找不到需要的函数就报错退出。标准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

静态链接lib库(Linux中与之对应的是的.a的两个特点:

1)链接后产生的可执行文件包含了所有需要调用的函数的代码,因此占用磁盘空间较大。

2)如果有多个(调用相同库函数的)进程在内存中同时运行,内存中就存有多份相同的库函数代码,因此占用内存空间较多。

DLL隐式调用静态链接库

使用DLL隐式链接时,可执行程序链接到一个包含DLL导出函数信息的输入库文件(.LIB文件)。操作系统在加载使用可执行程序时加载DLL。可执行程序直接通过函数名像调用其他源文件中的函数一样调用DLL中的导出函数。

我们可以用记事本打开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中相应的API

除了在“Project SettingsàLinkàInputàobject/library modules”中填写静态链接库(*.lib)外,我们还可以通过#pragma comment宏显示输入*.lib库文件,例如在网络编程中需要添加WS2_32.LIB库,则可以在文件的开头包含头文件后#pragma comment(lib,"WS2_32.LIB")引入静态链接库文件。

由于我们经常要调用一些第三方厂商或其他编译器编写的动态链接库,但是一般都不提供源文件或.lib文件。我们若知道相应API的函数原型,通过LoadLibraryàGetProcAddress以实现正确的调用。如果隐式调用,则需要lib文件,可使用DLL2LIB工具生成DLL对应的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的创建和使用

深入探究VC-编译CL.EXE

隐式链接无.LIB动态链

分享到:
评论

相关推荐

    win32 API 调用

    ### Win32 API调用详解 #### 一、概述 Windows API(Application Programming Interface),即Windows应用程序编程接口,是微软Windows操作系统提供的一组标准应用程序接口集合。这些API函数构成了构建Windows应用...

    python 弹窗提示警告框MessageBox的实例

    win32api.MessageBox(0, 这是一个测试提醒OK消息框, 提醒,win32con.MB_OK) ##是否信息框 win32api.MessageBox(0, 这是一个测试是否信息框, 提醒,win32con.MB_YESNO) ##说明信息框 win32api.MessageBox(0, 这是一个...

    Win32 API 查询工具

    Win32 API查询工具是开发者用来查找和理解这些API函数的实用软件,它可以帮助开发人员确定特定功能的函数调用方式、参数类型和返回值等信息。 在编程过程中,尤其是在使用Visual Basic (VB) 这样的高级语言进行...

    从.NET平台调用Win32 API

    - 假设我们要调用Win32 API的`MessageBox`函数,来自user32.dll,它的原型是: ```c int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); ``` - 在C#中,我们可以这样定义: ```...

    VB windows API AND WIN32 API大全

    例如,`MsgBox`函数在VB中是内置的,但其底层实现就是通过调用Win32 API的`MessageBox`函数完成的。你可以通过如下方式在VB中声明并使用`MessageBox`: ```vb Private Declare Function MessageBox Lib "user32" ...

    VBS调用API必须使用的dll_dynwrap

    描述提到,VBS可以通过使用dll_dynwrap DLL文件来调用Win32 API,这使得VBS的能力大大增强。Win32 API是Windows操作系统的核心组件,提供了一系列函数,允许开发者进行文件操作、进程控制、网络通信等复杂任务。...

    vb.net c#.net win32 API 查询工具(旧的)

    4. **Win32Api.mdb**: 这个文件可能是一个数据库文件,包含了关于Win32 API的详细信息,如函数名、参数、返回值等。开发者可以通过查询这个数据库,获取API的使用指南和示例。 5. **SyntaxHighlightingTextBox....

    WIN32API功能150个(系统对话框篇).e.rar

    WIN32API,全称Windows 32位应用程序接口,是微软Windows操作系统提供给开发者用于构建应用程序的一系列函数、常量、数据结构等接口。它为程序员提供了对操作系统底层功能的访问权限,使得开发者能够创建高效、原生...

    Win32 API Programming with Visual Basic, Fourth Edition 随书光盘

    Visual Basic作为易用的事件驱动编程语言,通常自带的函数库不足以满足所有高级或系统级的需求,这时就需要调用Win32 API。 **API编程的基本概念** 1. **API函数**:API提供了许多函数,如窗口创建、消息处理、内存...

    PowerBuilder编程Win32API函数调用实例

    PowerBuilder与Win32 API函数调用 PowerBuilder作为一款强大的数据库应用开发工具,提供了丰富的组件和功能,但在某些特定需求下,如高级窗口管理、设备检测或系统操作等,其内置功能可能显得力不从心。这时,借助...

    新编WIN32API大全

    ### 新编WIN32API大全知识点总结 #### 一、Win32 API概述 - **定义**:Win32 API(Application Programming Interface)是微软32位操作系统平台上的应用程序编程接口,适用于Windows 9x、Windows NT 3.1/4.0/5.0、...

    Win32 中文文档 API大全.rar

    例如,你可以使用`win32api.MessageBox`创建一个弹出消息框,或者使用`win32api.GetSystemMetrics`获取系统的各种指标。除此之外,pywin32还提供了其他几个模块,如`win32con`用于常量定义,`win32process`用于进程...

    常用Win32 API函数简介

    ### 常用Win32 API函数简介 在Windows编程中,Win32 API(应用程序接口)是一组广泛使用的函数集合,它为开发者提供了与Windows操作系统进行交互的能力。本文将详细介绍几个常用的Win32 API函数及其功能。 #### 1....

    C#调用Win32类库几个例子.rar

    在.NET框架中,C#作为主要的编程语言之一,提供了丰富的功能和库,但有时我们需要访问Windows操作系统底层的功能,这就需要调用Win32 API(应用程序接口)。本压缩包文件"**C#调用Win32类库几个例子.rar**"提供了...

    win32 vbapi集合要使用的去下吧

    在Windows操作系统环境中,Win32 API(应用程序接口)是一组函数和系统调用,开发者可以使用它们来构建原生的Windows应用程序。VBAPI(Visual Basic API)是VB(Visual Basic)编程者对Win32 API的封装,使得VB...

    Win32API

    例如,要调用Win32 API中的`MessageBox`函数,可以这样写: ```csharp [DllImport("user32.dll")] public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type); ``` 这里,`...

    VC++串口编程之基于Win32 API

    在VC++串口编程中,基于Win32 API的方式提供了高效且灵活的接口来操作串行通信。在WIN32 API中,串口被抽象成一种文件类型,因此,你可以像处理普通文件一样来处理串口。这允许开发者使用熟悉的文件操作API来实现...

    Win32各种API函数的Delphi单元

    "Win32各种API函数的Delphi单元"通常是一些包含了大量预定义的Delphi接口,用于方便地调用Win32 API的代码库。这些单元文件中包含了对API函数的声明和实现,使得开发者无需关心底层的C语言调用约定,可以直接在...

    C#dllimport调用 C# 调用Win32

    在.NET框架中,C#程序员经常需要调用非托管代码,比如Windows API(Win32 API)函数,来实现特定的功能。"C# dllimport调用"是.NET平台提供的一种机制,允许C#代码直接与非托管DLL进行交互。这个过程涉及到`...

    WIN32GUI.zip

    例如,如果你需要调用Win32 API的`MessageBox`函数,你需要在C#方法上添加`[DllImport]`特性,指定库名(如`user32.dll`)和函数签名。 ```csharp using System.Runtime.InteropServices; [DllImport("user32.dll...

Global site tag (gtag.js) - Google Analytics