Using WndProc to Override Mouse Functions

Introduction

Today, I will show you how to use the simple, yet powerful, WndProc command and its associated methods to override various window and mouse functions.

But first, the basics.

Overriding

The term Overriding means to give a new function to an existing function, thus replacing the function's old methodology.

WndProc

WindowProc (or window procedure) is a user-defined callback function that processes messages sent to a window. Here is more information on it:

https://msdn.microsoft.com/en-us/library/system.windows.forms.form.wndproc%28v=vs.110%29.aspx

API

The Windows API is a set of several hundred functions and subroutines that are located in a set of files called Dynamic Link Libraries (DLLs). You can make a function from the Windows API available to your Visual Basic program by declaring the function to be callable from your program. You then can use the Windows API function as you would any built-in Visual Basic function or a function that you have written yourself.

End users cannot access these functions; however, programmers can access the code written in the DLLs through the API and use this code in their programs. This enables you to use existent code in the DLLs and save you time in the programming development cycle. The advantage of using Windows APIs in your code is that they can save development time because they contain dozens of useful functions that are already written and waiting to be used. The disadvantage is that Windows APIs can be difficult to work with and unforgiving when things go wrong.

Read here for more information:

http://www.codeguru.com/vb/gen/vb_general/ideincludingvisualstudionet/article.php/c11981/Discovering-the-API-Using-Visual-Basic-6-and-Visual-Basic-NET.htm

Our Projects

Today, I will show you different examples on how to use this feature productively.

Example 1: Disabling Right Click

The purpose of this project is to show you how to override a standard right-click event on a form to do something completely different.

Create a new Visual Basic Windows Forms project. You do not need to add any controls to it apart from a ContextMenuStrip, which is shown in Figure 1. The ContextMenuStrip deals with a user right-clicking at an appropriate place. A standard context menu is shown in Figure 2.

Wnd1
Figure 1: ContextMenuStrip

Wnd2
Figure 2: Standard Context menu

Surprisingly, there isn't much code needed to subclass your window, or mouse handles for it to be overridden, as you will see now in the following code demonstration.

Add the Right button system constant:

   'Right Button Up API Constant
   Public Const WM_RBUTTONUP = &H205

Add the Override method:

   'Override Main Window
   Protected Overrides Sub WndProc(ByRef m As Message)
      'If Message Sent = Right Button Up
      If m.Msg = WM_RBUTTONUP Then

         'Just To Show How You Can Manipulate This :)
         MessageBox.Show("Caught Event")
         'Do Nothing Further. IOW, Do Not Show
         'Context Menu
         Exit Sub
      End If

   MyBase.WndProc(m)
   End Sub

Overrides means that this sub will replace the standard function. In this case, we are overriding the WM_RBUTTONUP system message which is the action of the right mouse button being clicked. In the IF structure, I determined whether the message being sent is the right mouse button. If it is the right mouse button being pressed, I included a Messagebox that gives a dull message.

In order to put this to the test, add the following code:

   'Added A ContextMenuStrip To The Form, With Some Items
   Private Sub Test1ToolStripMenuItem_Click(ByVal sender _
         As System.Object, ByVal e As System.EventArgs) _
         Handles Test1ToolStripMenuItem.Click
      'This Will Never Be Displayed :)
      MessageBox.Show("I Will Not Show")
   End Sub

The menu items will not show when the right button is clicked, so the preceding code will never get the chance to be executed.

Example 2: Disable Mouse Scroll

You actually could achieve this with two ways. Both are quite easy; it is just the messages that differ at the end of the day.

You could add the following System constants:

   'Middle Button Down
   Private Const WM_MBUTTONDOWN As Int32 = &H207

   'Middle Button Up
   Private Const WM_MBUTTONUP As Int32 = &H208

And then catch either event in the WndProc procedure:

   'Override Main Window
   Protected Overrides Sub WndProc(ByRef m As Message)
      'If Message Sent = Right Button Up
      If m.Msg = WM_MBUTTONUP Then

         'Just To Show How You Can Manipulate This :)
         MessageBox.Show("Caught Event")
         'Do Nothing Further. IOW, Do Not Show
         'Context Menu
         Exit Sub
      End If

      MyBase.WndProc(m)
   End Sub

As you can see, it resembles the code in Example 1 quite closely.

