Windows Forms - Disable the Close Button
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 occurring 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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:
1
2
3
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:
1
2
3
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.
UPDATE: - 10.31.2003 12:39PM - Added information about using
OnResize
.