none
[Excel VBA] 新しいインスタンス (New Application) で起動した Excel でウィンドウが非表示のマクロブックを開いてそのマクロブックの中でシート保護をした非表示のブックを開くと落ちる RRS feed

  • 質問

  • 説明が難しいのですが、例えば同じフォルダ内に test.xlsx、test.xlsm、test2.xlsm の
    1 つのブックと 2 つのマクロブックを作ってマクロブックはそれぞれ以下のような
    マクロを書いておきます。

    test.xlsm (Module1)

    Sub test()
        ChDir ThisWorkbook.Path
        With New Application
            .Visible = True
            .Workbooks.Open ThisWorkbook.Path & "\test2.xlsm", , True
        End With
    End Sub

    test2.xlsm (ThisWorkbook)

    Private Sub Workbook_Open()
        ChDir ThisWorkbook.Path
        With Workbooks.Open("test.xlsx", , True)
            .Unprotect
            .Windows(1).Visible = True
        End With
    End Sub

    test2.xlsm はウィンドウを非表示にして保存します。
    test.xlsx はシートの保護を設定してウィンドウの非表示をして保存します。

    test.xlsm の test メソッドを実行すると test2.xlsm の
    .Windows(1).Visible = True のところで Excel が落ちて test.xlsm 側で
    オートメーションエラーが発生してしまうようです。

    なお、test2.xlsm か test.xlsx のどちらかを表示状態にしていたり
    test.xlsx のシート保護を解除している状態では発生しませんでした。

    この件に関する情報などはありませんか。



    • 編集済み infade 2022年10月11日 5:33 タイトルの追記
    2022年10月11日 5:31

回答

  • FYI
    -----------------------
    イベント ID 1000 で記録される Application Error のイベント ログについて
    https://jpdscore.github.io/blog/debugging/application-error-eventlog/
    -----------------------
    • 回答としてマーク infade 2022年10月17日 4:21
    2022年10月17日 1:24
  • 提示された AppCrash ログでは、excel.exe プロセス内のオフセット 0x00000000005cc575 で、STATUS_ACCESS_VIOLATION エラーが発生したことを示しています。
    Excel で追加しているアドインがある場合、それらを全て削除した状態でもクラッシュするのか、検証されることをお勧めします。
    アドインを削除しても改善され無い場合は、さらにクリーン ブート設定を行い検証されることをお勧めします。

    ---------------------------------------------
    Windows でクリーン ブートを実行する方法
    https://support.microsoft.com/ja-jp/help/929135/how-to-perform-a-clean-boot-in-windows
    ---------------------------------------------

    それでも改善しないのであれば先に示した Blog でも説明されている様に、WER (Windows Error Reporting) のレジストリ設定を行い AppCrash 時のクラッシュ ダンプを採取し、それを解析すればよいかと。
    • 回答としてマーク infade 2022年10月17日 6:35
    2022年10月17日 5:52

