Adapt windows messages in this class

2019-09-20 11:47发布

I would like to know if this code can be adapted to use it inside a normal class (Example: Form1 Class) to don't need subclassing a Control.

Code is from BlueMonkMN here Capture the option selected by the user in a windows default contextmenu?

Class MyTextBox : Inherits TextBox
  Public Enum ContextCommands
     WM_CUT = &H300
     WM_COPY = &H301
     WM_PASTE = &H302
  End Enum

  Public Class ContextCommandEventArgs
     Inherits EventArgs
     Public Property Command As ContextCommands
  End Class

  Event OnCut(sender As Object, e As ContextCommandEventArgs)
  Event OnCopy(sender As Object, e As ContextCommandEventArgs)
  Event OnPaste(sender As Object, e As ContextCommandEventArgs)

  Protected Overrides Sub WndProc(ByRef m As Message)
     MyBase.WndProc(m)
     Select Case m.Msg
        Case ContextCommands.WM_CUT
           RaiseEvent OnCut(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_CUT})
        Case ContextCommands.WM_COPY
           RaiseEvent OnCopy(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_COPY})
        Case ContextCommands.WM_PASTE
           RaiseEvent OnPaste(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_PASTE})
     End Select
  End Sub
End Class

To understand better my problem, I'll show you what I've tried to adapt it:

Public Class Form1

    Public Enum ContextCommands
        WM_CUT = &H300
        WM_COPY = &H301
        WM_PASTE = &H302
    End Enum

    Public Class ContextCommandEventArgs
        Inherits EventArgs
        Public Property Command As ContextCommands
    End Class

    Event OnCut(TextBox1 As Object, e As ContextCommandEventArgs)
    Event OnCopy(TextBox1 As Object, e As ContextCommandEventArgs)
    Event OnPaste(TextBox1 As Object, e As ContextCommandEventArgs)

    Protected Overrides Sub WndProc(ByRef m As Message)
        MyBase.WndProc(m)
        Select Case m.Msg
            Case ContextCommands.WM_CUT
                RaiseEvent OnCut(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_CUT})
            Case ContextCommands.WM_COPY
                RaiseEvent OnCopy(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_COPY})
            Case ContextCommands.WM_PASTE
                RaiseEvent OnPaste(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_PASTE})
        End Select
    End Sub

Private Sub TextBox1_OnTextCommand() Handles TextBox1.TextChanged
    If WM_CUT then...
    Elseif WM_COPY then...
    Elseif WM_Paste then...
    End if
End Sub

Private Sub TextBox1_OnTextCommand() Handles Me.OnPaste, Me.OnCopy, Me.OnCut
    MsgBox("Activated")
End Sub

End Class

1条回答
Evening l夕情丶
2楼-- · 2019-09-20 12:24

This is not a simple task for a VB developer, but I hope this helps. The code no longer relates to a specific textbox, but you should be able to identify which textbox caused the event by comparing cwp.hWnd with TextBox.Handle values if you need to. EditMenuHook.Enable should probably only be called once at the beginning of your application to turn on the hook, and once at the end to turn it off.

Imports System.Runtime.InteropServices

Public Class Form1
   Protected Overrides Sub OnLoad(e As EventArgs)
      MyBase.OnLoad(e)
      EditMenuHook.Enable(True)
   End Sub

   Protected Overrides Sub OnClosed(e As EventArgs)
      EditMenuHook.Enable(False)
      MyBase.OnClosed(e)
   End Sub
End Class

Friend Class EditMenuHook
   Public Structure CWPSTRUCT
      Public lParam As IntPtr
      Public wParam As IntPtr
      Public message As UInt32
      Public hWnd As IntPtr
   End Structure

   Public Delegate Function CallBack( _
       ByVal nCode As Integer, _
       ByVal wParam As IntPtr, _
       ByVal lParam As IntPtr) As Integer

   Shared hHook As Integer = 0

   'Import for the SetWindowsHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function SetWindowsHookEx _
          (ByVal idHook As Integer, ByVal HookProc As CallBack, _
           ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
   End Function

   'Import for the CallNextHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function CallNextHookEx _
          (ByVal idHook As Integer, ByVal nCode As Integer, _
           ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
   End Function

   'Import for the UnhookWindowsHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function UnhookWindowsHookEx _
              (ByVal idHook As Integer) As Boolean
   End Function

   Private Const WH_CALLWNDPROC = 4

   Public Enum TextCommandMessage
      WM_CUT = &H300
      WM_COPY = &H301
      WM_PASTE = &H302
   End Enum

   'Keep the reference so that the delegate is not garbage collected.
   Private Shared hookproc As CallBack

   Shared Sub Enable(enable As Boolean)
      If hHook = 0 AndAlso enable = True Then
         hookproc = AddressOf EditCommandHook
         hHook = SetWindowsHookEx(WH_CALLWNDPROC, _
                                  hookproc, _
                                  IntPtr.Zero, _
                                  AppDomain.GetCurrentThreadId())
         If hHook.Equals(0) Then
            MsgBox("SetWindowsHookEx Failed")
            Return
         End If
      ElseIf hHook <> 0 AndAlso enable = False Then
         Dim ret As Boolean = UnhookWindowsHookEx(hHook)

         If ret.Equals(False) Then
            MsgBox("UnhookWindowsHookEx Failed")
            Return
         Else
            hHook = 0
         End If
      End If
   End Sub

   Public Shared Function EditCommandHook( _
      ByVal nCode As Integer, _
      ByVal wParam As IntPtr, _
      ByVal lParam As IntPtr) As Integer

      If nCode < 0 Then
         Return CallNextHookEx(hHook, nCode, wParam, lParam)
      End If

      Dim cwp = DirectCast(System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, GetType(CWPSTRUCT)), CWPSTRUCT)

      Select Case cwp.message
         Case TextCommandMessage.WM_CUT
            System.Diagnostics.Debug.WriteLine(String.Format("Cut {0}", cwp.hWnd))
         Case TextCommandMessage.WM_COPY
            System.Diagnostics.Debug.WriteLine(String.Format("Copy {0}", cwp.hWnd))
         Case TextCommandMessage.WM_PASTE
            System.Diagnostics.Debug.WriteLine(String.Format("Paste {0}", cwp.hWnd))
      End Select

      Return CallNextHookEx(hHook, nCode, wParam, lParam)
   End Function
End Class
查看更多
登录 后发表回答