# Visual Basic > Visual Basic FAQs >  Classic VB - How can I Hide/Unhide the Form's "X" Button

## MartinLiss

Credit for the Hide/Unhide code goes to crptcblade.


VB Code:
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd  _
As Long, ByVal bRevert As Long) As Long
Public Declare Function RemoveMenu Lib "user32" (ByVal hMenu _
 As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Public Declare Function DrawMenuBar Lib "user32" (ByVal hwnd  _
As Long) As Long
 Private Const MF_BYCOMMAND = &H0&
Private Const SC_CLOSE = &HF060&
 Public Sub SetXState(frm As Form, blnState As Boolean)
    Dim hMenu As Long
     hMenu = GetSystemMenu(frm.hwnd, blnState)
    Call RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND)
    Call DrawMenuBar(frm.hwnd)
 End Sub

Example of Usage:

VB Code:
Private Sub Command1_Click()
    If opt(0).Value = True Then 
        SetXState Me, True   'enable the X button
    ElseIf opt(1).Value = True Then
        SetXState Me, False  'disable the X button
    End If
End Sub

You can however avoid all that and just do this

VB Code:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    If UnloadMode = vbFormControlMenu Then
        cmdExit = True ' Change cmdExit to the name of your exit command button
                       ' and the code behind that button will be executed
    End If
End Sub

----------


## si_the_geek

An alternative method is to disable both the close button, and the close menu option from the system menu. 

To do that you can use this code:  (originally posted by Hack)

VB Code:
Private Const SC_CLOSE As Long = &HF060&
Private Const MIIM_STATE As Long = &H1&
Private Const MIIM_ID As Long = &H2&
Private Const MFS_GRAYED As Long = &H3&
Private Const WM_NCACTIVATE As Long = &H86
 Private Type MENUITEMINFO
    cbSize As Long
    fMask As Long
    fType As Long
    fState As Long
    wID As Long
    hSubMenu As Long
    hbmpChecked As Long
    hbmpUnchecked As Long
    dwItemData As Long
    dwTypeData As String
    cch As Long
End Type
 Private Declare Function GetSystemMenu Lib "user32" ( _
    ByVal hWnd As Long, ByVal bRevert As Long) As Long
 Private Declare Function GetMenuItemInfo Lib "user32" Alias _
    "GetMenuItemInfoA" (ByVal hMenu As Long, ByVal un As Long, _
    ByVal b As Boolean, lpMenuItemInfo As MENUITEMINFO) As Long
 Private Declare Function SetMenuItemInfo Lib "user32" Alias _
    "SetMenuItemInfoA" (ByVal hMenu As Long, ByVal un As Long, _
    ByVal bool As Boolean, lpcMenuItemInfo As MENUITEMINFO) As Long
 Private Declare Function SendMessage Lib "user32" Alias _
    "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _
    ByVal wParam As Long, lParam As Any) As Long
 Private Declare Function IsWindow Lib "user32" _
    (ByVal hWnd As Long) As Long
 ' Enables / Disables the close button on the titlebar and in the system menu
' of the form window passed.
' Return Values:
'
'    0  Close button state changed succesfully / nothing to do.
'   -1  Invalid Window Handle (hWnd argument) Passed to the function
'   -2  Failed to switch command ID of Close menu item in system menu
'   -3  Failed to switch enabled state of Close menu item in system menu
 Public Function EnableCloseButton(ByVal hWnd As Long, Enable As Boolean) 
