Visual Basic 6 – Disable Min Max Buttons and Movable Property at Run Time

Recently I had need to allow one of my old Visual Basic 6 applications to run in “kiosk mode”. That is, maximize the window to fill up the screen, disable the form Min button, Max Button, Close button, prevent form re-sizing, and form dragging. There are properties to allow this at design time (MinButton, MaxButton, Movable etc) but they cannot be modified at run-time. So the problem was to set these properties at run-time. There was a quite bit of information online about it but nothing that tied it all up. Here’s my solution.

Firstly, it’s all done via API calls so you need to define a number of those as follows.

Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long, ByVal bRevert As Long) As Long
Public Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function GetMenuItemCount Lib "user32" (ByVal hMenu As Long) As Long
Public Declare Function ModifyMenu Lib "user32" Alias "ModifyMenuA" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpString As Any) As Long
Public Declare Function GetMenuString Lib "user32" Alias "GetMenuStringA" (ByVal hMenu As Long, ByVal wIDItem As Long, ByVal lpString As String, ByVal nMaxCount As Long, ByVal wFlag As Long) As Long
Public Declare Function GetSubMenu Lib "user32" (ByVal hMenu As Long, ByVal nPos As Long) As Long

Public Const MF_BYPOSITION = &H400&
Public Const MF_GRAYED = &H1&

Here’s how you put a form in “Kiosk Mode”. This code gets a reference to the system menu of the current form. It then loops through each sub menu item of the system menu getting the menu caption, it then disables the sub menu using the ModifyMenu API call. This API requires you to pass a new caption to the disabled menu item, so we just pass the menu captions back to the ModifyMenu call.

  Dim hSysMenu As Long
  Dim lngCount As Long
  Dim lngLoop As Long
  Dim strName As String
  Dim lLength As Long
  Dim hSubMenu As Long
  Dim sSubMenuName As String
  
  '
  'Get a reference to the system menu of the form
  '
  hSysMenu = GetSystemMenu(Me.hWnd, False)
  '
  'Set the border style to fixed dialog
  '
  Me.BorderStyle = 3
  '
  'Set window state to normal so we can re-size it
  '
  Me.WindowState = 0
  '
  'Set form width to screen width and position at top left of screen
  '
  Me.Width = Screen.Width
  Me.Height = Screen.Height
  Me.Top = 0
  Me.Left = 0
  '
  'If we got a reference to the system menu
  '
  If hSysMenu Then
    lngCount = GetMenuItemCount(hSysMenu)
    '
    'loop through each item in the system menu
    '
    For lngLoop = 0 To lngCount
      sSubMenuName = Space(256)
      '
      'get a reference to the sub menu
      '
      hSubMenu = GetSubMenu(hSysMenu, lngLoop)
      '
      'get the caption of the sub menu
      '
      lLength = GetMenuString(hSysMenu, lngLoop, sSubMenuName, Len(sSubMenuName), MF_BYPOSITION)
      sSubMenuName = Left(sSubMenuName, lLength)
      '
      'gray out (disable) the sub menu item
      '
      ModifyMenu hSysMenu, lngLoop, MF_BYPOSITION Or MF_GRAYED, hSubMenu, sSubMenuName
    Next lngLoop
    '
    'redraw the menu bar
    '
    DrawMenuBar Me.hWnd
  End If

The result of this is a form fills up all the screen space, and while there are min/max/close buttons they do not actually do anything. Right clicking on the menu bar shows the form system menu but all items are disabled. The form cannot be be re-sized or dragged.

Of course you’re going to want to get the form out of kiosk mode. This is actually pretty simple, just use the GetSystemMenu call again but pass TRUE as the second parameter. Here’s what I mean:

      hSysMenu = GetSystemMenu(Me.hWnd, True)
      DrawMenuBar Me.hWnd
      Me.BorderStyle = 2

According to the API documentation the second parameter (bRevert) when set to true resets the window menu back to the default state. Which is exactly what we want!

There’s other ways to do this of course. For example, the RemoveMenu API call could be called to remove the sub menu items altogether and then restored as I have above. However my solution works fine and I am pretty happy with the result!