トップ回答者
モードレスなユーザーフォームの QueryClose で ThisWorkbook.Close をすると以降、動作がおかしくなる

質問
-
モードレスなユーザーフォームの QueryClose で以下のような感じで
ThisWorkbook.Close をすると閉じた後に、動作がいろいろと
おかしくなるようです。Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) ' 保存フラグを True にしておく ThisWorkbook.Saved = True ' 閉じる ThisWorkbook.Close End Sub
まず、そのあとにユーザーフォームを表示しようとすると
実行時エラー '429':
ActiveX コンポーネントはオブジェクトを作成できません。
とエラーが出ます。
そのあと、実行を続行するとそのまま動作します。また、閉じた後に UserForms.Count を調べようとすると
実行時エラー '440':
オートメーション エラーです。
が発生します。
同様に、続行するとそのあとは正常動作します。あと、Stop を書いているとそこでコードの実行が終了してしまうようです。
これも再実行すればそのあとは正常動作します。Word でも同様の実験 (ThisWorkbook ではなく ThisDocument に修正) を
してみたところ、同じ動作をしました。
PowerPoint では相当する ThisPresentation みたいなプロパティがないようなので
ActivePresentation で代用しましたが同じ動作をしました。
そのため、VBA というよりは MSForms 側の問題のようにも思いますが、なにか
対策はないでしょうか。Excel 2010 (x86) / 2016 (x64) で動作確認しました。
回答
-
infadeさん、こんばんは。
推測が当たっていて、無駄にならず、良かったです。
それでは、QueryClose()ではなくて、UserForm_Terminate()を使って、
Private Sub UserForm_Terminate()
Call closeOpe
End Sub
とし、
Sub closeOpe()
'DoEvents ';無くても問題ない。
ThisWorkbook.Saved=True
ThisWorkbook.Close
End Sub
としてみてはいかがでしょうか。(DoEventsは無くても問題ないようです。)と、ここまで書いて、待てよ、と気がつきました。こんなまだるっこしい事をしなくても、直接UserForm_Terminate()にブックを閉じる処理を書いても問題ないようです。すなわち…
Private Sub UserForm_Terminate()
ThisWorkbook.Saved=True
ThisWorkbook.Close
End Sub
以上、ご確認ください。UserForm_QueryClose()をどうしても使わなければならない事情があるとすれば、別の方策を模索しなければなりませんが。
- 編集済み KokemomoYamamomo 2019年7月2日 15:25
- 回答としてマーク infade 2019年7月3日 6:24
-
inafadeさん、こんばんは。
凡人の私には想像もできない処理なので分からない点がいくつかありますが…
1.「まず、そのあとにユーザーフォームを表示しようとすると」とありますが、これは、「ThisWorkbookをCloseした後で再度ブックを立ち上げてマクロを走らせてユーザーフォームを表示しようとすると」という意味と理解しましたが、よろしいでしょうか。
2.「また、閉じた後に UserForms.Count を調べようとすると」とは、「ThisWorkbookをCloseした後に再度/別の?ブックを開いてvbaマクロを実行してUserForms.Countを調べようとすると」と理解しましたが、よろしいでしょうか。
3.一応、私の方では、1.及び2.の理解で再現確認できました。このようなエラーが出るのは、ThisWorkbookは閉じられても、エクセルが立ち上がったままであるからでしょう。Application.QuitをCloseの前に入れてエクセルを閉じるようにすれば問題ありません。しかし、エクセルを閉じるわけにはいかないのでしょう。
4.解決策として正当なものなのかどうかは疑問ですが、次のようにエラー処理をすると、エラーは出なくなるようです。UserForms.Countについても同様です。Windows10+Excel2016で確認。再度のTestMain()の立ち上がりが異常に遅いのが気にかかりますが…。
Sub TestMain()
on error goto ErrOpe
UserForm1.show vbModeless
on error goto 0
Exit SubErrOpe:
Resume
DoEvents
Return
End Sub- 編集済み KokemomoYamamomo 2019年7月2日 13:44 脱字1字追加
- 回答としてマーク infade 2019年7月3日 6:24
すべての返信
-
inafadeさん、こんばんは。
凡人の私には想像もできない処理なので分からない点がいくつかありますが…
1.「まず、そのあとにユーザーフォームを表示しようとすると」とありますが、これは、「ThisWorkbookをCloseした後で再度ブックを立ち上げてマクロを走らせてユーザーフォームを表示しようとすると」という意味と理解しましたが、よろしいでしょうか。
2.「また、閉じた後に UserForms.Count を調べようとすると」とは、「ThisWorkbookをCloseした後に再度/別の?ブックを開いてvbaマクロを実行してUserForms.Countを調べようとすると」と理解しましたが、よろしいでしょうか。
3.一応、私の方では、1.及び2.の理解で再現確認できました。このようなエラーが出るのは、ThisWorkbookは閉じられても、エクセルが立ち上がったままであるからでしょう。Application.QuitをCloseの前に入れてエクセルを閉じるようにすれば問題ありません。しかし、エクセルを閉じるわけにはいかないのでしょう。
4.解決策として正当なものなのかどうかは疑問ですが、次のようにエラー処理をすると、エラーは出なくなるようです。UserForms.Countについても同様です。Windows10+Excel2016で確認。再度のTestMain()の立ち上がりが異常に遅いのが気にかかりますが…。
Sub TestMain()
on error goto ErrOpe
UserForm1.show vbModeless
on error goto 0
Exit SubErrOpe:
Resume
DoEvents
Return
End Sub- 編集済み KokemomoYamamomo 2019年7月2日 13:44 脱字1字追加
- 回答としてマーク infade 2019年7月3日 6:24
-
状況がよくわからないので教えてください。
「まず、そのあとにユーザーフォームを表示しようとすると」と記載されていますが、ユーザーフォームを閉じると同時に、Thisworkbook.Close にてそのユーザーフォームを含むブックが閉じられているので、ユーザーフォームは存在していないように思います。
よって、表示されないのは当然と思うのですが、私の勘違いでしょうか?
そもそも、その「表示しようとしているコード」は、どこに記述されているのでしょう?
説明不足で分かりにくかったようで、申し訳ありません。
KokemomoYamamomo さんの解釈でほぼ合ってます。
あとは KokemomoYamamomo さんの方の返信に書きます。 -
inafadeさん、こんばんは。
凡人の私には想像もできない処理なので分からない点がいくつかありますが…
1.「まず、そのあとにユーザーフォームを表示しようとすると」とありますが、これは、「ThisWorkbookをCloseした後で再度ブックを立ち上げてマクロを走らせてユーザーフォームを表示しようとすると」という意味と理解しましたが、よろしいでしょうか。
2.「また、閉じた後に UserForms.Count を調べようとすると」とは、「ThisWorkbookをCloseした後に再度/別の?ブックを開いてvbaマクロを実行してUserForms.Countを調べようとすると」と理解しましたが、よろしいでしょうか。
説明不足の中、理解して頂きありがとうございます。
ほぼ、それで合ってます。
閉じたマクロブックを再度、開き直しでも別のマクロブックでも、
新規で作っても同様です。
また、UserForms.Count のみイミディエイトウィンドウでも
問題を確認できます。
(ただし、UserForms が VBA モジュールのオブジェクトなので
試すときは 1 つ以上の Workbook が必要)3.一応、私の方では、1.及び2.の理解で再現確認できました。このようなエラーが出るのは、ThisWorkbookは閉じられても、エクセルが立ち上がったままであるからでしょう。Application.QuitをCloseの前に入れてエクセルを閉じるようにすれば問題ありません。しかし、エクセルを閉じるわけにはいかないのでしょう。
他のブックを開いている場合に、Excel 自体を終了させてしまうわけに
いかないので。4.解決策として正当なものなのかどうかは疑問ですが、次のようにエラー処理をすると、エラーは出なくなるようです。UserForms.Countについても同様です。Windows10+Excel2016で確認。
Sub TestMain()
on error goto ErrOpe
UserForm1.show vbModeless
on error goto 0Exit Sub
ErrOpe:
Resume
DoEvent
ReturnEnd Sub
実行するのが限定されている場合はその対象のブックだけを
対策すればいいのですが、不特定の別のマクロを実行する可能性も
あります。
それらすべてを対策するのは不可能なので、新たに実行する側で
対策するのではなくできれば閉じるときに対策をしたいのです。一応、標準モジュールのプロシージャに処理内容を移して
Application.OnTime を利用し、時間差で (QueryClose を
抜けた後に) 実行させるようにすると、正常動作はしますが、
タイマー実行させるプロシージャを標準モジュール内に
書かないといけない上に、タイマー用のプロシージャ実行時に
ブレークポイントで停止しているとエラーになってしまうようで、
デバッグを正常にできないのはちょっと困ります。
また、確実に QueryClose を抜けている保証もないので。' 1 秒後に タイマー プロシージャを実行する Application.OnTime DateAdd("s", 1, Now), "タイマー"
-
infadeさん、こんばんは。
推測が当たっていて、無駄にならず、良かったです。
それでは、QueryClose()ではなくて、UserForm_Terminate()を使って、
Private Sub UserForm_Terminate()
Call closeOpe
End Sub
とし、
Sub closeOpe()
'DoEvents ';無くても問題ない。
ThisWorkbook.Saved=True
ThisWorkbook.Close
End Sub
としてみてはいかがでしょうか。(DoEventsは無くても問題ないようです。)と、ここまで書いて、待てよ、と気がつきました。こんなまだるっこしい事をしなくても、直接UserForm_Terminate()にブックを閉じる処理を書いても問題ないようです。すなわち…
Private Sub UserForm_Terminate()
ThisWorkbook.Saved=True
ThisWorkbook.Close
End Sub
以上、ご確認ください。UserForm_QueryClose()をどうしても使わなければならない事情があるとすれば、別の方策を模索しなければなりませんが。
- 編集済み KokemomoYamamomo 2019年7月2日 15:25
- 回答としてマーク infade 2019年7月3日 6:24