As Integer
    Const xSC_CLOSE As Long = -10
    ' Check that the window handle passed is valid    
    EnableCloseButton = -1
    If IsWindow(hWnd) = 0 Then Exit Function
    ' Retrieve a handle to the window's system menu    
    Dim hMenu As Long
    hMenu = GetSystemMenu(hWnd, 0)    
    ' Retrieve the menu item information for the close menu item/button    
    Dim MII As MENUITEMINFO
    MII.cbSize = Len(MII)
    MII.dwTypeData = String(80, 0)
    MII.cch = Len(MII.dwTypeData)
    MII.fMask = MIIM_STATE
    
    If Enable Then
        MII.wID = xSC_CLOSE
    Else
        MII.wID = SC_CLOSE
    End If
        EnableCloseButton = -0
    If GetMenuItemInfo(hMenu, MII.wID, False, MII) = 0 Then Exit Function
    ' Switch the ID of the menu item so that VB can not undo the action itself    
    Dim lngMenuID As Long
    lngMenuID = MII.wID
    
    If Enable Then
        MII.wID = SC_CLOSE
    Else
        MII.wID = xSC_CLOSE
    End If
        MII.fMask = MIIM_ID
    EnableCloseButton = -2
    If SetMenuItemInfo(hMenu, lngMenuID, False, MII) = 0 Then Exit Function
    ' Set the enabled / disabled state of the menu item    
    If Enable Then
        MII.fState = (MII.fState Or MFS_GRAYED)
        MII.fState = MII.fState - MFS_GRAYED
    Else
        MII.fState = (MII.fState Or MFS_GRAYED)
    End If
    MII.fMask = MIIM_STATE
    EnableCloseButton = -3
    If SetMenuItemInfo(hMenu, MII.wID, False, MII) = 0 Then Exit Function
    SendMessage hWnd, WM_NCACTIVATE, True, 0    
    EnableCloseButton = 0    
End Function
Usage:

VB Code:
Private Sub Form_Load()
        'enabled
        EnableCloseButton Me.hWnd, True
End Sub
 Private Sub Command1_Click()
      'disabled
       EnableCloseButton Me.hWnd, False
End Sub

----------


## Mark Gambo

A simpler way is to just catch the form close event:

VB Code:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    If UnloadMode = vbFormControlMenu Or UnloadMode = 1 Then
        'the X has been clicked or the user has pressed Alt+F4
        Cancel = True
    End If
End Sub

----------


## LaVolpe

This will toggle system menu items. See comments in code


