top
Loading...
在VB中“遙控”鼠標
p>盡管Windows的API函數在當今“Visual”成風的時代正漸漸被人們忘卻,但是正如當年開發高技巧的DOS軟件離不開匯編語言和DOS系統功能調用一樣,無論開發平臺高級到什么地步,只要Windows還在最底層運作,就要用到API函數。無論VB的開發手段多么高明,功能多么強大,但是在現實應用中,總有一些功能的實現是VB無能為力的。微軟也早意識到這一點,在VB的諸多版本中始終為API留有一席之地。根據筆者多年的VB開發經驗,每當我們為了實現一個功能在VB浩瀚的對象、屬性、方法、函數中苦苦搜索最終無功而返時,如果換一個角度,到API函數庫中去探尋,往往能得到一個驚喜。

Windows API

API函數是應用程序接口函數的縮寫。Windows在API函數庫中,為用戶開發基于Windows的應用程序提供了所需的各種基本功能。API函數庫的幾百個函數分布在Windows系統的三個動態鏈接庫中。這三個庫在Windows3.x中名為“KERNEL”、“USER”和“GDI”,在Windows 95中名為“KERNEL32”、“USER32”和“GDI32”。這些API函數的功能和接口在Windows的編程手冊中有系統詳盡的說明,也可以從Visual C++ for Windows或Borland C++ for Windows的聯機幫助手冊中查到。

在VB中引用API函數,就像混合語言編程一樣,要聲明所引用的函數調用接口以及函數所在的動態鏈接庫。聲明一個API函數的語法為:

Declare Function 函數名
Lib “庫名 ([參數表])
As 返回類型

如果一個API函數返回值為空(void),可以把它聲明為過程:

Declare Sub 過程名
Lib “庫名 ([參數表])

此外,相當一部分API函數的調用參數中用到了Windows系統定義的結構數據類型,這些類型在VB中也要用Type語句定義。

好在VB在提供訪問API函數功能的同時,也給用戶提供了一個友好的工具,可以省去我們聲明API函數、定義結構數據類型的絕大部分工作。這個工具在VB5.0中叫“API Text Viewer”。在該工具中,載入“Win32api.txt”后,就可以在“Declares”類中搜索API函數聲明,在“Types”類中搜索結構數據類型的定義,或在“Costants”類中搜索API函數中用到的常量定義。把搜索到的聲明、定義直接通過剪貼板復制 B程序中比較省力省心。

把需要用到的函數聲明和相關的數據結構定義以及常數定義加到窗體(Form)的General部分,就可以在程序中像調用普通VB函數一樣方便地調用API函數。

應用實例

以下從實用的角度出發,援引筆者在實際工作開發中的API應用一例,謹供參考。

漫游過Windows 3.2的五分鐘教程的用戶,大概還記得演示鼠標用法。鼠標指針自動隨教程的指令移動,自動點開菜單、按動按鈕,完全脫離了對物理鼠標器的依賴。這種高級控制技巧在VB的函數和方法中是找不到的,仍然要借助于API函數。

本例用API函數實現對鼠標的“遙控”:無論鼠標指針在何處,一個菜單命令Alt-C就能使鼠標指針自動平滑移動到窗口中一個按鈕的中心,然后自動按動按鈕,激活按鈕的Click事件處理過程。掌握了本例的方法后,前面提到的五分鐘教程軟件就不難制作了。

本例中,以下幾個功能的實現至關重要:獲取鼠標光標在屏幕上的位置、獲取按鈕在屏幕上(注意:不是在窗口中)的位置、移動鼠標光標、自動按下和放開按鈕,下面分別介紹。

獲取鼠標光標在屏幕上的位置和移動鼠標光標,分別需調用API函數GetCursorPos和SetCursorPos。這兩個函數的接口聲明如下:

Declare Function GetCursorPos Lib “user32 (lpPoint As POINTAPI) As Long Declare Function SetCursorPos Lib “user32 (ByVal X As Long, ByVal Y As Long) As Long

在SetCursorPos函數中,參數X和Y指定了鼠標光標在屏幕上的坐標。GetCursorPos函數把鼠標光標的當前位置存到結構變量lpPoint中。結構變量的定義如下:

Private Type POINTAPI
X As Long
Y As Long
End Type

按鈕的Left和Top屬性給出的是按鈕的左上角在窗口客戶區坐標系中的坐標位置。要把鼠標光標移到按鈕正中,需要得到按鈕中心在屏幕坐標系中的坐標位置。窗口客戶區坐標系與屏幕坐標系不僅坐標原點不同,二者的坐標單位(scale)也不同。窗口客戶區坐標系的單位是Twip,屏幕坐標系單位是Pixel,二者的關系可以從屏幕對象Screen的TwipsPerPixelX和TwipsPerPixelY屬性獲取。這兩個屬性分別代表了水平和垂直兩個方向上的單位轉換比例。

把按鈕中心在窗口客戶區中的坐標單位轉換為屏幕坐標系的單位后,還要通過一個API函數進行坐標平移變換,最終取得按鈕中心在屏幕坐標系中的坐標位置。這個API函數的接口聲明如下:

