.NETでエクセルを制御しようとして沼った話

はじめに

結論から言いますと死ぬほど情けないミスです
設定とかレジストリを弄るとかそんなのはございません

沼るまでの経緯

とある社内ソフトを制作していた
その社内ソフトはエクセルを制御しないといけないので、COM(Microsoft Office Excel 16.0 Object Library)を使っていたクラスを作成し、そのクラスを用いてソフトを制作していた
ただ、解放云々とかが複雑なのに継ぎ接ぎの雑なクラスで運用していたが為にクラスの解放部分がグロイことになっており、

検証時にエラーを吐いて終了⇒クラスの解放が例外処理で行っていなかった⇒以降「別のプロセスが使用しています」でエラー落ち

と言う事態が発生したのに思い切ってクラスを全面改修することにした

と言う事で、IDisposableとUsingでしっかりと解放できる仕組みを整えるようにして、そのソフトもそのように修正した

修正したソフトは少数のテストデータで検証し特に目立った問題も無かったのでリリースした

沼った

リリース翌日に稼働しているか見に行ったが動いていないので実際に使用しているデータを使用してデバッグすることにした
すると、

Main.vb

'色んな処理があるがこの記事と関係ない部分なので大分端折ってます
    Private Sub DataAdd(KanriExcel As ExcelClass)
        For Each Kantoku In KantokuList
            For r = 2 To UBound(KanriDatas, 1)
                'ここでエラー
                KanriExcel.DataWrite("OK", r, 1)
            Next
        Next
    End Sub

ExcelClass.vb

    Private disposedValue As Boolean

    Private Property ExcelApp As Application
    Private Property ExcelBook As Workbook
    Private Property Savepath As String
    Private Property NewBookThen As Boolean
    Private Property ExcelSheets As List(Of Worksheet)

'中略

    Friend Sub DataWrite(WriteData As String, Row As Integer, Col As Integer, Optional SheetIndex As Integer = 0)
        'ここでエラー
        CType(ExcelSheets(SheetIndex).Cells(Row, Col), Range).Value = WriteData
    End Sub

でエラーを吐いており内容が、

リモート プロシージャ コールに失敗しました。 (HRESULT からの例外:0x800706BE)
System.Runtime.InteropServices.COMException (0x800706BE): リモート プロシージャ コールに失敗しました。 (HRESULT からの例外:0x800706BE)

RPC サーバーを利用できません。 (HRESULT からの例外:0x800706BA)
System.Runtime.InteropServices.COMException (0x800706BA): RPC サーバーを利用できません。 (HRESULT からの例外:0x800706BA)

の二つがコロコロ変わって出ている上、とある「Kantoku」の処理時に暫く経ってから落ちていたので「???」状態でした

まぁ「???」で終わる訳にもいかないので、

・セルの書き方を変更
・配列に一回書き込みデータ全て入れて、Rangeで一括書き込み

とかやってみましたが不発に終わりました・・・

あっけない解決

解放時にエラーが起きていたので「ん?」と思い見てみると、なんか2回解放処理がされています
おかしいなと思いタスクマネージャーを確認すると、1個しかエクセルファイルを起動していない筈なのに何故か2個もプロセスに存在しています
「まさか」と思いデータ読み込みのロジックを見てみると・・・

Main.vb

    Sub Main()
        Using KanriExcel As New ExcelClass(ExcelPath)
            DataRead(KanriExcel)
            DataAdd(KanriExcel)
        End Using
    End Sub

    Private Sub DataRead(KanriExcel As ExcelClass)
        KanriExcel = New ExcelClass(ExcelPath)
    End Sub

    Private Sub DataAdd(KanriExcel As ExcelClass)
        For Each Kantoku In KantokuList
            For r = 2 To UBound(KanriDatas, 1)
                KanriExcel.DataWrite("OK", r, 1)
            Next
        Next
    End Sub

                  _,.. -‐―‐-- .. __
                /´   .      `ヽ 、
               / 、、: ヘ :::::::.....      \
              ./ ‐-_ヽ!:...__>;::;;;;;;;::::::::...._   \
             /   /  \!´   \::::::::! ̄ ヽ、 ヽ,
             `‐rt-'....,,,___」     \::!    .ソ_,ノ
               !:.\::::..  ヽ      >、:.、../_,、!、
     , --v‐- 、 _   ヽ::.ヽ::::..  `、    /  ニ,:',.':::::::(´
   , - ';;/::ノ.:::// l ̄`―`-:;;\::..  ヽ   /   /jヽ:::::::::::)-‐'チ、
 //.:::;ィ'":;∠;_/.:::!::::::.............  トr-;;_ `、 / ,.-=';`-- -‐ii'   ミ!ユ、
..:ヽ-'⌒/::;;-‐'´ └‐┴‐-----=;;:.ヽ;:::::..:;ノ::.::.ヽ;:::::::....... .. .oo!!_,,..-"-┘.....
     `┘             ´ ̄       ̄  ̄  ̄ ´

馬鹿じゃねぇのこいつ
COMの仕様とかWindowsの設定ミスとかを疑ってた状態でのこのポカミスの発覚は激しく落胆しました

予想通り、

KanriExcel = New ExcelClass(ExcelPath)

を削除したら普通に動作しました クソが

どういう状態だったか

結局、インスタンス生成と解放の仕様を変更したのに新旧の生成が混在している状態でした
詰まる所、

クラスは1個だが2回同じエクセルを起動している

と言う悍ましい状態でした

こんな状態なのに特定の条件だと動作するらしく、少数のテストデータだとすり抜けちゃうんですね・・・

多重起動なら、

ExcelApp = New Application

の時点でエラー吐きそうなもんですが何で動いたんだろ

結局その所為で、「2重生成は無いだろう」と思い込んでしまう悪い癖が出て、泥沼に嵌り無駄に時間を浪費する羽目になりました

まとめ

・Applicationの二重開放は中途半端なとこまで粘った後COMExceptionエラーを吐くので原因特定は(多分)難しい
・横着せずにちゃんとコードは全体を確認しよう