vb.neteventssystemmenu

Handle clicks to custom buttons in the System Menu


I've managed to create a Class that allows me to add an 'About...' button to the System Menu of any form. This part works fine, with the button being added by the load event of the form, but how do I handle clicks of that button? Thanks.

Here is how I'm adding the button -

Private Sub mainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    {More code....}
    Dim SysMenu = New SystemMenu(Me)
    {More code....}

End Sub

And here is the SystemMenu class -

Imports System.Windows.Forms

Public Class SystemMenu

    Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As IntPtr, ByVal bRevert As Boolean) As IntPtr
    Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
    Private Const MF_STRING As Integer = &H0
    Private Const MF_SEPARATOR As Integer = &H800

    Private m_hSysMenu As IntPtr
    Private Property hSysMenu() As IntPtr
        Get
            Return Me.m_hSysMenu
        End Get
        Set(ByVal Value As IntPtr)
            Me.m_hSysMenu = Value
        End Set
    End Property

    '**
    ' Constructor
    '*
    Protected Friend Sub New(ByRef Form As Form)
        Me.hSysMenu = GetSystemMenu(Form.Handle, False)
        AddAbout(Form)
    End Sub

    '**
    ' Add an 'About' button to the system menu of the given form
    '*
    Private Sub AddAbout(ByRef Form As Form)
        AppendMenu(Me.hSysMenu, MF_SEPARATOR, 1000, Nothing)
        AppendMenu(Me.hSysMenu, MF_STRING, 1001, "About...")
    End Sub

End Class

Solution

  • Check this out. Add the SubclassedSystemMenu.vb file to the project and add this to your main form

    Private WithEvents sysMenu As SubclassedSystemMenu
    
    Protected Overrides Sub OnLoad(e As System.EventArgs)
      sysMenu = New SubclassedSystemMenu(Me.Handle.ToInt32, "&About...")
    End Sub
    

    And then subscribe to its LaunchDialog event and open the form

    Private Sub sysMenu_LaunchDialog() Handles sysMenu.LaunchDialog
      Dim f as New frmAbout
      f.ShowDialog(Me)
    End Sub
    

    And here is the SubclassedSystemMenu class

    Public Class SubclassedSystemMenu
    Inherits System.Windows.Forms.NativeWindow
    Implements IDisposable
    
    Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Int32, _
                                                         ByVal bRevert As Boolean) As Int32
    
    Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal hMenu As Int32, _
                                                                          ByVal wFlags As Int32, _
                                                                          ByVal wIDNewItem As Int32, _
                                                                          ByVal lpNewItem As String) As Int32
    
    Private Const MF_STRING As Int32 = &H0       ' Menu string format
    Private Const MF_SEPARATOR As Int32 = &H800  ' Menu separator
    Private Const WM_SYSCOMMAND As Int32 = &H112 ' System menu 
    Private Const ID_ABOUT As Int32 = 1000       ' Our ID for the new menu item
    
    Private mintSystemMenu As Int32 = 0                 ' Parent system menu handle
    Private mintHandle As Int32 = 0                     ' Local parent window handle
    Private mstrMenuItemText As String = String.Empty   ' New menu item text
    
    Public Event LaunchDialog()
    
    Public Sub New(ByVal intWindowHandle As Int32, _
                   ByVal strMenuItemText As String)
    
        Me.AssignHandle(New IntPtr(intWindowHandle))
    
        mintHandle = intWindowHandle
        mstrMenuItemText = strMenuItemText
    
        ' Retrieve the system menu handle
        mintSystemMenu = GetSystemMenu(mintHandle, 0)
    
        If AddNewSystemMenuItem() = False Then
            Throw New Exception("Unable to add new system menu items")
        End If
    
    End Sub
    
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    
        Select Case m.Msg
            Case WM_SYSCOMMAND
    
                MyBase.WndProc(m)
    
                If m.WParam.ToInt32 = ID_ABOUT Then
                    If mintSystemMenu <> 0 Then
                        RaiseEvent LaunchDialog()
                    End If
                End If
    
            Case Else
                MyBase.WndProc(m)
        End Select
    
    End Sub
    
    Public Sub Dispose() Implements System.IDisposable.Dispose
    
        If Not Me.Handle.Equals(IntPtr.Zero) Then
            Me.ReleaseHandle()
        End If
    
    End Sub
    
    Private Function AddNewSystemMenuItem() As Boolean
        Try
            ' Append the extra system menu items
            Return AppendToSystemMenu(mintSystemMenu, mstrMenuItemText)
    
        Catch ex As Exception
            Return False
        End Try
    End Function
    
    Private Function AppendToSystemMenu(ByVal intHandle As Int32, _
                                        ByVal strText As String) As Boolean
    
        Try
            ' Add the seperator menu item
            Dim intRet As Int32 = AppendMenu(intHandle, MF_SEPARATOR, 0, String.Empty)
    
            ' Add the About... menu item
            intRet = AppendMenu(intHandle, MF_STRING, ID_ABOUT, strText)
    
            If intRet = 1 Then
                Return True
            Else
                Return False
            End If
    
        Catch ex As Exception
            Return False
        End Try
    End Function
    

    End Class