虫草鹿鞭丸 尼玛:在VB中使用API函数详解

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 06:12:12
API(Advanced Programmers Interface,高级程序员接口)(注:API实际是指Application Programming Interface,应用程序编程接口;此处疑为原文错误,不过在VB中也可以这么说吧!)是一套用来控制Windows的各个部件(从桌面的外观到位一个新进程分配的内存)的外观和行为的一套预先定义的Windows函数.用户的每个动作都会引发一个或几个函数的运行以Windows告诉发生了什么. 一、什么是API?   API(Application Programming Interface,应用程序编程接口)是一套用来控制Windows的各个部件(从桌面的外观到位一个新进程分配的内存)的外观和行为的一套预先定义的Windows函数.在某种程度上很象Windows的天然代码.其他的语言只是提供一种能自动而且更容易的访问API的方法. 二、Windows的一些特别之处   Windows通过句柄(Handle)识别每个窗体,控件,菜单,菜单项或其他任何你能想得到的东西.当你的程序运行时,它所包含的每个部件都有一个唯一确定的句柄用来同其他的部件相区别.例如,某个按钮得句柄就与其他部件不同,当你想要通过API来执行有关该按钮的某种操作时就必须使用这个句柄.从哪儿得到它呢?VB 为每个拥有Windows句柄的控件都提供了Hwnd属性来表示其句柄. 三、Any 四、回调(CallBacks) 五、子类处理 Windows的一些特别之处   这个主题是要告诉你Windows 的一些与在VB中的不同的细节   Windows通过句柄(Handle)识别每个窗体,控件,菜单,菜单项或其他任何你能想得到的东西.当你的程序运行时,它所包含的每个部件都有一个唯一确定的句柄用来同其他的部件相区别.例如,某个按钮得句柄就与其他部件不同,当你想要通过API来执行有关该按钮的某种操作时就必须使用这个句柄.从哪儿得到它呢?VB 为每个拥有Windows句柄的控件都提供了Hwnd属性来表示其句柄.   Windows 使用像素(Pixel)而不是缇(Twip).因此,把涉及API函数调用的控件的ScaleMode属性设为3--(Pixel)是个不错的主意,这样你可以通过ScaleXXX属性得到它们的公制单位值.尽管这样,你可能有时仍需要进行从Twip到Pixel的转换(反之亦然),你可以通过Screen对象的TwipsPerPixelX和TwipsPerPixelY来实现.举例如下: PixelXValue=TwipXValue\Screen.TwipsPerPixelX PixelYValue=TwipYValue\Screen.TwipsPerPixelY TwipXValue=PixelXValue*Screen.TwipsPerPixelX TwipYValue=PixelYValue*Screen.TwipsPerPixelY   我并没有在实际中见到过TwipsPerPixelX 和 TwipsPerPixelY 的值有什么不同,但你最好是把它们区别开来而不是混用,这至少是一种好的程序设计风格.另外需要注意的是,这里用的是" \ "(整除) 而不是 " / " ,这是因为像素值必须是整数.   另外需要提到的是,Windows函数中用到了不同的坐标系统,因此需要注意.  Windows的一些特别之处   最后要注意的是,一旦你使用了API 函数,VB就可能不再可靠了---API调用中一个简单的语法错误就会导致VB 崩溃!(请经常保存您的工程).VB 并不能识别API调用中的错误,因此一旦你的程序出现异常,要先检查API调用---是否缺少 ByVal,或者是错误的类型,参数等等. 从哪里得到有关函数的说明?   这个主题不会告诉你如何通过API函数改变按钮文字或如何快速查找一个文件.这不是一个API函数文档.   为了得到有关函数的说明,你需要SDK帮助或微软的SDK文档(至少有40M---我怎么可能放在这里?).这些SDK帮助通常都包含在Borland Dephli 3.0 开发包或者MS Visual C++中.到网上去或找您的朋友要一个,版本越新越好   注意Win3.x 的SDK 帮助对你并没有用,因为很多函数已经过于陈旧甚至废弃不用,尽管他们中的大多数由于与 Windows95兼容而依然存在.  API参数类型   如果你已经有了一个SDK帮助,你肯定主意到了函数的返回之或参数有很多奇怪的类型如VOID,LPCSTR, 和DWORD.如果你对C语言比较熟悉的话,那你肯定明白它们的意思.对于其他不熟悉C语言的人,这里有一张摘自 VB Books Online(标题是:C语言声明到Visual Basic的转换) Books Online(标题是:C语言声明到Visual Basic的转换) <> bordercolordark="#FFFFFF" cellpadding="0"> <>