```
Option Explicit
Private Declare Function GetSystemMenu Lib "user32.dll" (ByVal hWnd As Long, ByVal bRevert As Long) As Long 'used in clsMenubar and clsButtons
Private Declare Function GetMenuItemInfo Lib "user32" Alias "GetMenuItemInfoA" (ByVal hMenu As Long, ByVal pID As Long, ByVal byPos As Long, lpMenuItemInfo As Any) As Long
Private Declare Function SetMenuItemInfo Lib "user32.dll" Alias "SetMenuItemInfoA" (ByVal hMenu As Long, ByVal pID As Long, ByVal byPos As Long, lpcMenuItemInfo As Any) As Long
Private Declare Function DrawMenuBar Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long 'also used in CustomWindow
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long 'also used in CustomWindow
Private Type MENUITEMINFO        ' used to retrieve/store menu items
     cbSize As Long              ' this structure is used with all O/S
     fMask As Long
     fType As Long
     fState As Long
     wID As Long
     hSubMenu As Long
     hbmpChecked As Long
     hbmpUnchecked As Long
     dwItemData As Long
     dwTypeData As Long 'String
     cch As Long
     hBmpData As Long
End Type
 Private Enum SystemMenuIdEnum
    SC_CLOSE = &HF060&          ' Close menu item & X button
    SC_MAXIMIZE = &HF030&       ' Minimize menu item & button
    SC_MINIMIZE = &HF020&       ' Maximize menu item & button
    SC_MOVE = &HF010&           ' Move menu item & prevent dragging window
    SC_SIZE = &HF000&           ' Size menu item & prevent sizing via borders
   'SC_RESTORE = &HF120&        ' shouldn't need to toggle this, doesn't prevent restore after maximizing/minmizing
End Enum
Private Const MIIM_STATE As Long = 1
Private Const MFS_DISABLED As Long = 3

Private Sub EnableSysMenuItem(ByVal hWnd As Long, ByVal MenuID As SystemMenuIdEnum, ByVal Enable As Boolean)

    ' Conflicts with these form properties:
    '   ControlBox = False & not borderless else no system menu exists
    '   MinButton = True else no minimize menu item to toggle
    '   MaxButton = True else no maximize menu item to toggle
    '   Movable = True else toggling has no effect
    '   BorderStyle is not a fixed-style else no size menu item to toggle
    ' Toggling menu items may not prevent manipulation via SendMessage WM_SYSCOMMAND
   
    ' Alt+F4 needs handling if toggling Close menu item else Alt+F4 is always active
    ' Sample code shown below can be replaced by subclassing
    ' -------------------------------------------------------------------------------
    '   Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    '       If UnloadMode = vbFormControlMenu Then
    '           If IsSysMenuItemEnabled(Me.hWnd, SC_CLOSE) = False Then Cancel = 1
    '       End If
    '   End Sub
    ' -------------------------------------------------------------------------------
   
    Dim hMenu As Long, MII As MENUITEMINFO
    Dim lID As Long, lStyle As Long
    Const MIIM_ID As Long = 2
    Const GWL_STYLE As Long = -16
    Const WS_MAXIMIZEBOX As Long = &H10000
    Const WS_MINIMIZEBOX As Long = &H20000
    Const DISABLE_MASK As Long = &HEFFF&
   
    Select Case MenuID
    Case SC_CLOSE, SC_MOVE, SC_SIZE
    Case SC_MAXIMIZE: lStyle = WS_MAXIMIZEBOX
    Case SC_MINIMIZE: lStyle = WS_MINIMIZEBOX
   'Case SC_RESTORE                             ' un-rem if toggling this
    Case Else: Exit Sub
    End Select
   
    hMenu = GetSystemMenu(hWnd, 0)              ' get system menu handle
    If hMenu = 0 Then Exit Sub                  ' no system menu to manipulate
   
    MII.cbSize = Len(MII)
    MII.fMask = MIIM_ID Or MIIM_STATE
    If Enable = True Then
        lID = MenuID And DISABLE_MASK           ' look for disabled item to re-enable
        MII.fState = 0                          ' set new state & restore old ID
        MII.wID = MenuID
    Else
        lID = MenuID                            ' look for enabled item to disable
        MII.fState = MFS_DISABLED               ' set new state
        If MenuID = SC_CLOSE Then
            MII.fMask = MIIM_STATE              ' set only state, then ID later else X button not grayed out
        Else                                    ' otherwise, set both state & new ID at same time
            MII.wID = MenuID And DISABLE_MASK
        End If
    End If
    If SetMenuItemInfo(hMenu, lID, 0, MII) <> 0 Then
        If lID = SC_CLOSE Then                  ' if disabling X button, change its ID now
            MII.wID = MenuID And DISABLE_MASK
            MII.fMask = MIIM_ID
            SetMenuItemInfo hMenu, MenuID, 0, MII
        ElseIf lStyle <> 0 Then                 ' enable/disable the min/max titlebar graphics as needed
            If Enable = True Then
                lStyle = GetWindowLong(hWnd, GWL_STYLE) Or lStyle
            Else                                ' if both are disabled, they are removed from titlebar
                lStyle = GetWindowLong(hWnd, GWL_STYLE) And Not lStyle
            End If
            SetWindowLong hWnd, GWL_STYLE, lStyle
        End If
        DrawMenuBar hWnd                        ' per MSDN always call after calling SetMenuItemInfo
    End If

End Sub

Private Function IsSysMenuItemEnabled(ByVal hWnd As Long, ByVal MenuID As SystemMenuIdEnum) As Boolean

    Dim hMenu As Long, MII As MENUITEMINFO
   
    hMenu = GetSystemMenu(hWnd, 0)              ' get system menu handle
    If hMenu <> 0 Then                          ' simply look for passed menu item
        MII.cbSize = Len(MII)                   ' if not there, then custom-disabled else never existed
        MII.fMask = MIIM_STATE                  ' if is there, check its enabled state
        If GetMenuItemInfo(hMenu, MenuID, 0, MII) <> 0 Then
            IsSysMenuItemEnabled = ((MII.fState And MFS_DISABLED) <> MFS_DISABLED)
        End If
    End If

End Function
```

Edited: Reason for the DISABLE_MASK? We don't want the disabled menu ID to be a system ID. And if using any value >= &HF000&, Windows sends a WM_SYSCOMMAND message. By ANDing it with the mask, we change the ID so we can find it later to re-enable and also drop it from a system ID to a non-system ID. So, if adding your own custom menu items to the system menu, do not use an ID that begins with &HE in the 2nd byte to be safe, i.e., IDs between 57344 and 61439 (&HE000& thru &HEFFF&) inclusively.

----------

