经典Win32 API讲座.doc

上传人:丁** 文档编号:1546273 上传时间:2019-10-25 格式:DOC 页数:67 大小:219.50KB
返回 下载 相关 举报
经典Win32 API讲座.doc_第1页
第1页 / 共67页
经典Win32 API讲座.doc_第2页
第2页 / 共67页
经典Win32 API讲座.doc_第3页
第3页 / 共67页
点击查看更多>>
资源描述
Win32 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:Mydlluser32 函数声明中的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函数中使用的数据类型基本上和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传递的原因。 第四课:鼠标、插入符及系统函数 一、指针剪切什么是指针呢?我想大家都知道,没必要我多讲。只是概念上应当清楚,指针是指针,鼠标是鼠标,鼠标控制着指针。在win16中,指针只有一个,运行在系统中的应用程序共享这个指针。但在win32中,各个窗体都具有着自己的指针。这倒不是说屏幕上能同时出现好几个指针,而是说每个窗体都具有它自己的样式和一些特征的指针。指针移动到某窗体的时候,指针就自动变成那个窗体的指针样式。对指针需要认识的另一点是,指针的位置都是以像素屏幕坐标指定的。这一点很重要。接下来,我们进入本小节的主题。只要你细心一些就能够发现,鼠标指针一般是不能超出屏幕(显示器屏幕)范围的。但这倒也不是绝对的,一会儿你就会明白。指针一般控制在屏幕以内,这是事实。从这个事实中我们可以知道,指针是限定在某个区域之内活动着的。把指针的活动限定一个区域的过程叫做指针剪切。有关指针剪切的函数有两个。一个是GetClipCursor函数,它可以获得当前鼠标指针的剪切区域。此函数只有一个RECT结构的参数,函数调用结束后,这个RECT结构的数据,中便装载当前鼠标的矩形剪切区域大小。如果在一般情况下调用此函数,你大概获得的正好是屏幕大小。另一个函数是,SetClipCursor函数,作用是设置指定大小的指针剪切区域。欲设置的指针大小是装在一个RECT结构的数据中传递的。这两个函数一般搭配使用。在设置新的指针剪切区域前用GetClipCursor函数获取当前指针剪切区域,以便保存其值,而后调用SetClipCursor函数设置新的剪切区域。当这种新的剪切区域不再需要时就向SetClipCursor函数传递先前保存下来的区域值,就可以恢复到原来的指针剪切区域了。本教程为此提供了Program1.vbp演示程序。本人在设计这个程序时,忘了恢复原来的指针区域,结果指针无法脱离新设定的框架(自然也就无法按动敗翑按钮了),不得不用 CTRL+ALT+DELETE强行关闭VB设计器。望也成为您的一个经验。二、指针位置函数指针位置函数,简单得和指针剪切函数差不多。WINDOWS为此也提供了两个函数,一个是GetCursorPos,另一个是SetCursorPos。GetCursorPos函数只有一个参数,用来装载一个POINTAPI结构的数据,该数据说明的是当前指针的X和Y的坐标。但SetCursorPos 函数有所不同,它不依靠POINTAPI结构的数据,需要直接向它传递指针的X和Y两个参数。 本教程提供的program2.vbp正是对这两个函数的使用演示,演示一种指针的自动移动。在这两个函数中GetCursorPos函数的使用率比较高。作为一个比较精彩的例子,本人把自己的第4号演示程序简化为本教程的program6.vbp。此程序演示如何拖动无标题栏的窗体。程序还使用了我们在前几个课堂当中学习到的一些窗口函数,可作为一个好的复习材料,欢迎阅读。在program2.vbp中,对程序中采用的一点数学知识,怕有些朋友难以看出来(注意不是斈岩岳斫鈹),在此简单说明。首先程序通过一些方法计算出了鼠标指针的开始位置( OldPoint)和终点位置(NewPoint)。指针需要在连接这两个点的直线上移动。我们知道,如果设(X,Y)是指这个直线上的一个点,那么这个直线的方程可以是这样写的Y=aX+b 这里a和b是常数。 如果我们知道了a和b,那么只要X按一定的单位量增加,那么Y的值也就可以得知了。问题是如何确定这里的a和b呢? 解决方法是 设,鼠标起点坐标为X1,Y1 鼠标终点坐标为X2,Y2 则 Y1=aX1+b Y2=aX2+b 从可得b=Y1aX1 把代入可得,Y2=aX2+Y1aX1 从可得a=(Y2-Y1)(X2-X1) 获得a以后剩下的事情就好办了,可以用或获得b。 (这实际上是初中一年级的内容!不过一年级的学生可能不一定知道它代表的是直线。解析几何好象在高中二年级开课。你也可以用解析几何的方法来理解。)三、其他指针函数有关的指针函数还有以下几个函 数 说 明 CreateCursor 在两幅位图的基础上创建一个指针 DestroyCursor 清除一个指针,并释放它占用的系统资源 GetCursor 取得当前指针的句柄 GetDoubleClickTime 了解两次连续鼠标单击之间被看作双击事件的时间长度 LoadCursor 从一个文件载入指针资源 mouse_event 生成一个鼠标模拟事件 SetCursor 选择要使用的指针 SetDoubleClickTime 设置两次连续单击之间被看作一次双击的时间长度 SetSystemCursor 改变任何系统标准指针 ShowCursor 控制指针的可视性 SwapMouseButton 交换左右鼠标按钮的作用这里写出的很多函数并没有太大的用处。当然,您正好找这些函数,那这对你来说可能是一件非常斨卮髷的发现。你已经看到,对于有些函数功能,VB自身已经拥有的。在这几个函数中本人最喜欢mouse_even函数,有必要向大家介绍。 如果你还没有看Program2.vbp,那么最好先启动并运行它一下。程序的目的是用鼠标指针的移动来自动演示擷ing对你说“Xing对你说”这个按钮的用法。可喜,鼠标指针移动到这个按钮后,按钮并没有被按下。当然,这个程序根本没有设计成按那个按钮,而是直接调用Command2_Click过程。这当然是假的了,糊弄人的了。有了mouse_even函数以后,我们可以更换一下设计思路。也就是说,当鼠标指针移动到“Xing对你说”这个按钮以后,不要直接调用Command2_Click过程,而是模拟产生对按钮的点击操作。Program3.vbp正是把这个设计思路搬到了实际程序。程序中还使用了一个还没有学到的API函数Sleep。此函数用来使线程等待一定时间,时间以毫秒表示。如果想等待20毫秒,就可以写为: Sleep 20 。由于经常需要使用,先在这里简单讲一讲为好。 请打开并运行Program3.vbp,现在,示范过程是否像个那么一回事?四、关于插入符作为一个资源,插入符通常用于表示文本编辑器中的一个位置,用来输入文字。外观上一般是闪烁的线段或者光标块。但,事实上插入符可以是任何样式。其样式可以用位图来处理。但由于使用位图是以后的学习内容,本教程不予演示程序。等您学会了位图,这些都是很轻松的事情。您现在需要认识的是什么是插入符,它到底是如何创建,如何固定位置,如何显示等内容,以便在自己的程序中应用。函 数 说 明 CreateCaret 创建和选择一个插入符 DestroyCaret 清除插入符 GetCaretBlinkTime 获得插入符的闪烁频率 GetCaretPos 了解插入符在逻辑坐标中的位置 HideCaret 隐藏插入符 SetCaretBlinkTime 设置插入符的闪烁速率 SetCaretPos 设置插入符在逻辑坐标中的位置 ShowCaret 让插入符可见按道理来讲,插入符函数应当在控件的GotFocus事件中进行创建和初始化处理。也就是说,在控件得到焦点的时候。不幸的是,VB中只有在控件丢失焦点,并由当前应用程序中另一个控件继承焦点的事后,才会触发GotFocus事件。你可以启动Program4.vbp作试验。具体方法是,应用程序启动以后点一下VB设计器,使VB设计器成为活动窗体。这时,应用程序窗口将退到VB后面看不到。确认VB设计器中看到了闪烁的插入符光标后,从任务栏中点一下应用程序任务条,使应用程序窗体成为活动窗口。这时应用程序窗体将从VB 设计器后面跳到前台并显示。这时你可以观察到,上面的Picture1控件的光标消失了。用鼠标点击控件也无任何反应。除非你先点一下下方的Text控件,然后再点Picture控件,光标是不会出现的。当然完全可以用一个计时器来探测控件得到焦点的情况,但这种作法显然麻烦。对于这个问题我一般是在控件的Click事件中写一行代码来激活GotFocus事件内部的代码(参看程序,并将Private Sub Picture1_Click()事件中的代码Picture1_GotFocus无效)。书本上说,插入符应当在LostFocus事件中清楚掉。但本人认为没有必要,这反而会产生一些符作用。比如在本教程提供的Program4中,如果使LostFocus事件内的代码dl& = DestroyCaret有效,结果当我们点击Text控件的时候会看不到插入符的。所以我认为,在想用插入符的时候,您尽管创建就可以了。不想看到它,就采用HideCaret函数隐藏之。 附带的演示程序Program4.vbp总结了一些常用的插入符函数。五、键盘控制函数要使用键盘控制函数,首先必须认识什么是扫描码,什么是虚拟键码。所谓扫描码是一种计算机键盘的硬件决定的代码,可以说不同类型的键盘有不同标准的扫描码。如果我们直接用扫描码来设计程序,那大概是一件非常枯燥的事情,况且很难保证程序的兼容性。与扫描码不同,虚拟键码对每台计算机来讲都是一样的。这里,所谓键码不是别的,只是用哪个数据来表示哪个按键的问题。 从工作原理来讲,计算机键盘向计算机发送的是扫描码,然后来自键盘的扫描码将被转换为虚拟键码,究竟谁去做这种转换,我们就不用关心了。最后,WINDOWS将虚拟键码再转换为ASCII码或字符。正如所看到过的那样,VISUAL BASIC的KeyPressed等事件中递送过来的键码就是ASCII码了。另外,Unicode字符集和ANSI字符集的概念也需要掌握一些。时间关系,以下直接把一本叫VB核心技术的书中一段精彩的论述摘录给大家,作者是Bruce Mckinney。现在就要学习的朋友请点击这里。 现在,把有关键盘处理函数列到如下表,大家先大概看一下究竟都有哪些功能的函数键盘处理API函数函 数 说 明 ActivatekeyBoardLayout 在由系统定义的键盘布局中选择一种 EnumSystemCodePages 获得系统支持或安装的所有代码页的一个列表 GetACP 获得目前使用的ANSI代码页的表示符 GetAsyncKeyState 获取函数调用时指定虚拟键的状态 GetCPinfo 获取与一个代码页有关的信息 GetKeyboardLayout 判断一个指定线程使用的键盘布局 GetkeyboardLayoutList 取得目前可用键盘布局的一个列表 GetKeyboardLayoutName 取得键盘布局的名称 GetKeyboardState 取得256个虚拟键目前的状态 GetKeyboardType 判断目前使用的键盘类型 GetKeyNameText 判断一个特定虚拟键的名称 GetKeyState 取得指定虚拟键的状态 GetOEMCP 取得目前正在使用的OEM代码页的标识符 keybd_event 为系统生成一个新的键盘布局 LoadKeyboardLayout 载入一个新的键盘布局 MapVirtualKey 在字符与虚拟键、扫描码以及ASCII码之间进行转换 MapVirtualKeyEx 同上 OemKeyScan 将ASCII代码转换成OEM扫描码 SetKeyboardState 将256个虚拟键的状态设为WINDOWS所能理解的状态 ToAsciiToAsciiEx 将一个扫描码和Shift状态转换成对应的ASCII码字符 ToUnicode 将一个扫描码和Shift状态转换成对应的Unicode字符 UnloadKeyboardLayout 卸载以前有LoadKeyboardLayout函数载入的键盘布局 VkkeyScan,VkKeyScanEx 将ASCII代码转换成虚拟键码和Shift键状态。已经看到有很多键盘控制函数。但这些函数的参数基本上都局限在虚拟码和扫描码,也就是说你能够为这些函数提供虚拟码和扫描码,这些函数基本上都能够掌握和使用的。虚拟键码供有256个,很难在这里给出,但我觉得也没必要给出,简单说明一下就可以了。虚拟键码是以“VK_”开头的,比如Ctrl键为VK_CONTROL,Alt键为VK_MENU。只要你打开API文本游览器,选择常数(Consts)后键入“VK_”那么从列表框中可以看到一大堆以“VK_”开头的虚拟键码。另一种技巧是VB本身也有自己的简码表,VK_CONTROL在VB中大概是vbKeyControl了,你把前面的“vbKey”字样去掉,然后换上“VK_”,估计也能查到相关的虚拟键名( 好象其值也一样,比如vbKeyContol的值为17(十进制),而VK_CONTRIL的值为&H11(16进制)。我不敢保证,也没有太多的时间一一对照,有兴趣的朋友可以直接用vbKeyXXXXX来试一试。)。至于扫描码,就依靠MapVirtualKey函数从一个虚拟键码转换一下就可以了。为了帮助大家理解好这些函数的实际应用,本教程附带了两个例程,一个是Program4.vbp,另一个是Program8.vbp。 前一个程序是从我的一个演示程序中转化过来的,主要演示按键检测与设定快捷键。这个程序可能有另外的一个用处。举例来说,我一开始不明白键盘上的“Print Screen SysRq”键的虚拟键名是什么。我当然有虚拟键码表,但正如一般人只知道“Ctrl”键名而不知道“CONTROL”这个键名一样(两者实际上是一样),我在列表上无法找到“Print Screen SysRq”这个键的虚拟键名了。后来我启动我这个程序,按了一下“Print Screen SysRq”一看,程序表明它的虚拟键值等于是40(十进制),接着拿Windows提供的计算器一换算,其16进制数据为&H2C。哈,这下就好办了,一看列表,合&H2C对应的虚拟键名为“VK_SNAPSHOT”怎么样,很有意思吧?第二个程序是我曾经回答一位网由时做的小程序。不幸的是这位网友提的问题在我上一次的硬盘故障中丢失了。大概内容是这样的,很像考试卷里的提问“窗体里只有一个文本框,在文本框中输入一些文字后点击一下已经启动的Word,Windows的笔记本等文本编辑器程序窗口,这时,文本框里的内容直接粘贴到这些编辑页中,而不按任何其他的键(如Ctrl+V)。”我的这个程序是以Word为例编写的,要成功地运行它事先您必须启动Word编辑器,并打开一个新的编辑页。六、系统函数对与Windows的系统函数,我觉得没必要进行特别的说明,因为这些函数根本就没有特别之处。只是,这些函数主要是用来获取和设置系统有关信息的。比如设置桌面壁纸,默认的窗体呀,命令按钮呀之类的颜色呀什么的,还是您自己看更好一些。如下列表函 数 说 明 Beep 使PC喇叭发出声音 ExitWindowsEx 以各种方式退出和重新启动WINDOWS ExpandEnvironmentStrings 构件环境字串 FreeEnvironmentStrings 释放由GetEnvironmentStrings分配的一个环境字串块 GetCommandLine 取得用于启动应用程序的命令行 GetCOmputerName 取得当前电脑的名字 GetEnvironmentString 取得一个环境块,它提供的对环境变量的访问途径 GetEnvironmentVariable 获得环境块中一个单独的环境变量 GerLastError 取得上一个API函数调用的扩展错误信息。Visual Basic中请用Err对象的LastDLLError属性获得这个值 GetSysColor 取得Windows当前的各种颜色设置 GetSystemInfo 取得与硬件平台有关的信息 GetSystemMetrics 判断一系列系统样式信息;比如菜单栏或窗体标题的高度,垂直滚动条的高度,窗口最小尺寸等等。 GetSystemPowerStatus 取得与电源以及系统状态的有关信息 GetUserName 取得当前的用户名字 GetVersion,GetVersionEx 判断正在运行的Windows和DOS的版本 MessageBeep 生成一个标准系统声音 SetComputerName 设置电脑名 SetEnvironmentVariable 设置一个环境块中的环境变量 SetSysColors 设置Windows对象的当前颜色 SystemParametersInfo 一个功能强大的函数,用来获取与设置众多的系统参数比如保护程序,桌面墙纸,键盘延迟和重复率等等。当然,从这些函数中忽乱选择一些函数,也做了一个演示程序(Program7.vbp)。分析代码时请掌握好SYSTEM_INFO和OSSYSTEMINFO结构的用法。第三课窗口函数 一、 关于窗口函数在上一堂课里,我们已经提出了句柄的概念,并为此进行了较深度的讨论。现在来想,我要补充的是,句柄并非是仅仅是窗口才有的。似乎所有的WINDOWS对象都具有句柄。如,GDI对象中的画笔、刷子等,不久即将要学习的设备场景等也有自己的句柄,等等。但,和一些控件不同,这些对象并不属于窗口。 什么是窗口呢?有一句非常有趣的话如果它位于屏幕,那么它肯定是在一个窗口里;如果它不在于屏幕,它仍然可能在一个窗口里。窗体也是窗口;滚动条、列表框,文本框,甚至是桌面上的快捷图表也是窗口。更有趣的是,就连作为背景的桌面也是窗口。很多控件基本上都提供了hWnd属性,但没有提供的也有。对于这些控件可以用SetFocus 方法,将输入焦点设向控件,然后用API函数GetFocus取得当前具有焦点的那个窗口的句柄。当然,这一过程应当写在GoFocus事件中。在我碰到过的问题中有一个有趣的事情是, VB提供的IE控件的hWnd属性不管用。这个问题我一般都采用上述方法来解决的。 很多窗口函数都能对系统的任何窗体进行操作。这意味了VB程序可以直接操纵正在运行中的其他窗体。大家知道,如果对VB设计出的程序未做特殊的处理,那么我们可以启动多个该应用程序实例。我们可以利用API窗口函数来判断一个窗体的先例是否在运行当中,从而可以做到如果有先例则停止启动。很多应用程序就是这个样子的。比如四通利方中文平台,在已经启动的情况下再此启动,程序会告诉用户四通利方已经在运行,并停止启动。窗口函数主要可分为四个类型(也许说为这是为了这次讲课分类出来的更适合一些) 1、窗口分级函数; 2、窗口位置与大小函数; 3、窗口信息函数; 4、其他窗口函数。 以下我们就一一讲述。但由于窗口函数比较多,在这里就选择性的进行讨论。关于窗口函数有多少,具体的用法如何,您可以注意小雁侠的VB API站点的技术文档,或者本站程序下载栏目中的WinAPI帮助文。由于帮助文其内容来自小雁侠的网站,因此其内容更新比较起来会较晚一些。二、窗口分级函数系统中运行的窗口是有级别的高低之分的。谁不知道这样?这当然是废话。很多文章都是采用类似的这种废话来做导语,在这里我只不过也是学学而罢。 一、父子关系。 每个窗口都可能有自己的父窗口和子窗口。但,系统中运行的窗口是有限的,说明总得有个窗口是没有其父,我们把它叫做顶级窗口。一般把一个应用程序的主窗口就是顶级窗口,VB独立窗体及MDI窗体都是顶级窗口。窗口间的父子关系一般遵循以下规则 1、父窗口显示时,所有包容在其中的可见的子窗口会随着父窗口的显示而显示出来。2、父窗口隐藏时,所有包容在其中的子窗口会随着父窗口的隐藏而隐藏。 3、父窗口被卸载时,哈,您已经知道我想说什么了,当然是跟着自动卸载。 4、父窗口移动时,跟着移动。 二、兄弟关系及Z序列 当然,一位父亲有好几个儿女,都是常见的事情。同样,一个父窗口可以拥有多个子窗体。比如,位于一个窗体中的各种控件之间以及MDI窗口的各子窗口之间的关系。父窗口与子窗口的显示、隐藏、卸载及移动,其先后顺序是显而易见的。那么各兄弟窗口之间的情况会是如何呢?显然,两个互相重叠的两个子窗体不能都同时显示出它的全貌,自然有个显示的顺序规则。这个顺序规则叫做Z序列。有个解释为,如果把屏幕坐标看层X和Y轴组成的平面(事实上正是如此),那么作为三维坐标系统Z轴可看做是垂直于屏幕的坐标轴。这样,可以认为屏幕上的所有窗口是垂直于这个Z轴的。在Z轴上,谁在前,谁在后,就产生了一个Z序列。很生动!可用WINDOWS API函数和Visual Basic Z序列方法对Z序列进行控制。有了以上简单的知识以后,我们就不难应用API窗口分级函数,主要有以下及个 窗口分级函数函数名 说 明 FindWindow 按类名或窗口名(Caption)查找一个窗口 FindWindowEx 类似于FindWindow提供了更多的功能 GetLastActivePopup 针对指定的窗口,取回上一个活动的弹出式窗口的句柄 GetParent 获得指定窗口父窗口的句柄 GetTopWindow 获得指定窗口的第一个子窗口的句柄 GetWindow 如给定一个窗口句柄,该函数能取回具有特定关系的另一个窗口 的句柄。如,第一个子窗口、父窗口或窗口列表内的上一个或下一个窗口。 SetParent 改变任何窗口的父窗口。 从我个人的经验来看,我最常用的是GetWindow和SetParent函数。三、窗口位置与大小函数Windows API函数基本上都是(尤其是USER32.DLL动态连接库内的函数)以屏幕像素为度量单位的。这一点很重要,必须牢记。为此,在使用API函数的时候,我们经常把窗体或图片框控件的ScaleMode属性设置为3,即vbPixels(像素)。 理解窗口位置及大小函数的关键在于分清屏幕坐标、窗口坐标及客户坐标这三个概念。以下图展示了这三个坐标系统之间的关系。屏目、窗口与客户区坐标系统只要对这些坐标有了明确的概念,对使用窗口位置及大小函数就不难了。关于窗口的位置,有些函数返回的是上一堂课学习到的RECT结构。有关窗口位置及大小函数如下表所列 窗口位置及大小函数函数名 说 明 BringWindowToTop 使指定的窗口进入可见窗口列表的顶部,如它被部分或全部隐藏,则令其全部可见。同时,该窗口成为当前活动窗口。只有从前台线程调用时,才生效。 ChildWindowFromPoint 在规定的坐标取得某子窗口的句柄(如果有的话),这儿的坐标是指相对于父窗口的客户区坐标。 ChildWindowFromPointEx 与 ChildWindowFromPoint相同,功能更强。 ClientToScreen 判断指定点在窗口客户区内的屏幕坐标。 GetClientRect 获得对窗口客户区进行表述的一个矩形(RECT)。这是以像素为单位判断客户区大小的一个简便的方法。 GetWindowPlacement 获得指定窗口的一个WINDOWPLACEMENT结构。该结构说明了窗口的状态。 GetWindowRect 用于获得一个矩形(RECT)结构,它描述了窗体在屏幕坐标系统中的位置。 MapWindowPoints 对某窗口客户区坐标内的一个或多个点进行转换,用另一窗口的客户区坐标表示。 MoveWindow 移动指定窗口的位置,并能改变它的大小。 OpenIcon 将一个最小化窗口恢复为原始状态。 ScreenToClient 针对屏幕内一个指定的点,用某个特定窗口内的客户区坐标表示它。 SetWindowsPos 更改窗口的位置和大小,并能修改它在内部窗口列表内的位置(这个列表起着控制窗口先是顺序)。 SetWindowPlacement 在一个WINDOWPLACEMENT结构的基础上,设置某窗口的特征。该结构描述了窗口的状态,以及它在最小化、最大化或正常显示时的位置。 WindowFromPoint 根据屏幕上一个指定的点,判断哪个窗口正位于它的下面。 以上函数的具体用法均可在WinAPI帮助文中找到。在这些函数当中,SetWindowsPos 函数的使用率比较高,现在很多人都是用这个函数来实现窗口总在前面的效果,即通过 HWND_TOPMOST常数把窗口置于列表顶部。如果想把From1置于列表顶部,方法如下 SetWindowPos Form1.hWnd, HWND_TOPMOST, Form1.Left / Screen.TwipsPerPixelX, Fo rm1.Top Screen.TwipsPerPixelY, Form1.Width Screen.TwipsPerPixelX, Form1.Height Screen.TwipsPerPixelY, 0您可以把这行代码放在Paint事件中。 另外,GetWindowRect函数、MoveWindow函数以及下一课堂即将要学到的 GetCursorPos函数的相互配合能够实现一个拖动无标题栏的窗口。这是您必须掌握的技巧之一。感兴趣的朋友,可以到前线的源码解析栏目,下载第4号演示程序。以下是其主要的代码部分 Private MyRect As RECT Private MyPoint As POINTAPI Private Movex As Long, Movey As LongPrivate Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)Dim dl& dl& = GetWindowRect(Form1.hwnd, MyRect) dl& = GetCursorPos(MyPoint) If Button = 1 Then Movex = MyPoint.X - MyRect.Left Movey = MyPoint.Y - MyRect.Top End If End SubPrivate Sub Image1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Dim dl& dl& = GetCursorPos(MyPoint) If Button = 1 Then dl& = MoveWindow(Form1.hwnd, MyPoint.X - Movex, MyPoint.Y - Movey, _MyRect.Right - MyRect.Left, MyRect.Bottom - MyRect.Top, -1) End If End Sub 在MouseDown事件中,程序首先用 GetWindowRect 函数确定窗口在屏幕中的位置。再次是通过GetCursorPos函数确鼠标在屏幕中的位置。从而可通过计算获得鼠标位置与窗口左上角之间的横向与纵向距离(Movex与Movey)。 在紧接着发生的MouseMove事件中程序不断地用GetCursorPos函数获得鼠标当前的位置,并按前面已经求得的Movex与Movey判断窗口所应处的位置,而这在MoveWindow函数调用中直接完成。MoveWindow函数将窗体移动到新的位置。四、窗口信息函数所谓窗口信息函数就是用来获取有关窗口当前状态信息的函数。这类函数主要有函 数 说 明 GetClassInfo 取得指定窗口的类信息结构 GetClassInfoEx 效果类似于GetClassInfo,但增加了一些功能 GetClassLong,GetClassWord 用于获取窗口类信息 SetClassLong,SetClassWord 用于设置窗口类信息 GetClassName 获取窗口类名 GetDesktopWindow 获取整个桌面(屏幕)的窗口句柄 GetWindowLong,GetWindowWord 获取与窗口有关的信息 SetWindowLong,SetWindowWord 设置与窗口有关的信息 GetWindowText 获得窗口文本。它的效果大致等价于窗体或控件的Text属性 GetWindowTextLength 获得窗口文本的长度,用字符数表示。 IsChild 判断某窗口是否为另一窗口的子窗口或从属窗口。 IsIconic 判断某窗口是否处于最小化状态 IsWindow 判断指定的句柄是否为窗口句柄。 IsWindowEnabled 判断指定的窗口是否处于活动状态。 IsWindowVisible 判断某窗口是否可见。 IsZoomed 判断窗口是否处于最大化状态。 SetWindowText 设置窗口文本。大致等价于窗体或控件的Text属性。 大部分窗口信息函数是非常好理解的,按照有关手册中进行的函数说明,按指定数据类型进行调用即可。有必要说明的是,关于类和窗口的样式位。Windows是用一个长整形的数据的位设置方式来记录类和窗口的样式的。其中,窗口样式由一个32位样式以及另一个32 位扩展样式来构成。类样式操作由上述列表中的GetClassLong以及GetClassLong来进行,窗口样式操作由GetWindowLong 以及SetWindowLong来进行。 由于样式位的内容较多,我无法在此给出,您可以参考有关手册。这里有必要提醒大家的是,您想改变或获取当前窗口或类的样式,绝大多数情况可以考虑样式位操作。下面,就这个问题举一个简单了例子来说明。下面是用BS_LEFTTEXT样式位将VB复选框或选项按钮的文本在左边和右边之间相互移动来、移动去的程序(是附带的Program1.vbp的部分内容)。程序的原理很简单。首先用 GetWindowLong函数获得当前样式位的信息,然后通过位操作来准备新的样式位信息,最后用SetWindowLong实际地去更改。如下 Dim f&, dl& f& = GetWindowLong(Option1.hWnd, GWL_STYLE) 获得当前样式位的信息 If Index = 0 Then f& = f& Or BS_LEFTTEXTElse f& = f& And Not BS_LEFTTEXT End If dl& = SetWindowLong(Option1.hWnd, GWL_STYLE, f&) 设置新的样式位 Option1.Refresh (对Or和And位操作不熟悉的朋友,请参考有关技术资料) 在这里,对样式位不进行更详细讨论,主要有这样一个原因。用SetWindowLong函数改变一个样式位之后,不会导致窗口发生相应的变化(至少不会立即变化)。有些样式位可能在运行时候才会成功变化,而大多数都只在窗口创建时才生效。因为,用API方式创建一个窗体已经超出了本教程的范围,就算我在这里对样式位谈得再多,您可能也没有多大用处。同时,微软公司没有告诉我们哪些样式位在运行期间安全地改变,因此对具体的情况,只好靠自己进行具体试验。而从我个人的实际编程经验来看,没有特别的要求,我们不大会涉及到这些样式位操作,很多都可在VB中很方便地实现。本教程还附带了一个Program2.vbp的演示程序。是我本人随便编写的,没什么特别希罕之处。想看就看看好了。 最后,想简单提一提的是,使用SetWindowLong函数的时候,改变GWL_WNDPROC 数据是非常危险的(系统或VB经常挂死),即更改窗口函数的位置。一般,这种更改在需要进行子类处理的地方应用到。每次试运行程序,都应当习惯性地进行存盘。五、其他窗口函数 API中还有以下本教程未列出的窗口函数,以供大家参考。函数名 说 明 AnyPopup 判断是否存在可见的弹出式窗口 CascadeWindows 令窗口在一个父窗口内层叠显示 CloseWindow 对指定的窗口进行最小化处理(如果它是个
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 管理文书 > 各类标准


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!