Declare Function ClientToScreen Lib “user32 (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

最后一步是自動按下和放開按鈕,這是通過模擬鼠標左鍵的按下和放開來實現的。該功能需調用API函數SendMessage向按鈕發一對鼠標左鍵按下和放開的消息,函數接口聲明如下:

Declare Function SendMessage Lib “user32 Alias “SendMessageA (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

SendMessage函數的hwnd參數為接收消息的窗口或控件的hwnd句柄。wMsg參數指定具體的消息值。在本例中,要發送鼠標左鍵按下和釋放的消息,wMsg的取值分別為WM_LBUTTONDOWN和WM_LBUTTONUP,這兩個常量定義如下:

Const WM_LBUTTONDOWN = &H201
Const WM_LBUTTONUP = &H202

把這兩個消息發送給一個按鈕,按鈕就會像真的被鼠標左鍵單擊了一樣。SendMessage函數中的后兩個參數在本例中可以不理,簡單置0即可。

還有一點細節需注意。如果連續給按鈕發送一對WM_LBUTTONDOWN和WM_LBUTTONUP消息,Windows會來不及進行一些必要的系統操作,這樣在視覺上就看不出按鈕被按下后又放開的效果,好像按鈕沒有按動。但按鈕的Click事件處理過程被激活執行表明按鈕確實被按過。為了達到視覺上的完美效果,我們不妨在WM_LBUTTONDOWN和WM_LBUTTONUP兩個消息之間插入一段短短的延時,比如說200毫秒,在這段延時期間,把處理權交給Windows,這樣Windows就有時間顯示按鈕被按下的效果了。插入延時的辦法有很多,可以加入一段空循環或利用計時器控件,這里再介紹一個API函數GetTickCount,該函數獲取自Windows啟動至被調用時所經過的毫秒數。利用這個函數控制延時,不僅精確,而且節省資源。GetTickCount函數的接口聲明如下:

Declare Function GetTickCount Lib “kernel32 () As Long

下面進行窗體設計。我們在窗體Form1中安插一個按鈕Command1。Command1的Click事件處理過程調用VB的Beep產生一聲蜂鳴。另外在Form1的主菜單上加上一個ClickButton的菜單命令,熱鍵設為Alt-C,該菜單命令的事件處理過程完成對鼠標光標的遙控。

最后給出Form1的完整程序清單:

Begin VB.Form Form1
BorderStyle = 1 Fixed Single
Caption = “Auto-click demonstration
ClientHeight = 3195
ClientLeft = 150
ClientTop = 720
ClientWidth = 4680
LinkTopic = “Form1
MaxButton = 0 False
ScaleHeight = 3195
ScaleWidth = 4680
StartUpPosition = 3 Windows Default
Begin VB.CommandButton Command1
Caption = “Click me!
Height = 495
Left = 1740
TabIndex = 0
Top = 1380
Width = 1215
End
Begin VB.Menu mnuClickButton
Caption = “&ClickButton
End
End
Attribute VB_Name = “Form1
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Type POINTAPI
X As Long
Y As Long
End Type
Const WM_LBUTTONDOWN = &H201
Const WM_LBUTTONUP = &H202
Dim ButtonPos As POINTAPI
Private Declare Function GetCursorPos Lib “ user32 (lpPoint As POINTAPI) As
ongPrivate Declare Function SetCursorPos Lib “user32 (ByVal X As Long,
yVal Y As Long) As Long
Private Declare Function SendMessage Lib “ user32 Alias “SendMessageA
ByValhwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any)
s LongPrivate Declare Function ClientToScreen Lib “user32 (ByVal hwnd As
ong, lpPoint As POINTAPI) As Long
Private Declare Function GetTickCount Lib “ kernel32 () As Long
Private Sub Command1_Click()
Beep
End Sub
Private Sub Form_Load()
Dim tmp As Long
With Command1
ButtonPos.X = (.Left + .Width / 2) / Screen.TwipsPerPixelX
ButtonPos.Y = (.Top + .Height / 2) / Screen.TwipsPerPixelY
End With
tmp = ClientToScreen(Me.hwnd, ButtonPos)
End Sub
Private Sub mnuClickButton_Click()
Const MoveStep As Integer = 50
Dim CursorPos As POINTAPI
Dim DistX As Double, DistY As Double
Dim tmp As Long
Dim i As Integer
Dim PosX As Integer, PosY As Integer
Dim TickCount As Long
tmp = GetCursorPos(CursorPos)
DistX = ButtonPos.X - CursorPos.X
DistY = ButtonPos.Y - CursorPos.Y
For i = 1 To MoveStep
PosX = CursorPos.X+DistX*i / MoveStep
PosY = CursorPos.Y+DistY*i / MoveStep
tmp = SetCursorPos(PosX, PosY)
Next i
tmp = SendMessage(Command1.hwnd,
M_LBUTTONDOWN, 0, 0)
TickCount = GetTickCount()
While GetTickCount() - TickCount < 200
mp = DoEvents()
Wend
tmp = SendMessage(Command1.hwnd,
WM_LBUTTONUP, 0, 0)
End Sub

筆者所用的操作系統為Windows 95,開發環境為VB企業版5.0。以上兩例已調試運行成功,所介紹的方法同樣適用于Windows 3.x下的VB 3.0和VB 4.0。

結語

API函數在Windows程序開發中的地位就相當于DOS的INT 21H系統功能調用,無論高級語言、可視平臺,最終都要歸結于對這些底層功能的調用。我們開發高級的應用軟件不能純粹依賴這些底層調用(當然不是技術上實現不了,而是工作量吃不消),但也不能徹底撇開,就像蓋樓時既要充分利用大量的預制件,也要用最基本的泥砂磚石一樣。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