bordercolordark="#FFFFFF" cellpadding="0">

 

 

 

C 语言数据类型

 

在VB中的声明

 

Call with

 

 

 

 

ATOM

 

ByVal variable As Integer

 

An expression that evaluates to an Integer

 

 

 

 

BOOL

 

ByVal variable As Long

 

An expression that evaluates to a Long

 

 

 

 

BYTE

 

ByVal variable As Byte   

 

          

 

An expression that evaluates to a Byte

 

 

 

 

CHAR

 

ByVal variable As Byte   

 

          

 

An expression that evaluates to a Byte

 

 

 

 

COLORREF

 

ByVal variable As Long

 

               An

 

expression that evaluates to a Long

 

 

 

 

DWORD

 

ByVal variable As Long   

 

          

 

An expression that evaluates to a Long

 

 

 

 

HWND, HDC,HMENU,etc.(Windows handles)

 

ByVal variable As Long   

 

          

 

An expression that evaluates to a Long

 

 

 

 

INT, UINT

 

ByVal variable As Long

 

An expression that evaluates to a Long

 

 

 

 

LONG

 

ByVal variable As Long

 

An expression that evaluates to a Long

 

 

 

 

LPARAM   

 

               

 

 

ByVal variable As Long   

 

           

 

An expression that evaluates to a Long

 

 

 

 

LPDWORD

 

variable As Long

 

An expression that evaluates to a Long

 

 

 

 

LPINT, LPUINT

 

variable As Long

 

An expression that evaluates to a Long

 

 

 

 

LPRECT

 

variable As type

 

Any variable of that user-defined type

 

 

 

 

LPSTR, LPCSTR

 

ByVal variable As String

 

An expression that evaluates to a String

 

 

 

 

LPVOID

 

variable As Any

 

Any variable (use ByVal when passing a string)

 

 

 

 

LPWORD

 

variable As Integer

 

An expression that evaluates to an Integer

 

 

 

 

LRESULT

 

ByVal variable As Long

 

An expression that evaluates to a Long

 

 

 

 

NULL

 

As Any orByVal variable As Long ByVal Nothing or ByVal0& or vbNullString

 

 

 

 

 

 

SHORT   

 

                

 

 

ByVal variable As Integer

 

An expression that evaluates to an Integer

 

 

 

 

VOID

 

Sub procedure

 

Not applicable

 

 

 

 

WORD

 

ByVal variable As Integer

 

An expression that evaluates to an Integer

 

 

 

 

WPARAM

 

ByVal variable As Long

 

