logo资料库

Win32 API讲座 经典教程.pdf

第1页 / 共66页
第2页 / 共66页
第3页 / 共66页
第4页 / 共66页
第5页 / 共66页
第6页 / 共66页
第7页 / 共66页
第8页 / 共66页
资料共66页,剩余部分请下载后查看
Win32 API 讲座讲座讲座讲座 第一课第一课第一课第一课∶∶∶∶认识认识认识认识 API 第一课∶认识 API 一、什么是 API 首先,有必要向大家讲一讲,什么是 API。所谓 API 本来是为 C 和 C++程序员写的。API 说来说去,就是一种函数,他们包含在一个附加名为 DLL 的动态连接库文件中。用标准的 定义来讲,API 就是 Windows 的 32 位应用程序编程接口,是一系列很复杂的函数,消息和 结构,它使编程人员可以用不同类型的编程语言编制出的运行在 Windows95 和 Windows NT 操作系统上的应用程序。可以说,如果你曾经学过 VC,那么 API 对你来说不是什么问题。 但是如果你没有学过 VC,或者你对 Windows95 的结构体系不熟悉,那么可以说,学习 API 将是一件很辛苦的事情。 如果你打开 WINDOWS 的 SYSTEM 文件夹,你可以发现其中有很多附加名为 DLL 的文 件。一个 DLL 中包含的 API 函数并不只是一个,数十个,甚至是数百个。我们能都掌握它 嘛?回答是否定的∶不可能掌握。但实际上,我们真的没必要都掌握,只要重点掌握 Windos 系统本身自带的 API 函数就可以了。但,在其中还应当抛开掉同 VB 本身自有的函数重复的 函数。如,VB 的 etAttr 命令可以获得文件属性,SetAttr 可以设置文件属性。对 API 来讲也 有对应的函数 GetFileAttributes 和 SetFileAttributes,性能都差不多。如此地一算,剩下来 的也就 5、600 个。是的,也不少。但,我可以敢跟你说,只要你熟悉地掌握 100 个,那么你的编程水平 比现在高出至少要两倍。尽管人们说 VB 和 WINDOWS 具有密切的关系,但我认为,API 更 接近 WINDOWS。如果你学会了 API,首要的收获便是对 WINDOWS 体系结构的认识。这 个收获是来自不易的。 如果你不依靠 API 会怎么样?我可以跟你说,绝大多是高级编程书本(当然这不是书的名 程叫高级而高级的,而是在一开始的《本书内容》中指明《本书的阅读对象是具有一定 VB 基础的读者》的那些书),首先提的问题一般大都是从 API 开始。因此可以说,你不学 API, 你大概将停留在初级水平,无法往上攀登。唯一的途径也许就是向别人求救∶我快死了,快 来救救我呀,这个怎么办,那个怎么办?烦不烦呢?当然,现在网上好人太多(包括我在内, 嘻嘻),但,你应当明白,通过此途径,你的手中出不了好的作品。这是因为缺乏这些知识 你的脑子里根本行不成一种总体的设计构思。 二、API 文本游览器。 很 多 API 函 数 都 是 很 长 很 长 的 。 想 看 什 么 样 子 吗 ? 如 下 就 是 作 为 例 子 的 API DdeClientTransaction 函数∶ Declare Function DdeClientTransaction Lib "user32" (pData As Byte, ByVal cbData As Long, ByVal hConv As Long, ByVal hszItem As Long, ByVal wFmt As Long, ByVal wType As Long, ByVal dwTimeout As Long, pdwResult As Long) As Long 哇!这么长?如果你从来没有接触过 API,我想你肯定被吓住了。你也许考虑,该不该继 续学下去。不过不要担心,幸运的是 Microsoft 的设计家们为我们提供了有用的工具,这便 是 API
文本查看器。 通过 API 文本查看器,我们可以方便地查找程序所需要的函数声明、结构类型和常数, 然后将它复制到剪贴板,最后再粘贴到 VB 程序的代码段中。在大多数情况下,只要我们确 定了程序所需要的函数、结构和常数这三个方面后,就可以通过对 API 文本游览器的以上操 作将他们加入到程序段中,从而程序中可以使用这些函数了。这些是学习 API 最基本的常识 问题,它远远占不到 API 的庞大的体系内容。今后我们把精力浪费(这绝不是浪费)在哪里呢? 那就是∶ 什么时候使用什么函数,什么时候使用什么结构类型,什么时候使用什么常数。 三、API 函数声明。 让我们回想一下。在 VB 中,如何声明函数呢?我想,如果你正在看此文,那么你绝对能 够回答得出这个问题。以下便是你应该很熟悉的函数声明∶ Function SetFocus (ByVal hwnd As Long) As Long 即,这行代码定义了名为 SetFocus 的函数,此函数具有一个 Long 型数据类型的参数,并 按值传递(ByVal),函数执行后将返回一个 Long 型数据。 API 函数的声明也很类似,如,API 中的 SetFocus 函数是这样写的∶ Declare Function SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long 有点复杂了一些。是的,是复杂了点。但我可以告诉你,除了这些多出来的部分,其他 部分还是和你以前学到的东西是一样的。函数在程序中的调用也是一样。如: Dim dl As Long dl&=SetFoucs(Form1.Hwnd) 但,一点是清楚的。它不象你自己写的程序那样能够看到里面的运行机理,也不像 VB 自带的函数那样,能够从 VB 的联机帮助中查到其用法。唯一的方法就是去学、查 VB 以外 的资料。 Declare 语句用于在模块级别中声明对动态链接库 (DLL) 中外部过程的引用。对此,你 只要记住任何 API 函数声明都必须写这个语句就可以了。 Iib 指明包含所声明过程或函数的动态链接库或代码资源。也就是说,它说明的是,函 数或过程从何而来的问题。 如在上例中,SetFocus Lib "user32"说明 函数 SetFocus 来自 user32.dll 文件。主要的 dll 动态连接库文件有∶ user32.dll Windows 管理。生成和管理应用程序的用户接口。 GDI32.dll 图形设备接口。产生 Windows 设备的图形输出 Kernel32.dll 系统服务。访问操作系统的计算机资源。 注意,当 DLL 文件不在 Windows 或 System 文件夹中的时候,必须在函数中说明其出处 ( 路径)。如,SetFocus Lib "c:\Mydll\user32" 函数声明中的 Alias 是可选的。表示将被调用的过程在动态链接库 (DLL) 中还有另外的 名称(别名)。如,Alias "SetFocus" ,说明 SetFocus 函数在 User32.dll 中的另外一个名称是, SetFocus。怎么两个名都一样呢?当然,也可以是不同的。在很多情况下,Alias 说明的函数
名,即别名最后一个字符经常是字符 A,如 SetWindowsText 函数的另一个名称是 SetWindowsTextA,表示为 Alias "SetWindowsTextA"。这个 A 只不过是设计家们的习惯的命 名约定,表示函数属于 ANSI 版本。 那么,别名究竟有什么用途呢?从理论上讲,别名提供了用另一个名子调用 API 的函数 方法。如果你指明了别名,那么 尽管我们按 Declare 语句后面的函数来调用该函数,但在 函数的实际调用上是以别名作为首要选择的。如,以下两个函数(Function,ABCD)声明都是 有效的,他们调用的是同一个 SetFocus 函数∶ Declare Function SetFocus Lib "user32" "SetFocus" (ByVal hwnd As Long) As Long Declare ABCD SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long 需要注意的是,选用 Alias 的时候,应注意别名的大小写;如果不选用 Alias 时的时候, 函数名必须注意大小写,而且不能改动。当然,在很多情况下,由于函数声明是直接从 API 文本游览器中拷贝过来的,所以这种错误的发生机会是很少的,但您有必要知道这一点。 最后提醒你一句,API 声明(包括结构、常数)必须放在窗体或模块的"通用(General Declarations)段。 (杭州元帅注:在窗体里的 API 声明必须 Private Declare Function ......) 四、数据类型与"类型安全" API 函数中使用的数据类型基本上和 VB 中的一样。但作为 WIN32 的 API 函数中,不存 在 Integer 数据类型。另外一点是在 API 函数中看不到 Boolean 数据类型。 Variant 数据类型在 API 函数中是以 Any 的形式出现,如 Data As Any。尽管其含义是允许任意参数类型作为一个该 API 函数的参数传递,但这样做存在一定的缺点。其原因是,这将会使得对目标参数的所有 类型检查都会被关闭。这自然会给各种类型的参数调用带来了产生错误的机会。 为了强制执行严格的类型检查,并避免上面提到的问题,一个办法是在函数里使用上面 提到到 Alias 技术。如对 API 函数 GetDIBits 可进行另外一种声明方法。如下∶ GetDIBits 函数的原型∶ Public Declare Function GetDIBits Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long GetDIBits 函数的改型∶ Public Declare Function GetDIBitsLong Lib "gdi32" Alias "GetDIBits" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Long, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long 通 过 本 课 程前 面 所学 到的 知 识 , 我们 已 经可 以得 知 原 型 GetDIBits 函数 也 好 , 改型 GetDIBitsLong 函数也好,实际将调用的都是 Alias 所指定的 GetDIBits 原函数。但你应当看 到,两者的区别在于,我们在改型的函数中强制指定 lpBits 参数为 Long 形。这样就会使得 函数调用中发生的错误机率减少到了最小。这种方法叫做"安全类型"声明。
API 函数中经常看到的数据类型有∶Long,String,Byte,Any....(也就这些吧。) 五、常数 对于 API 常数来讲,没有什么太特别的学问。请看 VB 中的以下代码∶ Msg = MsgBox("您好", vbOKCancel) 我们知道, vbOKCancel 这个常数的值等于 1。对上面的代码我们完全可以这样写,而 不会影响代码的功能∶ Msg = MsgBox("您好", 1) 但你大概不太愿意选择后一种,因为这会使得看懂代码费劲起来。这种方法也被 API 采 取了。只是 API 常数必须在事情之前做好初始化声明 VB 本身是看不懂的。其内容仍然来自 与 API 文本游览器。具体形式如下等等∶ Public Const ABM_ACTIVATE = &H6 Public Const RIGHT_CTRL_PRESSED = &H4 Public Const RPC_E_SERVER_DIED = &H80010007 Private Const RPC_S_CALL_FAILED_DNE = 1727& 在常数的初始化中,有些程序使用 Global,如 Global Const ABM_ACTIVATE = &H6,但 我认为 Public 完全可以代替它。过去我也用过 Global,但现在不大用了。一会儿用这个, 一会儿用那个,各程序之间不能保持一致性了,起码看起来别扭。 六、结构 结构是 C 和 C++语言中的说法。在 VB 中一般称为自定义数据类型。想必很多朋友都已 经认识它。在 API 领域里,我更喜欢把它叫做结构,因为 API 各种结构类型根本不是我定义 ( 自定义)的。 在 VB 中,API 结构同样由 TYPE.......END TYPE 语句来定义。如,在 API 中,点(Point) 结构的定义方法如下: Public Type POINTAPI X As Long '点在 X 坐标(横坐标)上的坐标值 Y As Long '点在 Y 坐标(纵坐标)上的坐标值 End Type 又如,API 中矩形(Rect)结构的定义如下∶ Public Type RECT Left As Long '矩形左上角的 X 坐标 Top As Long '矩形左上角的 Y 坐标 Right As Long '矩形右下角的 X 坐标 Bottom As Long '矩形右下角的 Y 坐标 End Type 这些内容同样可以从 API 文本游览器中拷贝过来。这些结构中的变量名可随意改动,而 不会影响结构本身。也就是说,这些成员变量都是虚拟的。如,POINTAPI 结构可改为如下∶
Public Type POINTAPI MyX As Long '点在 X 坐标(横坐标)上的坐标值 MyY As Long '点在 Y 坐标(纵坐标)上的坐标值 End Type 不过,一般来讲,是没有这种必要的。结构本身是一种数据类型,因此,使用时必须声 明具体变量为该结构型,才能在程序中真正使用到该结构。结构的声明方法和其他数据的声 明方法一样,如,以下语句把变 MyPoint 声明为 POINTAPI 结构类型∶ MyPoint As POINTAPI 引用结构中的成员变量也十分简单,在结构名后面加上一个".",然后紧接着写要引用的 成员变量即可。这很象 VB 中的引用一个对象的某个属性。如,假如我们把上面已经声明的 MyPoint 结构中的 X 变量的值赋给变量 Temp& 则代码如下∶ Temp&=MyPoint.X 但,特别注意的是,你千万不要认为上例中的 MyPoint 是一个值。它不是值,而是地址 ( 指针)。值和地址是完全不同的概念。结构要求按引用传递给 WINDOWS 函数,即所有 API 函数中,结构都是按 ByRef 传递的(在 Declare 语句 中 ByRef 是默认型)。对于结构的传递, 你不要试图采用 ByVal,你将一无所获。由于结构名实际上就是指向这个结构的指针(这个结 构的首地址),所以,你也就传送特定的结构名就可以了(参见小结,我用红色字体来突出了 这种传递方式)。 由于结构传送的是指针,所以函数将直接对结构进行读写操作。这种特性很适合于把函 数执行的结果装载在结构之中。 七、小结 以下的程序是为了总结本课中学到的内容而给出的。启动 VB,新建一个项目,添加一 个命令按钮,并把下面的代码拷贝到代码段中,运行它。 Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Private Type POINTAPI '定义点(Point)结构 X As Long '点在 X 坐标(横坐标)上的坐标值 Y As Long '点在 Y 坐标(纵坐标)上的坐标值 End Type Sub PrintCursorPos( ) Dim dl AS Long Dim MyPoint As POINTAPI dl&= GetCursorPos(MyPoint) '调用函数,获取屏幕鼠标坐标 Debug.Print "X=" & Str(MyPoint.X) & " and " & "Y=" & Str(MyPoint.Y) End Sub Private Sub Command1_Click() PrintCursorPos
End Sub 输出结果为(每次运行都可能得到不同的结果,这得由函数调用时鼠标指针在屏幕中所处 的位置而决定)∶ X= 240 and Y= 151 程序中,GetCursorPos 函数用来获取鼠标指针在屏幕上的位置。 以上例子中,你可以发现,以参数传递的 MyPpint 结构的内容在函数调用后发生了实质 性变化。这是由于结构是按 ByRef 传递的原因。
Win32 API 讲座讲座讲座讲座 第二课第二课第二课第二课∶∶∶∶句柄句柄句柄句柄、、、、矩形和画点函数 矩形和画点函数 矩形和画点函数 矩形和画点函数 第二课∶句柄、矩形和画点函数 一、句柄 今天开始,我向大家讲有关 API 的是实质性内容。我们就从"句柄"开始。 只要你来到了 API 的世界,经常碰到的问题之一就是句柄。那么究竟什么是句柄呢? 如果你从来都没有听说过"句柄"这个词,可能首先觉得句柄当中有很多内容。其实不然, 所谓句柄实际上是一个数据,是一个 Long (整长型)的数据。在 API 中,它经常是以一个参 数的形式传递给各种 API 函数。如: Public Declare Function GetWindow& Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) 其中,hwnd 就是句柄。在 VB 里,句柄是一种属性,您打开 VB 中的对象游览器看一看 Form 窗体或者 PictureBox 控件等究竟有没有 hwnd 属性。是有的。VB 中的解释是这样的∶ Microsoft Windows 运行环境,通过给应用程序中的每个窗体和控件分配一个句柄(或 hWnd)来标识它们。hWnd 属性用于 Windows API 调用。许多 Windows 运行环境函数需 要活动窗口的 hWnd 作为参数。 如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知 道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内的。如 果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。 但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows 是一个以虚拟内 存为基础的操作系统。在这种系统环境下,Windows 内存管理器经常在内存中来回移动对 象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总 是如此变化,我们该到哪里去找该对象呢? 为了解决这个问题,Windows 操作系统为各应用程序腾出一些内存储地址,用来专门登 记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows 内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样 我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在 对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。 句柄地址(稳定)→记载着对象在内存中的地址────→对象在内存中的地址(不稳定)→ 实际对象 但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来 的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成是一个应 用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们 的门票总是不同的一个座位是一样的道理。 在 VB 中获得一个对象的句柄十分简单,如要获取 Form1 窗体的句柄,就可以这样写∶ Form1.Hwnd
对象的句柄还可以通过 API 函数来获得,如∶ GetActiveWindow 返回位于最顶部的具有输入焦点的窗口句柄 GetFocus 获得当前线程里补获鼠标输入的窗口句柄 GetForegroundWindow 从位于前台的线程里返回活动窗口的句柄 GetCursor 取得当前指针的句柄 GetDesktopWindow 获取整个桌面的句柄 GetWindow 获得一个窗口的句柄,该窗口与某源窗口有特定的关系 《以上函数说明均可在 WinAPI.hlp 文件中找到。》 本教程提供了演示例程──play1.vbp,正是为了说明这些函数的具体用法的。 程序运行后,用鼠标做一些任何你想做的事情,并观察各项目数据的变化。 通过本程序,注意观察以下几点∶ 1,线程内与线程外。(VB 不支持多线程)。其他应用程序对此程序来说都是线程外。 2,在 windows95 操作系统下,各个窗体(包括一些控件,如文本框,图片框等, MICROSORT 对它们均统称为窗体)拥有各自的鼠标指针。这和 win16 下各应用程序使用同 样一个指针是截然不同的。 3,每次从新启动,各窗体的句柄都有所变化。Text5 的装载和卸载过程中,句柄始终是在 变化着的。这说明了上面提的影院售门票中存在的现象是真实的。 获得对象句柄的函数还有很多,以后碰到它们时再介绍给大家。 二、驾驶句柄 只要弄清了什么是句柄,尤其是窗口句柄,那么操纵一个对象就变得自如一多了。比如, 可以通过 GetWindowText API 函数,我们可以轻松地获得某特定窗口的标题。 GetWindowText 在 VB 中的声明如下: Private Declare Function GetWindowText& Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) 于是,我们可以通过以下一段代码来获得 Form1 的窗口标题。(新建一个项目,添加一 个命令按钮,把以下的代码拷贝过去,还有上面的函数声明。你就可以运行了) Private Sub Command1_Click() Dim dl As Long Dim FormCaption As String FormCaption = Space(128) dl& = GetWindowText(Form1.hwnd, FormCaption, 128) Print FormCaption End Sub 注∶其中 128 是指字符串变量 FormCaption 的长度(又称缓冲区大小) 这有必要吗?为了获得 Form1 窗体的标题,何必写这么多代码呢?难道这就是 API。是 的,的确在 VB 中用 Print Form1.Caption 一行代码就可以抵挡住以上代码了。但是,假如
分享到:
收藏