DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

フォームの「閉じる」ボタンを無効にする

Windowsフォームの右上にある「閉じる」ボタン(Xボタン)を無効にする方法を幾つか紹介します。

ControlBoxをFalseにする方法

まず一番単純なのは、フォームのControlBoxプロパティをFalseにしたり、FormBorderStyleプロパティをNoneにしたりすることにより、「閉じる」ボタンを消す方法です。この場合は、最小化、最大化ボタン、コントロールボックスも消えてしまうという欠点があります。

閉じられる原因を調べて判断する方法

また、「フォームが閉じられる時その原因を知る」と「条件によりフォームが閉じられないようにする」により、「閉じる」ボタンが押されてフォームが閉じられる場合に、フォームを閉じないようにする方法もあります。

この方法を使った例は、次のようなものです。なおこの例では、システムメニューの「閉じる」や、「Alt」+「F4」キーも無効になります。

VB.NET
コードを隠すコードを選択
'Imports System.Security.Permissions

<SecurityPermission(SecurityAction.Demand, _
    Flags:=SecurityPermissionFlag.UnmanagedCode)> _
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    Const WM_SYSCOMMAND As Integer = &H112
    Const SC_CLOSE As Long = &HF060L

    If m.Msg = WM_SYSCOMMAND AndAlso _
        (m.WParam.ToInt64() And &HFFF0L) = SC_CLOSE Then
        Return
    End If

    MyBase.WndProc(m)
End Sub
C#
コードを隠すコードを選択
//using System.Security.Permissions;

[SecurityPermission(SecurityAction.Demand,
    Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
    const int WM_SYSCOMMAND = 0x112;
    const long SC_CLOSE = 0xF060L;

    if (m.Msg == WM_SYSCOMMAND &&
        (m.WParam.ToInt64() & 0xFFF0L) == SC_CLOSE)
    {
        return;
    }

    base.WndProc(ref m);
}
補足:上の例では「条件によりフォームが閉じられないようにする」は使いませんでした。これを使う場合は、「閉じる」ボタンが押された時にフラッグを立てて、Closingイベントで閉じないようにします。
補足:.NET Framework 2.0以降では、次のようにFormClosingイベントを使うと簡単そうに思われます。ただし、フォームのCloseメソッドで閉じる場合もCloseReasonがUserClosingとなりますので、Application.Exitメソッドなどで閉じなければならなくなります。
VB.NET
コードを隠すコードを選択
'フォームのFormClosingイベントハンドラ
Private Sub Form1_FormClosing(ByVal sender As Object, _
        ByVal e As FormClosingEventArgs) Handles MyBase.FormClosing
    If e.CloseReason = CloseReason.UserClosing Then
        e.Cancel = True
    End If
End Sub
C#
コードを隠すコードを選択
//フォームのFormClosingイベントハンドラ
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing)
        e.Cancel = true;
}

CreateParamsをオーバーライドする方法

さらに、GotDotNetの掲示板の「Disabling the close button?」(リンク切れ)にMickDohertyさんがとてもおもしろい投稿をされています。これはフォームのCreateParamsプロパティをオーバーライドするという方法で、「閉じる」ボタンが無効状態となり、押すことができなくなります。システムメニューの「閉じる」も表示されなくなり、「Alt」+「F4」キーも無効になります。 以下にそのコードを引用させていただきます。

VB.NET
コードを隠すコードを選択
'Imports System.Security.Permissions

Protected Overrides ReadOnly Property CreateParams() As _
        System.Windows.Forms.CreateParams
    <SecurityPermission(SecurityAction.Demand, _
        Flags:=SecurityPermissionFlag.UnmanagedCode)> _
    Get
        Const CS_NOCLOSE As Integer = &H200
        Dim cp As CreateParams = MyBase.CreateParams
        cp.ClassStyle = cp.ClassStyle Or CS_NOCLOSE

        Return cp
    End Get
End Property

C#
コードを隠すコードを選択
//using System.Security.Permissions;

protected override CreateParams CreateParams
{
    [SecurityPermission(SecurityAction.Demand,
        Flags = SecurityPermissionFlag.UnmanagedCode)]
    get
    {
        const int CS_NOCLOSE = 0x200;
        CreateParams cp = base.CreateParams;
        cp.ClassStyle = cp.ClassStyle | CS_NOCLOSE;

        return cp;
    }
}

Win32 APIによる方法

最後にWin32 APIを使う方法も紹介しておきます。下記の方法では、「閉じる」ボタンが無効状態となり、システムメニューの「閉じる」が表示されなくなりますが、「Alt」+「F4」キーは有効です。

VB.NET
コードを隠すコードを選択
<System.Runtime.InteropServices.DllImport("user32.dll")> _
Shared Function GetSystemMenu(ByVal hWnd As IntPtr, _
    ByVal bRevert As Boolean) As IntPtr
End Function

<System.Runtime.InteropServices.DllImport("user32.dll")> _
Shared Function GetMenuItemCount(ByVal hMenu As IntPtr) As Integer
End Function

<System.Runtime.InteropServices.DllImport("user32.dll")> _
Shared Function DrawMenuBar(ByVal hWnd As IntPtr) As Boolean
End Function

<System.Runtime.InteropServices.DllImport("user32.dll")> _
Shared Function RemoveMenu(ByVal hMenu As IntPtr, _
    ByVal uPosition As Integer, _
    ByVal uFlags As Integer) As Boolean
End Function

Protected Overrides Sub OnLoad(ByVal e As EventArgs)
    MyBase.OnLoad(e)

    Const MF_BYPOSITION As Int32 = &H400
    Const MF_REMOVE As Int32 = &H1000

    Dim menu As IntPtr = GetSystemMenu(Me.Handle, False)
    Dim menuCount As Integer = GetMenuItemCount(menu)
    If menuCount > 1 Then
        'メニューの「閉じる」とセパレータを削除
        RemoveMenu(menu, menuCount - 1, MF_BYPOSITION Or MF_REMOVE)
        RemoveMenu(menu, menuCount - 2, MF_BYPOSITION Or MF_REMOVE)
        DrawMenuBar(Me.Handle)
    End If
End Sub
C#
コードを隠すコードを選択
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int GetMenuItemCount(IntPtr hMenu);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool DrawMenuBar(IntPtr hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);

protected override void OnLoad(EventArgs e)
{
    base.OnLoad (e);

    const Int32 MF_BYPOSITION = 0x400;
    const Int32 MF_REMOVE = 0x1000;

    IntPtr menu = GetSystemMenu(this.Handle, false);
    int menuCount = GetMenuItemCount(menu);
    if (menuCount > 1)
    {
        //メニューの「閉じる」とセパレータを削除
        RemoveMenu(menu, (uint) (menuCount - 1), MF_BYPOSITION | MF_REMOVE);
        RemoveMenu(menu, (uint) (menuCount - 2), MF_BYPOSITION | MF_REMOVE);
        DrawMenuBar(this.Handle);
    }
}
  • 履歴:
  • 2007/1/15 .NET Framework 2.0に関する記述を追加。
  • 2010/6/30 WndProcとCreateParamsにSecurityPermissionAttributeを付けた。
  • 2012/4/9 「閉じられる原因を調べて判断する方法」のコードが64ビット環境でうまくいかない不具合を修正。
  • 2013/12/9 SecurityAction.LinkDemandの代わりにSecurityAction.Demandを使うようにした。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。