Posts Windows Forms - Disable the Close Button
Post
Cancel

Windows Forms - Disable the Close Button

By Cory Smith

Download source code - 6.34kb

Introduction

I've seen it asked many times, “How do I disable the close button on a Windows Form, but still have the system menu, minimize and maximize available?”.  I've also seen a lot of answers to how to accomplish this.  Let's look at the answer that appears to be very popular for solving this issue.

CloseButtonRemove.vb

Option Explicit On
Option Strict
On

Imports
System.Runtime.InteropServices

Public Class CloseButtonRemove

  Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Integer, ByVal revert As Integer) As Integer
  Private Declare Function GetMenuItemCount Lib "user32" (ByVal menu As Integer) As
Integer
  Private Declare Function RemoveMenu Lib "user32" (ByVal menu As Integer, ByVal position As Integer, ByVal flags As Integer) As
Integer
 
Private Declare Function DrawMenuBar Lib "user32" (ByVal hwnd As Integer) As
Integer
 
Private Declare Function FormatMessageA Lib "kernel32" (ByVal flags As Integer, ByRef source As Object, ByVal messageID As Integer, ByVal languageID As Integer, ByVal buffer As String, ByVal size As Integer, ByRef arguments As Integer) As Integer

  Private Const MF_BYPOSITION As Integer = &H400
  Private Const MF_DISABLED As Integer
= &H2

  Private Sub New ()
  End Sub

  Public Shared Sub Disable(ByVal form As System.Windows.Forms.Form)
   
' Get handle to system menu for the form provided.
   
Dim menu As Integer
= GetSystemMenu(form.Handle.ToInt32, 0)
   
' Get number of items in this system menu.
   
Dim count As Integer
= GetMenuItemCount(menu)
   
' Remove last item from system menu (last item should be ’Close’).
   
If RemoveMenu(menu, count - 1, MF_DISABLED Or MF_BYPOSITION) = 0
Then
     
Throw New
Exception(FormatMessage(Err.LastDllError))
   
Else
     
' On success, force a redraw of the system menu.
     
If DrawMenuBar(form.Handle.ToInt32) = 0
Then
       
Throw New
Exception(FormatMessage(Err.LastDllError))
      End
If
   
End
If
 
End Sub

  Public Shared Function FormatMessage(ByVal [error] As Integer) As String
   
Const FORMAT_MESSAGE_FROM_SYSTEM As Short
= &H1000
    Const LANG_NEUTRAL As Short
= &H0
    Dim buffer As String
= Space(999)
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, [error], LANG_NEUTRAL, buffer, 999, 0)
    buffer = Replace(Replace(buffer, Chr(13), ""), Chr(10), "")
    Return
buffer.Substring(0, buffer.IndexOf(Chr(0)))
  End
Function

End

Class

I've attempted to document the code fully so that it's easier to see what is going on.  Also, bear in mind that most of the sample code floating around on the net doesn't take into account possible errors occuring doing this task in more of a brute force method.  Based on the SDK documentation, I've added error checking where appropriate.  Also, take note of the FormatMessage function that is included in this class.  It may come in handy when your working with other Win32 Interop functions as it will (usually) return a string version of a numeric error value.

So, you might ask, what's the problem with this code?  It does what was asked.  Actually, it doesn't.  The question was “How do I ***disable*** the close button?”; which would mean disabling the 'close' menu item as well... not deleting it from the menu.

The following code shows how to disable the close button according to the SDK documentation.

CloseButton.vb

Option Explicit On
Option Strict
On

Public

Class CloseButton

  Private Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Integer, ByVal revert As Integer) As Integer
  Private Declare Function EnableMenuItem Lib "user32" (ByVal menu As Integer, ByVal ideEnableItem As Integer, ByVal enable As Integer) As Integer

  Private Const SC_CLOSE As Integer = &HF060
  Private Const MF_BYCOMMAND As Integer
= &H0
  Private Const MF_GRAYED As Integer
= &H1
  Private Const MF_ENABLED As Integer
= &H0

  Private Sub New()
  End Sub

  Public Shared Sub Disable(ByVal form As System.Windows.Forms.Form)
    ' The return value specifies the previous state of the menu item (it is either
    ' MF_ENABLED or MF_GRAYED). 0xFFFFFFFF indicates   that the menu item does not exist.
    Select Case EnableMenuItem(GetSystemMenu(form.Handle.ToInt32, 0), SC_CLOSE, MF_BYCOMMAND Or
MF_GRAYED)
      Case
MF_ENABLED
      Case
MF_GRAYED
      Case
&HFFFFFFFF
        Throw New
Exception("The Close menu item does not exist.")
      Case
Else
    End
Select
  End
Sub

End

Class

As you can see, we now only require two API declarations.  Also, there are not LastDllError checks necessary in order to determine the error since our single call returns one of three values as described by the SDK documentation.

To use this code within your Windows Form application, just import one of the classes into your code and add the CloseButton.Disable(Me) line to your Form1_Load (as an example).  For example:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  CloseButton.Disable(Me)
End Sub

You will also need to add the following code to your form if you will be allowing the form to minimize or maximize:

Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
  CloseButton.Disable(
Me)
End Sub

Thanks to EdwardA on GDN for pointing out that whenever the form is minimized or maximized, the close button becomes enabled again.  Not really sure at this point why this is, however, overriding the OnSize event will allow you to handle this issue.

[updated] - 10.31.2003 12:39PM -  Added information about using OnResize.

This post is licensed under CC BY 4.0 by the author.