すべての返信

  • VC++ で COM 呼び出しにより Excel 起動をして同様のことを
    やってみたところ、KernelBase.dll 内でエラー (例外) が
    発生しているみたいです。

    test2.xlsm を表示してから test.xlsx を表示させると落ちず、test2.xlsm を表示してすぐに非表示後に test.xlsx を表示
    落ちます。

    test.xlsx を表示してから test2.xlsm を非表示にしても
    落ちません。

    2022年10月14日 7:02
  • FYI
    -----------------------
    イベント ID 1000 で記録される Application Error のイベント ログについて
    https://jpdscore.github.io/blog/debugging/application-error-eventlog/
    -----------------------
    • 回答としてマーク infade 2022年10月17日 4:21
    2022年10月17日 1:24
  • FYI
    -----------------------
    イベント ID 1000 で記録される Application Error のイベント ログについて
    https://jpdscore.github.io/blog/debugging/application-error-eventlog/
    -----------------------

    すみません。

    該当するログは以下です。

    障害が発生しているアプリケーション名: EXCEL.EXE、バージョン: 16.0.5356.1000、タイム スタンプ: 0x62ce4588
    障害が発生しているモジュール名: EXCEL.EXE、バージョン: 16.0.5356.1000、タイム スタンプ: 0x62ce4588
    例外コード: 0xc0000005
    障害オフセット: 0x00000000005cc575
    障害が発生しているプロセス ID: 0x25ac
    障害が発生しているアプリケーションの開始時刻: 0x01d8e1dfd4655698
    障害が発生しているアプリケーション パス: C:\Program Files\Microsoft Office\Office16\EXCEL.EXE
    障害が発生しているモジュール パス: C:\Program Files\Microsoft Office\Office16\EXCEL.EXE
    レポート ID: a135f1ec-a46f-41cb-9919-ae454949fb74
    障害が発生しているパッケージの完全な名前: 
    障害が発生しているパッケージに関連するアプリケーション ID: 
    障害バケット 1415413911155373052、種類 4
    イベント名: APPCRASH
    応答: 使用不可
    Cab ID: 0
    
    問題の署名:
    P1: EXCEL.EXE
    P2: 16.0.5356.1000
    P3: 62ce4588
    P4: EXCEL.EXE
    P5: 16.0.5356.1000
    P6: 62ce4588
    P7: c0000005
    P8: 00000000005cc575
    P9: 
    P10: 
    
    添付ファイル:
    \\?\C:\xxxx\test2.xlsm
    \\?\C:\xxxx\test.xlsx
    \\?\C:\Users\xxxx\AppData\Local\Temp\{52860324-F978-42A1-AC0E-0363DD5EA1CA} - OProcSessId.dat
    \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER44D5.tmp.WERInternalMetadata.xml
    \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER44F5.tmp.xml
    \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER44F4.tmp.csv
    \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER4534.tmp.txt
    
    これらのファイルは次の場所にある可能性があります:
    \\?\C:\ProgramData\Microsoft\Windows\WER\ReportArchive\AppCrash_EXCEL.EXE_4c9b9a393caa138a10a5efeb6147d7d3810bb39_00000000_a49ef868-3d72-4a89-9680-e8a773e0cd8f
    
    分析記号: 
    解決策を再確認中: 0
    レポート ID: a135f1ec-a46f-41cb-9919-ae454949fb74
    レポートの状態: 268435456
    ハッシュされたバケット: f326fe8ca517f9bb33a48f73f88ff3fc
    Cab GUID: 0


    2022年10月17日 4:36
  • 提示された AppCrash ログでは、excel.exe プロセス内のオフセット 0x00000000005cc575 で、STATUS_ACCESS_VIOLATION エラーが発生したことを示しています。
    Excel で追加しているアドインがある場合、それらを全て削除した状態でもクラッシュするのか、検証されることをお勧めします。
    アドインを削除しても改善され無い場合は、さらにクリーン ブート設定を行い検証されることをお勧めします。

    ---------------------------------------------
    Windows でクリーン ブートを実行する方法
    https://support.microsoft.com/ja-jp/help/929135/how-to-perform-a-clean-boot-in-windows
    ---------------------------------------------

    それでも改善しないのであれば先に示した Blog でも説明されている様に、WER (Windows Error Reporting) のレジストリ設定を行い AppCrash 時のクラッシュ ダンプを採取し、それを解析すればよいかと。
    • 回答としてマーク infade 2022年10月17日 6:35
    2022年10月17日 5:52
  • 提示された AppCrash ログでは、excel.exe プロセス内のオフセット 0x00000000005cc575 で、STATUS_ACCESS_VIOLATION エラーが発生したことを示しています。
    Excel で追加しているアドインがある場合、それらを全て削除した状態でもクラッシュするのか、検証されることをお勧めします。
    アドインを削除しても改善され無い場合は、さらにクリーン ブート設定を行い検証されることをお勧めします。

    ---------------------------------------------
    Windows でクリーン ブートを実行する方法
    https://support.microsoft.com/ja-jp/help/929135/how-to-perform-a-clean-boot-in-windows
    ---------------------------------------------

    それでも改善しないのであれば先に示した Blog でも説明されている様に、WER (Windows Error Reporting) のレジストリ設定を行い AppCrash 時のクラッシュ ダンプを採取し、それを解析すればよいかと。

    以下のようにもっと簡単にウィンドウを非表示にした test1.xlsx と
    シートの保護とウィンドウの非表示をした test2.xlsx を作っても
    同様です。
    (前述のコードではシートの保護解除ではなく、ブックの
    保護解除していました)

    test.xlsm (Module1)

    Sub test()
        ChDir ThisWorkbook.Path
        ' test1.xlsx を作成
        With Workbooks.Add
            ' ウィンドウの非表示
            .Windows(1).Visible = False
            ' 保存
            .SaveAs "test1.xlsx", xlOpenXMLWorkbook
            ' 閉じる
            .Close
        End With
        ' test2.xlsx を作成
        With Workbooks.Add
            ' シートの保護 (保護する項目は 1 つ以上ならなんでもいい模様)
            .ActiveSheet.Protect , True
            ' ウィンドウの非表示
            .Windows(1).Visible = False
            ' 保存
            .SaveAs "test2.xlsx", xlOpenXMLWorkbook
            ' 閉じる
            .Close
        End With
        ' 新しい Excel インスタンスを起動する
        With New Application
            ' 新しいインスタンスのウィンドウを表示する
            .Visible = True
            ' test1.xlsx を読み取り専用で開く
            .Workbooks.Open ThisWorkbook.Path & "\test1.xlsx", , True
            ' test2.xlsx を読み取り専用で開く
            With .Workbooks.Open(ThisWorkbook.Path & "\test2.xlsx", , True)
                ' .ActiveSheet.Unprotect    ' シートの保護解除をすると正常動作する
                ' 以下の部分で落ちる
                .Windows(1).Visible = True  ' .Windows(1).Activate でも同様
            End With
        End With
    End Sub

    • 編集済み infade 2022年10月17日 6:31 少しコードのコメントを修正
    2022年10月17日 6:27
  • 以下のようにもっと簡単にウィンドウを非表示にした test1.xlsx と
    シートの保護とウィンドウの非表示をした test2.xlsx を作っても
    同様です。
    (前述のコードではシートの保護解除ではなく、ブックの
    保護解除していました)

    test.xlsm (Module1)

    Sub test()
        ChDir ThisWorkbook.Path
        ' test1.xlsx を作成
        With Workbooks.Add
            ' ウィンドウの非表示
            .Windows(1).Visible = False
            ' 保存
            .SaveAs "test1.xlsx", xlOpenXMLWorkbook
            ' 閉じる
            .Close
        End With
        ' test2.xlsx を作成
        With Workbooks.Add
            ' シートの保護 (保護する項目は 1 つ以上ならなんでもいい模様)
            .ActiveSheet.Protect , True
            ' ウィンドウの非表示
            .Windows(1).Visible = False
            ' 保存
            .SaveAs "test2.xlsx", xlOpenXMLWorkbook
            ' 閉じる
            .Close
        End With
        ' 新しい Excel インスタンスを起動する
        With New Application
            ' 新しいインスタンスのウィンドウを表示する
            .Visible = True
            ' test1.xlsx を読み取り専用で開く
            .Workbooks.Open ThisWorkbook.Path & "\test1.xlsx", , True
            ' test2.xlsx を読み取り専用で開く
            With .Workbooks.Open(ThisWorkbook.Path & "\test2.xlsx", , True)
                ' .ActiveSheet.Unprotect    ' シートの保護解除をすると正常動作する
                ' 以下の部分で落ちる
                .Windows(1).Visible = True  ' .Windows(1).Activate でも同様
            End With
        End With
    End Sub

    もう少しいろいろ調べてたら Window.Visible = True をする前に以下のように
    ShowWindow で表示してやるとなぜか落ちないよいうです。

            With .Workbooks.Open(ThisWorkbook.Path & "\test2.xlsx", , True)
                ShowWindow .Windows(1).hWnd, SW_SHOW  ' 追加
                .Windows(1).Visible = True
            End With
    


    2022年11月15日 6:33
  • いろいろ試してみたところ、New Application で作ったものだけではなく
    Shell 関数で起動してプロセス ID からウィンドウハンドルを取得、
    ウィンドウハンドルを Excel オブジェクトに変換した場合も同様の症状でした。

    Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
    Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As LongPtr, lpdwProcessId As Long) As Long
    Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr
    Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare PtrSafe Sub AccessibleObjectFromWindow Lib "OLEACC.DLL" (ByVal hWnd As LongPtr, ByVal dwId As Long, riid As GUID, ppvObject As Any)
    
    ' 次のウィンドウ
    Const GW_HWNDNEXT As Long = 2
    
    ' GUID を定義
    Private Type GUID
        lData1 As Long
        iData2 As Integer
        iData3 As Integer
        aBData4(0 To 7) As Byte
    End Type
    
    Private Const OBJID_NATIVEOM = &HFFFFFFF0
    
    ' IDispatch を設定
    Private Sub SetIDispatch(ByRef ID As GUID)
        With ID
            .lData1 = &H20400
            .iData2 = &H0
            .iData3 = &H0
            .aBData4(0) = &HC0
            .aBData4(1) = &H0
            .aBData4(2) = &H0
            .aBData4(3) = &H0
            .aBData4(4) = &H0
            .aBData4(5) = &H0
            .aBData4(6) = &H0
            .aBData4(7) = &H46
        End With
    End Sub
    
    ' ウィンドウハンドルから Excel オブジェクトを取得する
    Function GetXLApp(hWnd As LongPtr) As Application
        Dim hXlWnd As LongPtr
        Dim IDispatch As GUID
        Dim oWnd As Object
    
        ' Excel のウィンドウハンドルから Excel ワークブックのウィンドウを取得し
        ' Excel オブジェクトに変換する
        hXlWnd = FindWindowEx(FindWindowEx(hWnd, 0, "XLDESK", vbNullString), 0, "EXCEL7", vbNullString)
        If hXlWnd <> 0 Then
            SetIDispatch IDispatch
            ' Excel オブジェクトの取得
            AccessibleObjectFromWindow hXlWnd, OBJID_NATIVEOM, IDispatch, oWnd
    
            ' オブジェクトの型が Window の場合は成功
            If TypeName(oWnd) = "Window" Then
                Set GetXLApp = oWnd.Application
            End If
        End If
    End Function
    
    Sub test()
        Dim p As Long
        Dim hWnd As LongPtr
        Dim pID As Double
        Dim cname As String, cpt As String
        Dim xlApp As Application
        Dim d As String
    
        ' 自分のワークブックパスを取得
        d = ThisWorkbook.Path
    
        ' Excel を別のインスタンスで起動してプロセス ID を取得
        pID = Shell("excel.exe /x /r """ & d & "\test1.xlsx""")
    
        ' 約 2 秒待機
        Application.Wait DateAdd("s", 2, Now)
    
        ' 起動中の Excel のウィンドウを取得する (取得されるウィンドウは不定)
        hWnd = FindWindowEx(0, 0, "xlmain", vbNullString)
    
        ' 自分のウィンドウを取得するまでループ
        Do Until ThisWorkbook.Windows(1).hWnd = hWnd
            ' クレス名を取得する変数を初期化
            cname = String(100, vbNullChar)
            ' クラス名を取得
            GetClassName hWnd, cname, 99
    
            ' クラス名の後ろのゴミを除去
            For i = 1 To Len(cname)
                If Asc(Mid(cname, i, 1)) = 0 Then
                    Exit For
                End If
            Next
            cname = LCase$(Left$(cname, i - 1))
    
            ' Excel のウィンドウの場合に処理
            If cname = "xlmain" Then
                ' ウィンドウからプロセス ID を取得
                GetWindowThreadProcessId hWnd, p
    
                ' 起動した Excel のプロセス ID と同じだったらループを抜ける
                If pID = p Then
                    Exit Do
                End If
            End If
    
            ' 次のウィンドウを取得してループ
            hWnd = GetWindow(hWnd, GW_HWNDNEXT)
            DoEvents
        Loop
        
        ' ウィンドウハンドルから Excel オブジェクトを取得
        Set xlApp = GetXLApp(hWnd)
    
        If Not xlApp Is Nothing Then
            ' ファイルをオープンしてウィンドウ表示 (落ちる)
            xlApp.Workbooks.Open(d & "\test2.xlsx", , True).Windows(1).Visible = True
        End If
    End Sub
    

    2022年11月29日 8:21
  • その後もいろいろ調べてみるとこの現象は以下の条件がそろった時に
    再現するようです。
    (なお、以下ではウィンドウを非表示にしているだけのブックを test1.xlsx、
    シートの保護とウィンドウの非表示をしているブックを test2.xlsx と
    仮定します)

    1. New Application や CreateObject("Excel.Application")、
      excel.exe /e などのように初期表示ウィンドウが空の Excel を起動する
    2. 上記の Excel 上にほかのウィンドウが表示されていない状態で
      test1.xlsx と test2.xlsx を開く
    3. test2.xlsx のウィンドウを VBA で表示 (.Visible = True) しようとする

    1. で起動した Excel 上に表示されているウィンドウがひとつもない状態で
    VBA で test2.xlsx のウィンドウを表示しようとすることが原因のようです。

    通常の起動をした場合は再現しないこと、手動でウィンドウ表示したときは
    再現せず VBA から操作したときのみ再現すること、表示されているウィンドウが
    ひとつでもあれば再現しないこと、1. の条件で起動した Excel 上であれば
    ほかのウィンドウを一度表示したとしてもその後に条件さえそろえば再現すること
    などがわかりました。

    test2.xlsx のウィンドウを手動で表示した後に非表示に戻しても再現しないようで
    非表示状態で保存されているブックを開き直すと再現するようになります。

    挙動的に Excel 側の不具合だと思うのですが。


    • 編集済み infade 2022年12月7日 1:55 行間調整してみた
    2022年12月7日 1:54