An expression that evaluates to a Long

 

 

 

 

 

 API参数类型 注释:          你应该注意到了BOOL类型(Boolean)求出的值为Long而不是Boolean.因此0表示"假"(False),其他任何值表示"真"(True).   HWND,HDC,HMENU,etc.---etc.指其他所有与于前面所述相似的类型.它们都以"H"开头来表示不同对象的句柄.举例来说,HBITMAP表示一副位图的句柄,HBRUSH标是一个刷子的句柄等等.它们均为Long而且要按值传递(ByVal).         同样要注意LPVOID被声明为Any型变量,本文将会有独立的一个主题用来论述Any类型.        有些类型以"LP"开头.LP 是Long Pointer的缩写.因此LPWORD实际表示数据所存的内存地址.不过,你并没有必要调用某个函数来获取这个地址.当你按引用(ByRef)传递参数时,你实际上传递的就是它的地址.在这里你只需记住:如果某个参数的类型以"LP"开头,应该按引用(ByRef)传递.顺便说一句,LPARAM 与 Lparam 很相似但不是Lparam,Lparam不是一个指针,在这里你必须传递它的真实值,因此要按值(ByVal)传递.        NULL也是一种奇怪的类型.你可以从 VB 中了解到有关它的描述,这里我不去讨论它.按你需要的形式传递它吧,在大多数情况下,可以用 ByVal 0& 或 vbNullString.        最后,VOID是用来指那些没有返回值的函数的.API中没有SUBs,这就是它们的标志.只要记住:如果一个函数被声明为VOID ,你必须在 VB 中把它声明为 SUB  Any        有些消息的参数声明为Any.这表示该参数是一种可变的类型(你可以以整型,字符串,用户自定义或其他的类型来传递). 这有一个这样的例子: Public Declare Function SendMessage Lib "User32" Alias "SendMessageA"( ByVal Hwnd as Long, ByVal wMsg as Long, ByVal wParam as Long, lParam as Any) as Long lParam 声明为Any并按引用(ByRef)传递.       这里是在这个函数中如果lParam是不同类型的值时应遵循的规则: 如果该值是 传递形式 numeric ByVal(as Long,or as Any) Null ByVal(as Long,or as Any) String ByRef(as String,or as Any) Type ByRef(as Any) array of Type ByRef(as Any)        如果你的函数声明同上面的一个而且你想传递一个Long型数,你应该这样写:      Call SendMessage(Me.Hwnd,WM_XXXX,0&,ByVal LongValue)         注意尽管头三个参数也是数值,但它们前边并没有ByVal.这是因为在函数声明中它们已经被声明为按值传递(ByVal).第四个参数,由于是按引用传递(ByRef)(VB并不知道你要传递参数的类型),因此你必须加上ByVal 你可以使用别名技术来传递不同类型的参数: Public Declare Function SendMessageLng Lib "User32" Alias "SendMessageA"(ByVal Hwnd as Long, ByVal wMsg as Long, ByVal wParam as Long, ByVal lParam as Long) as Long 或: Public Declare Function SendMessageStr Lib "User32" Alias "SendMessageA"(ByVal Hwnd as Long, ByVal wMsg as Long, ByVal wParam as Long, lParam as String) as Long         注意API参数类型本身是不会改变的.例子中的第四个参数总是一个4字节的长型数.当你按值(ByVal)传递一个Long或 Null时,该4字节长的数值就直接传递给函数.如果你传递一个String或其他的什么,你是按引用(ByRef)传递,VB传递的实际上是变量的地址,也是4个字节.  参数传递   你已经知道如何传递参数了,只需把他放到调用的函数中即刻.然而,当你使用API函数时,在传递参数是有一些你应该注意的 细节. ByVal还是ByRef. 通常情况下,你不用为此担心,VB的API浏览器已经为你做好了一切,只需你输入数值它就会照所声明的方式传递.总的来说,ByVal是把实际的数值传递给函数,而ByRef是把地址传给函数.唯一的麻烦就是 Any 类型. 把字符串传递给API函数也并不难.API函数需要的是字符串首字符的地址,然后读取该地址直到遇到一个Null字符.听起来很糟糕,但只是VB处理字符串的实际方法.你唯一要记住的是一定要按引用传递(ByRef)字符串 当你想得到所需的函数返回值信息时,情况有稍微的一点不同. 这里是GetComputerName函数的声明:
Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA"(ByVal lpBuffer As String, nSize As Long) As Long
第一个参数是一个指向字符串的远程指针,第二个参数是字符串的长度.
  如果你只是简单的声明一个字符串类型的变量并把它传递给函数,就会出现错误.因此,你首先需要初始化字符串.这里是如何得到计算机名字的例子:
 Dim Buffer As String Buffer = Space(255) Ret& = GetComputerName(Buffer, Len(Buffer)) if Ret& > 0 then CompName$ = Left(Buffer, Ret&)   
         在这里,待传递的字符串被初始化为有255个空格的字符串.我们把它传递给函数同时还有它的长度.如果出错则返回值为0.  参数传递     CompName中将保存计算机名. 有些函数也需要传递数组,这里是一个例子: Declare Function SetSysColors Lib "user32" Alias "SetSysColors" (ByVal nChanges As Long, lpSysColor As Long, lpColorValues As Long) As Long        最后两个参数是Long型数组.为了传递数组,你只需传递它的第一个元素.下面是示例代码: Const COLOR_ACTIVECAPTION = 2 Const COLOR_INACTIVECAPTION = 3 Const COLOR_CAPTIONTEXT = 9 Const COLOR_INACTIVECAPTIONTEXT = 19 Dim SysColor(3) As Long Dim ColorValues(3) As Long Dim Ret& SysColor(0) = COLOR_ACTIVECAPTION    SysColor(1) = COLOR_INACTIVECAPTION SysColor(2) = COLOR_CAPTIONTEXT SysColor(3) = COLOR_INACTIVECAPTIONTEXT ColorValues(0) = RGB(58, 158, 58) ’深绿 ColorValues(1) = RGB(93, 193, 93) ’浅绿 ColorValues(2) = 0 ’黑色 ColorValues(3) = RGB(126, 126, 126) ’灰色 Ret& = SetSysColors(4&, SysColor(0), ColorValues(0)) 该程序将改变所有活动和非活动窗口的标题栏背景和文本的颜色.  回调(CallBacks)        所谓回调,就是你自己定义一个函数,并告诉Windows何时为何调用.你可以写一个有特定数量和类型参数的函数,然后告诉Windows何时调用,并传递给它所需的参数.Windows就会调用你定义的函数,处理参数,并给你返回值.   回调的一个典型应用是从Windows获得连续的数据流.这里是一个需要回调的函数的声明: Declare Function EnumWindows Lib "User32"ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long   第一个参数是你的回调函数的地址,第二个参数是你想传递的的任意数值.该值将被传递到你的函数,于是你就知道了它要调用什么.     VB 5.0已经提供了一个很有用的操作符 AddressOf ,可以得到一个函数的地址.当你调用一个函数时它只能用在参数的前面,下面这种用法是错误的并且会导致出错: FuncP = AddressOf MyFunction 因此你必须这样调用EnumWindows函数: Success& = EnumWindows(AddressOf cbFunc, 58&)   你必须也要自己写回调函数.问题是有很多不同类别的回调并且有各种各样的参数,有关这些参数的描述可以在SDK帮助或MS SDK文档中找到.这里是一个回调的声明: Function cbFunc (ByVal Hwnd, ByVal lParam) as Long 这里是一个回调的例子: Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA"(ByVal hwnd As Long,ByVal lpString As String,ByVal cch As Long) As Long Success& = EnumWindows(AddressOf cbFunc, 58&)  Function cbFunc (ByVal Hwnd, ByVal lParam) as Long If lParam = 58 then ’enum windows Str$ = Space(255) Ret& = GetWindowText(Str$, Len(Str$)) Debug.Print Left(Str$, Ret&) End If End Function 这个例子将列出窗口的标题,(不包含子窗体)  窗口程序
  Windows并不知道事件. 这些是VB特有的隐藏Windows获取你的窗口发生事件的真正方法的一种方式.VB很像是一个将Windows语言翻译成VB语言的解释器.
  但是事实并非如此,你很快就会遇到.设想你想知道用户何时加亮了菜单选项(不是点击,只是加亮即选择了)VB并不提供这种事件,但你可能见到其他的程序,但你浏览它的菜单时状态栏会出现相应的文字.如果他们能,你为何不能?
  OK,这里是大致的真实情况.每个窗口都有一个特殊的程序叫做窗口程序.它实际上是一个回调函数.该函数将在你的窗口发生事件的任何时间发送消息.这样当用户加亮一个菜单项时就会发送一条消息(WM_COMMAND).
   那为什么我看不到这条消息呢?这是因为是VB创建窗口程序而不是你.当Windows发送消息时,该程序将为之分派特定的事件,并将其参数转换为比较容易用的事件的参数.但是在有些情况下,它会忽略有些消息而不能收到真实的输入.如果你真的想得到这些消息,你必须对你的窗体进行子类处理,我们将在另外一个主题中谈到.
