跳轉到內容

Visual Basic/子類化

來自 Wikibooks,為開放世界提供開放書籍

當您想要新增 Visual Basic 不直接支援的功能時,子類化可能很有用。為了解釋它是如何工作的,我們必須先了解一些背景理論。

Windows 中的所有視窗(例如窗體、按鈕、列表框等)都具有一個函式,作業系統或其他程式可以呼叫該函式來與程式通訊。例如,Windows 可以傳送有關事件訊息,例如滑鼠指標移過視窗、視窗處於焦點時按下某個鍵,以及更多情況。程式也可以傳送請求視窗資訊的訊息;例如,EM_GETLINECOUNT 訊息要求文字框傳送回它儲存的文字行數。您也可以定義自己的函式。

要呼叫這些特殊函式,您可以使用PostMessageSendMessageCallWindowProc(如果您知道函式的地址,則僅使用最後一個)。

通常,這樣的過程看起來像這樣

  Public Function WindowProc(ByVal hwnd As Long, _
                             ByVal uMsg As Long, _
                             ByVal wParam As Long, _ 
                             ByVal lParam As Long) As Long
    	
    ' Your code here
    
    Select Case uMsg
      Case 0
        ' React to message 0 
      Case 1
        ' React to message 1
    End Select
    		
    WindowProc = 0 ' Return a value to the caller
    	
   End Function

在此函式中,hwnd 是呼叫方嘗試聯絡的視窗控制代碼uMsg訊息識別符號,它說明呼叫是關於什麼的;wParamlParam 用於呼叫方和視窗之間商定的任何用途。控制代碼 hwnd 不是地址,而是由 Windows 用於查詢地址。

例如,如果我們要設定窗體標題欄中顯示的文字,可以使用以下程式碼

  Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
          (ByVal hwnd As Long, _ 
           ByVal wMsg As Long, _
           ByVal wParam As Integer, _
           ByVal lParam As Any) As Long
    
    Private Const WM_SETTEXT = &HC
    
  Private Sub Form_Load()  
    SendMessage Me.hwnd, WM_SETTEXT, 0&, "This is a test"
  End Sub

The receiver will get this message via its window function, which will look something like this:

  Public Function WindowProc(ByVal hwnd As Long, _
                             ByVal uMsg As Long, _
                             ByVal wParam As Long, _ 
                             ByVal lParam As Long) As Long
    	
    ' hwnd is now equal to Me.hwnd, 
    ' uMsg is WM_SETTEXT, 
    ' wParam is 0 
    ' lParam is the address of the text: "This is a test"
         	
    ' It doesn't actually look like this of course, but this gives 
    ' a good enough impression of what happens under the surface
    Select Case uMsg
    	Case WM_SETTEXT	
        Me.Caption = lParam
      'Case ...
        '... many more here
    End Select    
  End Function

'one thing i have to say is if you press alt+f11 in vbe subclass not remove when you use unsubclassform and convey to vbe so this function have to be updated

為什麼要子類化

[編輯 | 編輯原始碼]

子類化的意義何在?

使用此技術,我們可以完全用自己的視窗函式替換程式的視窗函式。然後,我們可以以 Visual Basic 不允許的方式響應訊息,我們可以選擇將訊息進一步傳送到原始視窗函式,也可以不傳送,我們可以根據自己的喜好以任何方式修改它們。

要指定要使用我們的視窗函式,我們使用API 呼叫SetWindowLong。研究以下示例,並將其放在一個基本模組中

  Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
          (ByVal hwnd As Long, _
           ByVal nIndex As Long, _  
           ByVal dwNewLong As Long) As Long
  
  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
    
  Declare Function SetClipboardViewer Lib "user32" _
          (ByVal hwnd As Long) As Long
    
  Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
          (ByVal hwnd As Long, _
           ByVal wMsg As Long, _ 
           ByVal wParam As Integer, _
           ByVal lParam As Any) As Long
    
  Public Const WM_SETTEXT = &HC
  Public Const GWL_WNDPROC = (-4)
    
  Private PrevProc As Long ' The address of the original window function
    
  Public Sub SubclassForm(F As Form)  
    ' AddressOf WindowProc = finds the address of a function
    PrevProc = SetWindowLong(F.hwnd, GWL_WNDPROC, AddressOf WindowProc)
  End Sub
    
  Public Sub UnSubclassForm(F As Form)    
    ' It is _very_ important that we restore the original window function,
    ' because VB will crash if we don't.
    SetWindowLong F.hwnd, GWL_WNDPROC, PrevProc
  End Sub
    
  Public Function WindowProc(ByVal hwnd As Long, _
                             ByVal uMsg As Long, _
                             ByVal wParam As Long, _ 
                             ByVal lParam As Long) As Long
        
    Dim sTemp As String
        
    If uMsg = WM_SETTEXT Then
      ' Don't let the text get through, replace it with our own. Also, because all
      ' strings in VB are of the format UTF-16 (Unicode) and the receiving method
      ' expects a zero-terminated ASCII-string, it is necessary to convert it before
      ' passing it further down the chain.
      sTemp = StrConv("Subclassing" & Chr(0), vbFromUnicode)
      lParam = StrPtr(sTemp) ' get the address of our text
    End If
        
    ' Call the original function
    WindowProc = CallWindowProc(PrevProc, hwnd, uMsg, wParam, lParam)
  End Function

新增一個帶有一個按鈕cmdTest 的窗體,並新增以下程式碼

  Private Sub cmdTest_Click()
    SendMessage Me.hwnd, WM_SETTEXT, 0&, "This is a test"
  End Sub
    
  Private Sub Form_Load()    
    ' Start subclassing
    SubclassForm Me    
  End Sub
    
  Private Sub Form_Unload(Cancel As Integer)
    ' WARNING: If you stop the project (for example with the stop button) without calling this, 
    ' your program, as well as the VB IDE, will most likely crash.
    UnSubclassForm Me
  End Sub

當您單擊cmdTest 按鈕時,您會看到顯示的文字不是“這是一個測試”,而是“子類化”。

上一個: Windows_API 目錄 下一個: External_Processes
華夏公益教科書