有些消息的參數(shù)聲明為Any.這表示該參數(shù)是一種可變的類型(你可以以整型,字符串,用戶自定義或其他的類型來傳遞). 這有一個這樣的例子:
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)傳遞.
這里是在這個函數(shù)中如果lParam是不同類型的值時應遵循的規(guī)則: 如果該值是 傳遞形式 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)
注意盡管頭三個參數(shù)也是數(shù)值,但它們前邊并沒有ByVal.這是因為在函數(shù)聲明中它們已經(jīng)被聲明為按值傳遞(ByVal).第四個參數(shù),由于是按引用傳遞(ByRef)(VB并不知道你要傳遞參數(shù)的類型),因此你必須加上ByVal 你可以使用別名技術來傳遞不同類型的參數(shù):
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參數(shù)類型本身是不會改變的.例子中的第四個參數(shù)總是一個4字節(jié)的長型數(shù).當你按值(ByVal)傳遞一個Long或 Null時,該4字節(jié)長的數(shù)值就直接傳遞給函數(shù).如果你傳遞一個String或其他的什么,你是按引用(ByRef)傳遞,VB傳遞的實際上是變量的地址,也是4個字節(jié).
CompName中將保存計算機名. 有些函數(shù)也需要傳遞數(shù)組,這里是一個例子:
Declare Function SetSysColors Lib "user32" Alias "SetSysColors" (ByVal nChanges As Long, lpSysColor As Long, lpColorValues As Long) As Long
最后兩個參數(shù)是Long型數(shù)組.為了傳遞數(shù)組,你只需傳遞它的第一個元素.
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)) 該程序?qū)⒏淖兯谢顒雍头腔顒哟翱诘臉祟}欄背景和文本的顏色.
回調(diào)(CallBacks)
所謂回調(diào),就是你自己定義一個函數(shù),并告訴Windows何時為何調(diào)用.你可以寫一個有特定數(shù)量和類型參數(shù)的函數(shù),然后告訴Windows何時調(diào)用,并傳遞給它所需的參數(shù).Windows就會調(diào)用你定義的函數(shù),處理參數(shù),并給你返回值.
回調(diào)的一個典型應用是從Windows獲得連續(xù)的數(shù)據(jù)流.這里是一個需要回調(diào)的函數(shù)的聲明:
Declare Function EnumWindows Lib "User32"ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long 第一個參數(shù)是你的回調(diào)函數(shù)的地址,第二個參數(shù)是你想傳遞的的任意數(shù)值.該值將被傳遞到你的函數(shù),于是你就知道了它要調(diào)用什么.
VB 5.0已經(jīng)提供了一個很有用的操作符 AddressOf ,可以得到一個函數(shù)的地址.當你調(diào)用一個函數(shù)時它只能用在參數(shù)的前面,下面這種用法是錯誤的并且會導致出錯: FuncP = AddressOf MyFunction 因此你必須這樣調(diào)用EnumWindows函數(shù): Success& = EnumWindows(AddressOf cbFunc, 58&) 你必須也要自己寫回調(diào)函數(shù).問題是有很多不同類別的回調(diào)并且有各種各樣的參數(shù),有關這些參數(shù)的描述可以在SDK幫助或MS SDK文檔中找到.這里是一個
回調(diào)的聲明:
Function cbFunc (ByVal Hwnd, ByVal lParam) as Long
這里是一個回調(diào)的例子:
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獲取你的窗口發(fā)生事件的真正方法的一種方式.VB很像是一個將Windows語言翻譯成VB語言的解釋器.
但是事實并非如此,你很快就會遇到.設想你想知道用戶何時加亮了菜單選項(不是點擊,只是加亮即選擇了)VB并不提供這種事件,但你可能見到其他的程序,但你瀏覽它的菜單時狀態(tài)欄會出現(xiàn)相應的文字.如果他們能,你為何不能?
OK,這里是大致的真實情況.每個窗口都有一個特殊的程序叫做窗口程序.它實際上是一個回調(diào)函數(shù).該函數(shù)將在你的窗口發(fā)生事件的任何時間發(fā)送消息.這樣當用戶加亮一個菜單項時就會發(fā)送一條消息(WM_COMMAND).
那為什么我看不到這條消息呢?這是因為是VB創(chuàng)建窗口程序而不是你.當Windows發(fā)送消息時,該程序?qū)橹峙商囟ǖ氖录?并將其參數(shù)轉(zhuǎn)換為比較容易用的事件的參數(shù).但是在有些情況下,它會忽略有些消息而不能收到真實的輸入.如果你真的想得到這些消息,你必須對你的窗體進行子類處理,我們將在另外一個主題中談到.
這里是一個回調(diào)窗口程序的聲明:
Function WindowProc(ByVal Hwnd As Long, ByVal wMsg As Long,ByVal wParam As Long, ByVal lParam As Long) As Long
第一個參數(shù)指定窗口的句柄,第二個參數(shù)是消息的標識符(如WM_COMMAND或WM_MOUSEMOVE),wParam和lParam時兩個32位的數(shù)值,它們的意義依賴于消息的類型.
子類處理
當你一最大限度利用了VB所給你的并且還想知道更多的東西,或只是想更多地了解你自己的窗口,你將會發(fā)現(xiàn)子類處理的優(yōu)勢.
子類處理是指用一個新的窗口函數(shù)來取代當前活動窗口函數(shù).這個用戶自定義函數(shù)能處理任何需要的消息,并能調(diào)用原來的窗口函數(shù),它將在原來的窗口函數(shù)之前收到各種消息.但原來的那個窗口處理函數(shù)依然存在,并沒有消失.如果你不想處理某條消息,你應該讓原來的窗口函數(shù)去處理它.
子類處理是通過調(diào)用SetWindowLong函數(shù)實現(xiàn)的,該函數(shù)將改變指定窗口的特殊屬性.下面是它的聲明:
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"(ByVal hwnd As Long, ByVal nIndex As Long,ByVal dwNewLong As Long) As Long
第一個參數(shù)代表要進行子類處理的窗口,第二個參數(shù)應該是GWL_WNDPROC(-4),第三個參數(shù)是新的窗口函數(shù)的地址.參見回調(diào)和窗口函數(shù)一節(jié). 此函數(shù)將在窗口取得焦點,發(fā)生事件,或其他情況下(如其他進程改變了系統(tǒng)的某些參數(shù))被隨時調(diào)用. 如果發(fā)生錯誤SetWindowLong函數(shù)將返回0,否則將返回原來的窗口函數(shù)的地址.這個地址特別重要,你應該把它保存在一個變量中或其他地方.當你不處理某些消息時(實際上,你可能只處理不到1%的消息,其他的都將由原窗口函數(shù)處理),調(diào)用原來的窗口函數(shù)就需要該地址.
子類處理
當你一最大限度利用了VB所給你的并且還想知道更多的東西,或只是想更多地了解你自己的窗口,你將會發(fā)現(xiàn)子類處理的優(yōu)勢.
子類處理是指用一個新的窗口函數(shù)來取代當前活動窗口函數(shù).這個用戶自定義函數(shù)能處理任何需要的消息,并能調(diào)用原來的窗口函數(shù),它將在原來的窗口函數(shù)之前收到各種消息.但原來的那個窗口處理函數(shù)依然存在,并沒有消失.如果你不想處理某條消息,你應該讓原來的窗口函數(shù)去處理它.
子類處理是通過調(diào)用SetWindowLong函數(shù)實現(xiàn)的,該函數(shù)將改變指定窗口的特殊屬性.
下面是它的聲明:
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"(ByVal hwnd As Long, ByVal nIndex As Long,ByVal dwNewLong As Long) As Long
第一個參數(shù)代表要進行子類處理的窗口,第二個參數(shù)應該是GWL_WNDPROC(-4),第三個參數(shù)是新的窗口函數(shù)的地址.參見回調(diào)和窗口函數(shù)一節(jié). 此函數(shù)將在窗口取得焦點,發(fā)生事件,或其他情況下(如其他進程改變了系統(tǒng)的某些參數(shù))被隨時調(diào)用. 如果發(fā)生錯誤SetWindowLong函數(shù)將返回0,否則將返回原來的窗口函數(shù)的地址.這個地址特別重要,你應該把它保存在一個變量中或其他地方.當你不處理某些消息時(實際上,你可能只處理不到1%的消息,其他的都將由原窗口函數(shù)處理),調(diào)用原來的窗口函數(shù)就需要該地址.
子類處理
調(diào)用原窗口函數(shù)將由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
第一個參數(shù)是原窗口函數(shù)的地址,其他的同你接收到的四個參數(shù)一樣.你可以改變其中的值來控制對消息的處理.例如,當你收到了一條WM_MOUSEMOVE消息時,你從lParam中得到鼠標所在位置的坐標并將其改成了其他的坐標.那么原窗口函數(shù)就會認為鼠標位于其他的位置從而做出一些有趣的事如顯示其他控件的Tooltip.
你指定的返回值也是有意義的,它依賴于發(fā)送的消息. 在結束你的程序時將控制權交回給原窗口函數(shù)是很重要的,通常在Form_Unload中完成如下: Ret& = SetWindowLong(Me.Hwnd, GWL_WNDPROC, oldWndProcAddress) 如果你在VB中啟動程序時忘掉了這一行,結果將是VB崩潰并會丟失尚未保存的數(shù)據(jù).千萬要小心.
這里是子類處理的一個簡單示例:
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
處理參數(shù)
有時函數(shù)并不以你所需的方式返回信息.一個典型的例子是將兩個代表鼠標位置的整形(2 byte)數(shù)合并為一個4 Byte的數(shù).還有一個例子是判斷一個數(shù)的某位是否為1.你還可能得到一個代表一個結構地址的Long型數(shù).
合并和分離一個數(shù)并不需要過多的描述.你能在我們的網(wǎng)站(www.geocities.com/SiliconValley/Lab/1632/)上找到APIMacro.bas,它包含了你需要的多種函數(shù). 可以用一下方法檢查一個數(shù)的第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函數(shù)來取得信息.下面是它的聲明: 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關鍵字.現(xiàn)在,如果你想把信息寫回,使用: Call CopyMem(ByVal Addr, Info, Len(Info))
結束語 我希望這份教程能幫助你理解如何控制API函數(shù)的威力和如何正確使用它們.但是要小心!就像火,如果你讓它失去控制,你就會玩蛋.當然,不要忘了VB是進行簡單.安全程序設計的語言,而API函數(shù)則正好相反.如果你想得到更多的控制功能,最好轉(zhuǎn)移到VC++ 或者Delphi. 祝你在API探險中好運!