はじめに
VBAのユーザーフォームで、ユーザーに特定のボタン(例: 「登録」や「キャンセル」)からの終了を強制し、右上の「閉じる」ボタン(×ボタン)ではフォームを閉じられないようにしたい、という場合があります。これにより、意図しないタイミングでフォームが閉じられ、処理が中途半半端になるのを防ぐことができます。
このような制御は、Windows API関数を直接呼び出すことで実装可能です。この記事では、ユーザーフォームのウィンドウメニューから「閉じる」項目を削除し、×ボタンをグレーアウトさせて無効化する、高度なテクニックを解説します。
「閉じる」ボタンを無効化するVBAサンプルコード
このマクロは、Declare
ステートメント、Const
(定数)、およびフォームの _Activate()
イベントプロシージャで構成されます。API関連の定義は、標準モジュールに記述するのが一般的です。
ステップ1: API宣言を標準モジュールに記述
'--- 標準モジュール (例: Module1) に記述 ---
' 64bit/32bit両対応
#If VBA7 Then
Public Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Public Declare PtrSafe Function GetSystemMenu Lib "user32" (ByVal hwnd As LongPtr, ByVal bRevert As Long) As LongPtr
Public Declare PtrSafe Function DeleteMenu Lib "user32" (ByVal hMenu As LongPtr, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Public Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long, ByVal bRevert As Long) As Long
Public Declare Function DeleteMenu 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
#End If
' DeleteMenuで使う定数
Public Const SC_CLOSE As Long = &HF060
Public Const MF_BYCOMMAND As Long = &H0
ステップ2: ユーザーフォームのイベントプロシージャを記述
このコードは、ユーザーフォームのコードモジュールに直接記述します。Initialize
イベントではまだウィンドウが完全に描画されていない可能性があるため、より確実な Activate
イベントを使用します。
【ユーザーフォーム: MainForm
】
' フォームがアクティブになったときに実行されるイベント
Private Sub UserForm_Activate()
Dim formHandle As LongPtr
Dim menuHandle As LongPtr
' 1. FindWindowで、このフォームのウィンドウハンドルを取得
' クラス名は "ThunderDFrame", ウィンドウ名はフォームのCaption
formHandle = FindWindow("ThunderDFrame", Me.Caption)
' 2. フォームのシステムメニューのハンドルを取得
menuHandle = GetSystemMenu(formHandle, 0&)
' 3. メニューから「閉じる」コマンドを削除
DeleteMenu menuHandle, SC_CLOSE, MF_BYCOMMAND
' 4. メニューバーを再描画して変更を適用
DrawMenuBar formHandle
End Sub
コードの解説
UserForm_Activate()
イベント
フォームが表示され、アクティブになった瞬間に実行されるイベントです。ウィンドウハンドルを取得するような、ウィンドウの存在が前提となる操作は、Initialize
よりもこの Activate
イベントで行う方が安全です。
formHandle = FindWindow("ThunderDFrame", Me.Caption)
FindWindow
: 指定した条件のウィンドウを探し出すAPI関数です。"ThunderDFrame"
: VBAのユーザーフォームのウィンドウクラス名です。これは固定の文字列です。Me.Caption
: 探したいウィンドウのタイトルバーのテキストを指定します。Me.Caption
で、このフォーム自身のタイトル(Captionプロパティの値)を渡しています。
GetSystemMenu
, DeleteMenu
, DrawMenuBar
これらのAPI関数の働きは、Excel本体の「閉じる」ボタンを無効化する記事で解説したものと同じです。対象が Application.hwnd
(Excel本体)から formHandle
(ユーザーフォーム)に変わっただけです。
GetSystemMenu
: フォームのシステムメニューを取得します。DeleteMenu
: そのメニューから「閉じる」コマンド(SC_CLOSE
)を削除します。DrawMenuBar
: 変更を画面に反映させます。
元に戻すには?
この方法で無効化した「閉じる」ボタンは、フォームが閉じられる(Unload
される)と、次回表示時には元の状態に戻ります。もしフォームを閉じずに元に戻したい場合は、GetSystemMenu formHandle, 1&
を実行する別のマクロを用意する必要があります。
まとめ
今回は、Windows APIを駆使して、ユーザーフォームの「閉じる」ボタン(×ボタン)を無効化する高度なテクニックを解説しました。
- ユーザーフォームのウィンドウハンドルは、
FindWindow("ThunderDFrame", Me.Caption)
で取得できる。 - ウィンドウハンドルを取得する処理は、
UserForm_Activate
イベントで行うのが安全。 - 取得したハンドルを使い、
GetSystemMenu
,DeleteMenu
,DrawMenuBar
の順でAPIを呼び出す。
ユーザーに特定の操作フローを徹底させたい場合など、限定的な状況で非常に有効なテクニックですが、ユーザーを混乱させる可能性もあるため、利用は慎重に検討しましょう。