这里是一个回调窗口程序的声明:
 Function WindowProc(ByVal Hwnd As Long, ByVal wMsg As Long,ByVal wParam As Long, ByVal lParam As Long) As Long
  第一个参数指定窗口的句柄,第二个参数是消息的标识符(如WM_COMMAND或WM_MOUSEMOVE),wParam和lParam时两个32位的数值,它们的意义依赖于消息的类型.  子类处理   当你一最大限度利用了VB所给你的并且还想知道更多的东西,或只是想更多地了解你自己的窗口,你将会发现子类处理的优势.   子类处理是指用一个新的窗口函数来取代当前活动窗口函数.这个用户自定义函数能处理任何需要的消息,并能调用原来的窗口函数,它将在原来的窗口函数之前收到各种消息.但原来的那个窗口处理函数依然存在,并没有消失.如果你不想处理某条消息,你应该让原来的窗口函数去处理它.   子类处理是通过调用SetWindowLong函数实现的,该函数将改变指定窗口的特殊属性.下面是它的声明: Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"(ByVal hwnd As Long, ByVal nIndex As Long,ByVal dwNewLong As Long) As Long        第一个参数代表要进行子类处理的窗口,第二个参数应该是GWL_WNDPROC(-4),第三个参数是新的窗口函数的地址.参见回调和窗口函数一节. 此函数将在窗口取得焦点,发生事件,或其他情况下(如其他进程改变了系统的某些参数)被随时调用. 如果发生错误SetWindowLong函数将返回0,否则将返回原来的窗口函数的地址.这个地址特别重要,你应该把它保存在一个变量中或其他地方.当你不处理某些消息时(实际上,你可能只处理不到1%的消息,其他的都将由原窗口函数处理),调用原来的窗口函数就需要该地址.  子类处理   当你一最大限度利用了VB所给你的并且还想知道更多的东西,或只是想更多地了解你自己的窗口,你将会发现子类处理的优势.   子类处理是指用一个新的窗口函数来取代当前活动窗口函数.这个用户自定义函数能处理任何需要的消息,并能调用原来的窗口函数,它将在原来的窗口函数之前收到各种消息.但原来的那个窗口处理函数依然存在,并没有消失.如果你不想处理某条消息,你应该让原来的窗口函数去处理它.   子类处理是通过调用SetWindowLong函数实现的,该函数将改变指定窗口的特殊属性.下面是它的声明: Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"(ByVal hwnd As Long, ByVal nIndex As Long,ByVal dwNewLong As Long) As Long         第一个参数代表要进行子类处理的窗口,第二个参数应该是GWL_WNDPROC(-4),第三个参数是新的窗口函数的地址.参见回调和窗口函数一节. 此函数将在窗口取得焦点,发生事件,或其他情况下(如其他进程改变了系统的某些参数)被随时调用. 如果发生错误SetWindowLong函数将返回0,否则将返回原来的窗口函数的地址.这个地址特别重要,你应该把它保存在一个变量中或其他地方.当你不处理某些消息时(实际上,你可能只处理不到1%的消息,其他的都将由原窗口函数处理),调用原来的窗口函数就需要该地址.  子类处理    调用原窗口函数将由CallWindowProc来完成.这里是它的声明: Declare Function CallWindowProc Lib "user32" Alias"CallWindowProcA"(ByVal lpPrevWndFunc As Long,ByVal hWnd As Long,ByVal Msg As Long,ByVal wParam As Long, ByVal lParam As Long) As Long   第一个参数是原窗口函数的地址,其他的同你接收到的四个参数一样.你可以改变其中的值来控制对消息的处理.例如,当你收到了一条WM_MOUSEMOVE消息时,你从lParam中得到鼠标所在位置的坐标并将其改成了其他的坐标.那么原窗口函数就会认为鼠标位于其他的位置从而做出一些有趣的事如显示其他控件的Tooltip.   你指定的返回值也是有意义的,它依赖于发送的消息. 在结束你的程序时将控制权交回给原窗口函数是很重要的,通常在Form_Unload中完成如下: Ret& = SetWindowLong(Me.Hwnd, GWL_WNDPROC, oldWndProcAddress) 如果你在VB中启动程序时忘掉了这一行,结果将是VB崩溃并会丢失尚未保存的数据.千万要小心. 这里是子类处理的一个简单示例:  Dim oldWndProc As Long Private Sub Form_Load() oldWndProc = SetWindowLong(Me.Hwnd, GWL_WNDPROC, AddressOf MyWndProc) End Sub  Private Sub Form_Unload() Ret& = SetWindowLong(Me.Hwnd, GWL_WNDPROC, oldWndProc) End Sub  Function MyWndProc(ByVal Hwnd As Long,ByVal wMsg as Long,ByVal wParam As Long,ByVal lParam As Long) Debug.Print wMsg & " " & wParam & " " & lParam Ret& = CallWindowProc(oldWndProc, Hwnd, wMsg, wParam, lParam) End Function  处理参数   有时函数并不以你所需的方式返回信息.一个典型的例子是将两个代表鼠标位置的整形(2 byte)数合并为一个4 Byte的数.还有一个例子是判断一个数的某位是否为1.你还可能得到一个代表一个结构地址的Long型数.   合并和分离一个数并不需要过多的描述.你能在我们的网站(www.geocities.com/SiliconValley/Lab/1632/)上找到APIMacro.bas,它包含了你需要的多种函数. 可以用一下方法检查一个数的第N位是否为1: If Value and (2^N) then ... 置1 Value = Value Or 2^N 置0 Value = Value And Not 2^N   如果你想设定或取得预先知道的某位的信息,用1024代替2^10要快的多.因为这样VB无需自己进行计算(VB憎恨 "^" ?).   如果你接收到一个类型的指针,你要做的工作将稍多一点.你可以使用CopyMem函数来取得信息.下面是它的声明: Declare Sub CopyMem Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long) 如果你接收到了一个指向RECT 类型的指针并存在Long型变量Addr 中,可以这样处理: Dim Info As Rect Call CopyMem(Info, ByVal Addr, len(Info)) 注意ByVal关键字.现在,如果你想把信息写回,使用: Call CopyMem(ByVal Addr, Info, Len(Info))  结束语   我希望这份教程能帮助你理解如何控制API函数的威力和如何正确使用它们.但是要小心!就像火,如果你让它失去控制,你就会玩蛋.当然,不要忘了VB是进行简单.安全程序设计的语言,而API函数则正好相反.如果你想得到更多的控制功能,最好转移到VC++ 或者Delphi.   祝你在API探险中好运!