none
モードレスなユーザーフォームの QueryClose で ThisWorkbook.Close をすると以降、動作がおかしくなる RRS feed

  • 質問

  • モードレスなユーザーフォームの 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) で動作確認しました。

    2019年7月2日 7:37

回答

  • 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()をどうしても使わなければならない事情があるとすれば、別の方策を模索しなければなりませんが。

    2019年7月2日 14:40
  • 状況がよくわからないので教えてください。

    「まず、そのあとにユーザーフォームを表示しようとすると」と記載されていますが、ユーザーフォームを閉じると同時に、Thisworkbook.Close にてそのユーザーフォームを含むブックが閉じられているので、ユーザーフォームは存在していないように思います。

    よって、表示されないのは当然と思うのですが、私の勘違いでしょうか?

    そもそも、その「表示しようとしているコード」は、どこに記述されているのでしょう?

    • 回答としてマーク infade 2019年7月3日 6:24
    2019年7月2日 12:25
  • 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 Sub

    ErrOpe:
      Resume
      DoEvents
      Return
    End Sub


    • 編集済み KokemomoYamamomo 2019年7月2日 13:44 脱字1字追加
    • 回答としてマーク infade 2019年7月3日 6:24
    2019年7月2日 13:01

すべての返信

  • 状況がよくわからないので教えてください。

    「まず、そのあとにユーザーフォームを表示しようとすると」と記載されていますが、ユーザーフォームを閉じると同時に、Thisworkbook.Close にてそのユーザーフォームを含むブックが閉じられているので、ユーザーフォームは存在していないように思います。

    よって、表示されないのは当然と思うのですが、私の勘違いでしょうか?

    そもそも、その「表示しようとしているコード」は、どこに記述されているのでしょう?

    • 回答としてマーク infade 2019年7月3日 6:24
    2019年7月2日 12:25
  • 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 Sub

    ErrOpe:
      Resume
      DoEvents
      Return
    End Sub


    • 編集済み KokemomoYamamomo 2019年7月2日 13:44 脱字1字追加
    • 回答としてマーク infade 2019年7月3日 6:24
    2019年7月2日 13:01
  • 状況がよくわからないので教えてください。

    「まず、そのあとにユーザーフォームを表示しようとすると」と記載されていますが、ユーザーフォームを閉じると同時に、Thisworkbook.Close にてそのユーザーフォームを含むブックが閉じられているので、ユーザーフォームは存在していないように思います。

    よって、表示されないのは当然と思うのですが、私の勘違いでしょうか?

    そもそも、その「表示しようとしているコード」は、どこに記述されているのでしょう?

    説明不足で分かりにくかったようで、申し訳ありません。
    KokemomoYamamomo さんの解釈でほぼ合ってます。
    あとは KokemomoYamamomo さんの方の返信に書きます。

    2019年7月2日 13:23
  • 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 0

     Exit Sub

    ErrOpe:
      Resume
      DoEvent
      Return

    End Sub

    実行するのが限定されている場合はその対象のブックだけを
    対策すればいいのですが、不特定の別のマクロを実行する可能性も
    あります。
    それらすべてを対策するのは不可能なので、新たに実行する側で
    対策するのではなくできれば閉じるときに対策をしたいのです。

    一応、標準モジュールのプロシージャに処理内容を移して
    Application.OnTime を利用し、時間差で (QueryClose を
    抜けた後に) 実行させるようにすると、正常動作はしますが、
    タイマー実行させるプロシージャを標準モジュール内に
    書かないといけない上に、タイマー用のプロシージャ実行時に
    ブレークポイントで停止しているとエラーになってしまうようで、
    デバッグを正常にできないのはちょっと困ります。
    また、確実に QueryClose を抜けている保証もないので。

        ' 1 秒後に タイマー プロシージャを実行する
        Application.OnTime DateAdd("s", 1, Now), "タイマー"
    
    2019年7月2日 13:56
  • 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()をどうしても使わなければならない事情があるとすれば、別の方策を模索しなければなりませんが。

    2019年7月2日 14:40
  • お返事ありがとうございます。

    UserForm_Terminate で処理すれば大丈夫そうですね。
    仕方ないので適宜使い分ける方向で行きたいと思います。

    UserForm_Terminate はほとんど使わないので失念していました。

    2019年7月3日 6:24