- ベストアンサー
VBA ユーザーフォームの×ボタン制御の不具合
PowerPoint VBAで複数のユーザーフォームからなるVBAマクロを作成しました。 フォーム内の「次へ」「前へ」ボタンでのみ、マクロの実行制御をしているので、途中で右上の×を押されると、想定外エラーが発生します。 そこで、一番下に貼りつけたようなコードを全てのフォームに挿入することで、右上の×が表示されないようにしました。 あくまでフォームにしかコードは埋め込んでいません。 (標準モジュール、クラスには入ってません) ですが、極稀に、「フォームの右上×」ではなく、「PowerPointの右上×」が非表示になってしまう現象が発生します。 いろいろやるうちに再現はするのですが、厳密な再現手順がよくわかりません。 状況と下記ソースから、どこらへんに原因がありそうかアドバイス頂けないでしょうか? 全コードは出せない部分が多いのですが、アドバイスにあたり必要なコードがあれば、別途貼らせて頂きます。 Private Const GWL_STYLE = (-16) Private Const WS_SYSMENU = &H80000 ' ウィンドウに関する情報を返す Private Declare Function GetWindowLong Lib "USER32.dll" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long ' ウィンドウの属性を変更 Private Declare Function SetWindowLong Lib "USER32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long ' Activeなウィンドウのハンドルを取得 Private Declare Function GetActiveWindow Lib "USER32.dll" () As Long ' メニューバーを再描画 Private Declare Function DrawMenuBar Lib "USER32.dll" (ByVal hWnd As Long) As Long ' フォームアクティブ時処理 Private Sub UserForm_Activate() Dim hWnd As Long Dim Wnd_STYLE As Long hWnd = GetActiveWindow() Wnd_STYLE = GetWindowLong(hWnd, GWL_STYLE) Wnd_STYLE = Wnd_STYLE And (Not WS_SYSMENU) SetWindowLong hWnd, GWL_STYLE, Wnd_STYLE DrawMenuBar hWnd End Sub
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
こんにちは。 今、見ただけで試験していませんが、どこかで拾ったコードでしょうか? > hWnd = GetActiveWindow() GetActiveWindow で、ハンドルを取るというのは目的が違うのではありませんか? Office のバージョンによっても違いますが、現行では、UserForm のクラス名、"ThunderDFrame" だったはすですね。 だから、 Private Declare Function FindWindow Lib "USER32.dll" Alias "FindWindowA" _ (ByVal lpClassName As String, ByVal lpWindowName As String) As Long '------------------------------------------- strClassName = "ThunderDFrame" hWnd = FindWindow(strClassName, Me.Caption) などとしますね。もしかしたら、UserForm 自身に、ハンドルを持っているバージョンもあったような気がしますが、そうしたら、直接使ってください。 '------------------------------------------- それと、 >Private Const GWL_STYLE = (-16) >Private Const WS_SYSMENU = &H80000 Long型と指定したほうがよいのでは? 最後に、通常、UserForm で終了ボタンは消さずに、QueryClose イベントで処理するはずです。
その他の回答 (2)
こんにちは。 ×ボタンを消すのではなくQueryCloseイベントで処理する方法です。 フォームを閉じるのはCommandButton1とします。 Private Sub CommandButton1_Click() MsgBox "フォームを閉じます。" Unload Me End Sub Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) If CloseMode = 0 Then MsgBox "×ボタンでは閉じません。" Cancel = True End If End Sub QueryCloseイベントのCloseModeで閉じられる原因(イベントの発生理由)が分かります。×ボタン以外にもいくつかあります。 詳しくはQueryCloseイベントのヘルプを参照してください。
補足
回答ありがとうございます。 バグの無いマクロを作るという観点では、×ボタン押下時の制御でも可ですが、 (システム要件としては、大丈夫ですが) 自分の考える配布の容易性のために、まずは×ボタン隠しを追求してみます。
- avanzato
- ベストアンサー率54% (52/95)
こんにちは。 こういった処理の場合、私もGetActiveWindowではなくFindWindowを使用します。 確かにFormActiveの中なので略間違いなく取得できるかと思いますが安定性を考えると後者を選びます。 また、マクロ実行中に操作をされて困るようでしたらマクロ中フォーム内キーマウス無効化も視野に入れたほうが安定性は高まるかと考えます。
補足
No1の方のアドバイスとあわせて、おぼろげながら見えてきました。 GetActiveWindowだと、何らかの場合にフォームではなく、PowerPointウインドウをアクティブと認識する可能性があるんですね・・。 FindWindowというキーワードで調べてみたところ、具体的にフォーム名を第二引数に指定するので間違いがなさそうな気がしてきました。 第一引数部分が、XPの場合、orその他で同じで行けるのかどうかがわからないので、それをこれから調べてみることにします。 マクロが、 設定項目別に別フォームにしてあり、それらを「次へ」「前へ」ボタンで戻る際に頻繁に、開/閉をしているので、そこらへんが怖いですね・・・。
補足
回答ありがとうございます。 仰る通り、Webサイトを探して、参考にさせて頂いています。 正式には覚えていませんが、 http://www.asahi-net.or.jp/~ef2o-inue/vba_o/sub05_100_050.html このあたりだったと思います。 そのため、自分のコード用に移植まではできていますが、根本的な理解はあまい状態です。 Officeのバージョン情報が漏れていて申し訳ありません。 WinXP Pro、Office XP Proでやってます。 アドバイス頂いたコードの中までは理解できてませんので、まずそのまま組み込み挙動を見て、動作を確認してみたいと思います。 ちなみに×ボタンを非表示にした理由は、同僚に配布した際に「キャンセル」ボタンを押してくれずに「×」ボタンを押すので、キャンセルボタン使用を促すよりも消した方が手っ取り早いと思ったが故です。