You could also do it like this:

   Private Const WM_MOUSEWHEEL As Integer = &H20A
      Protected Overrides Sub WndProc(ByRef m As _
            System.Windows.Forms.Message)
         If Not m.Msg = WM_MOUSEWHEEL Then _
            MyBase.WndProc(m)
      End Sub

Here you make use of the WM_MOUSEWHEEL constant and check for its associated message.

Example 3: Disable System Menus (Titlebar Buttons)

This example is a bit more complicated. With this example, you will learn how to override the Titlebar buttons.

You have to keep one thing in mind here, and that is that the close, maximize, and minimize buttons on a window's titlebar are simply just small little windows. For that reason, you have to get a handle to each of those tiny windows via the GetWindowLong and SetWindowLong APIs. To make them do what you want them to do, you need to use the CallWindowProc API function. Here is the full code of a Module containing all the necessary code:

Module Module1

   'Retrieves information about the specified window
   Declare Function GetWindowLong Lib "user32.dll" Alias _
      "GetWindowLongA" (ByVal hwnd As Int32, _
      ByVal nIndex As Int32) As Int32

   'Passes message information to the specified window
   'procedure
   Declare Function CallWindowProc Lib "user32.dll" Alias _
      "CallWindowProcA" (ByVal lpPrevWndFunc As Int32, _
         ByVal hwnd As Int32, ByVal msg As Int32, _
         ByVal wParam As Int32, ByVal lParam As Int32) As Int32

   'Changes an attribute of the specified window
   Declare Function SetWindowLong Lib "USER32.DLL" Alias _
      "SetWindowLongA" (ByVal hwnd As Integer, _
         ByVal attr As Integer, _
         ByVal lVal As SubClassProcDelegate) As Integer

   ' Sets a new address for the window procedure
   Const GWL_WNDPROC As Short = (-4)

   'This message is sent to a window when the user chooses
   'a command from the window menu, formerly known as the
   'system or control menu, or when the user chooses the
   'maximize button or the minimize button.
   Private Const WM_SYSCOMMAND As Short = &H112S

   Public WndProcOld As Int32   'Old Window Procedure

   'All messages are sent to the WndProc method after
   'getting filtered through the PreProcessMessage method
   Public Function WindProc(ByVal hwnd As Int32, _
      ByVal wMsg As Int32, ByVal wParam As Int32, _
      ByVal lParam As Int32) As Int32

      'Do nothing when any system buttons are pressed
      If wMsg = WM_SYSCOMMAND Then Exit Function

      'Override old Window method
      WindProc = CallWindowProc(WndProcOld, hwnd, wMsg, _
         wParam, lParam)

   End Function

   'Function to override normal activity
   Delegate Function SubClassProcDelegate(ByVal hwnd As Integer, _
      ByVal msg As Integer, ByVal wParam As Integer, _
      ByVal lParam As Integer) As Integer

   'Subclass ( override ) procedure
    Sub SubClassWindow(ByVal hwnd As Integer)

      If WndProcOld = 0 Then

         'Replace function
         WndProcOld = SetWindowLong(hwnd, GWL_WNDPROC, _
            AddressOf WindProc)

      End If

   End Sub

End Module

Inside the Form, you simply can do the following for your system windows to be overridden:

   Private Sub cmdQuit_Click(ByVal eventSender As System.Object, _
         ByVal eventArgs As System.EventArgs) Handles cmdquit.Click
      Me.Close()
   End Sub

   Private Sub Form1_Load(ByVal eventSender As System.Object, _
         ByVal eventArgs As System.EventArgs) Handles MyBase.Load
      SubClassWindow(Handle.ToInt32)   'Override System Menu
   End Sub

Note that you will have to provide another way to exit the window, as the Close button will not work as it should; hence, the code for a button named cmdQuit.

Conclusion

I hope you have enjoyed this introduction to using WndProc inside your programs. Until we meet again.



About the Author

Hannes DuPreez

Hannes du Preez is a Microsoft MVP for Visual Basic for the ninth consecutive year. He loves technology and loves Visual Basic. He loves writing articles and proving that Visual Basic is more powerful than what most believe. His ultimate dream is to write a Visual Basic book, hopefully one day that dream will come true. You are most welcome to reach him at: ojdupreez1978@gmail.com

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date