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%的消息,其他的都将由原窗口函数处理),调用原来的窗口函数就需要该地址.
调用原窗口函数将由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))
这就是我在网上搜到了。
大家看一下了。
希望有用!