完全用VB6.0自身功能實現對Windows消息的攔截
發表于:2007-07-14來源:作者:點擊數:
標簽:
眾所周知,VB 的功能沒有 VC++、Delphi 這樣的全功能 開發 平臺強大,但她也足以完成我們絕大部分的工作,只要你開動腦筋,敢想敢干,我們可以讓 VB 發揮最大的效能,做出許多令人驚嘆的軟件。開發高難度軟件,并不只是 VC++ 和 Delphi 的專利! 過去普遍認
眾所周知,VB 的功能沒有 VC++、Delphi 這樣的全功能
開發平臺強大,但她也足以完成我們絕大部分的工作,只要你開動腦筋,敢想敢干,我們可以讓 VB 發揮最大的效能,做出許多令人驚嘆的軟件。開發高難度軟件,并不只是 VC++ 和 Delphi 的專利!
過去普遍認為 VB 無法自定義攔截 Windows 的消息,只能靠 VB 本身提供的幾個有限的事件來編程,這有很大的局限性。缺少消息捕獲,同時又被認為不支持回調函數機制(主要是因為 VB 沒有指針,更談不上函數指針),這造成了 VB 編程的很大局限性。事實上,VB 可以采用別的辦法變相地實現這一機制。從 VB 5.0 開始就提供了 AddressOf 操作符,利用這個操作符可以獲取 VB 自定義函數的地址。有了函數地址就可以采用回調函數的機制了。當然,VB 仍然無法實現 VB 函數之間的地址傳遞,她只支持 VB 函數到 DLL 的函數抵制傳遞。但是,這已經足夠了。下面這個程序,就是采用了這一方法,程序中只有一個主窗體,通過設置屬性,使得主窗體沒有邊框,沒有標題欄,不能改變大小,不能通過標題欄托動。但是通過攔截 Windows 消息可以使得鼠標處在窗體中的任意位置都可以托動它,就像按住標題欄托動一樣。這個程序沒有用到任何附加的控件,全部采用 VB 代碼完成。注意,請增加一個公共模塊,以便聲明一些函數和常數。以下代碼在 VB 6.0 中通過。
' ===================================
' 這是公共模塊的代碼
Attribute VB_Name = "Module1"
Option Explicit
Public Const WM_NCHITTEST = &H84
Public Const VK_LBUTTON = &H1
Public Const HTCAPTION = 2
Public Const HTCLIENT = 1
Public Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Public 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
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Const GWL_WNDPROC = -4
Global lpPrevWndProc As Long
Global gHW As Long
' 這里是關鍵,我自定義了一個窗口函數(回調函數),以替代 VB 窗體自己的默認窗口函數。' 窗口函數是干什么的?它就是負責處理 Windows 發送給它的消息,并加以過濾,篩選出它感興趣' 的消息,映射成為事件供我們使用。VB 中每個窗口都有一個默認的窗口函數,我們是看不到的。' 有很多消息都被 VB 的默認窗口函數過濾掉了。了解 C/C++/Delphi 程序設計的朋友應該知道這些。
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' 我們也進行消息過濾,不過我們指攔截我們感興趣的消息
' 其他消息我們懶得處理,交給 VB 默認的窗口函數去處理吧。
Select Case uMsg
Case WM_NCHITTEST ' 攔截 WM_NCHITTEST 消息
If GetAsyncKeyState(VK_LBUTTON) < 0 Then ' 是否有鼠標左鍵在窗體客戶區按下?
' 如果是,函數返回值被設置為 HTCAPTION,欺騙 Windows,讓它以為鼠標是按在標題欄
' Windows 是通過窗口函數的返回值進行判斷處理的
WindowProc = HTCAPTION
Exit Function
Else
' 其他的我們不管,還是規規矩矩的該怎么樣就怎么樣
WindowProc = HTCLIENT
Exit Function
End If
End Select
' 這里又是關鍵,因為其他我們不關心的消息我們自己不處理,所以必須由 VB 的默認處理函數處理
' lpPrevWndProc 其實就是一個函數指針,它指向 VB 默認窗口函數
WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function
' ===================================
' 這是窗體的代碼
VERSION 5.00
Begin VB.Form Form1
BorderStyle = 0 'None
ClientHeight = 3195
ClientLeft = 0
ClientTop = 0
ClientWidth = 4680
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 3195
ScaleWidth = 4680
ShowInTaskbar = 0 'False
StartUpPosition = 3 '窗口缺省
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 375
Left = 2160
TabIndex = 0
Top = 720
Width = 1575
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private Sub Command1_Click()
Unload Me ' 按下這個按鈕就退出了
End Sub
Private Sub Form_Load()
gHW = Me.hwnd ' 保存窗體的句柄
' 下面是關鍵,完成兩個工作:1、將我們自己的全局函數替換為新的窗體回調函數
' 2、保存原來的 VB 默認窗戶口函數地址
lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
End Sub
其實,一切就這么簡單。有了這種辦法,Windows 中需要回調函數的 API 函數我們都可以調用了,有很多的功能我們都可以用 VB 來實現了。注意,我們自定義的回調函數只能是模塊中定義的全局函數!不能在窗體中定義。
寫出來只是想拋磚引玉,其實有很多功能不需要到處去找控件的。我現在在研究用 VB 6 + DirectX 7/8 寫
游戲。游戲速度當然不可能達到 C++ 的程度,但是足夠應付一些中小型的游戲題材了,比如 RPG 的。這又有很多值得寫出來的了。雖然不是什么新鮮的話題,但是其中仍不乏許多的技巧和編程思想。希望對此有研究的朋友能夠多多出來交流指點一二。
原文轉自:http://www.anti-gravitydesign.com