投稿者 魔界の仮面弁士  (社会人) 投稿日時 2024/4/9 21:36:29
> Excelを手動で起動しておくと、エラーにならず処理が進みました。
ひとまず起動済みであれば通るということは、状況的に、
ROT (Running Object Table) への登録遅延が発生しているのかな…。

こんな記事が見つかりました。
https://learn.microsoft.com/en-us/archive/blogs/adioltean/when-cocreateinstance-returns-0x80080005-co_e_server_exec_failure?WT.mc_id=DT-MVP-8907

> Most of the time, this error says that the COM server process failed to start in time and register the proper class factory for this specific COM class. 
『ほとんどの場合、このエラーは、COM サーバー プロセスの開始が間に合わず、
この特定の COM クラスに適切なクラス ファクトリを登録できなかったことを示しています。』

> We will see in a second what can cause this, but the idea is that under heavy CPU load the machine can be pretty slow, and some out-of-process COM servers will fail to initialize properly. That said, there is a limited number of cases when this error is caused by bad configuration or even a bug in the server code.
『この原因については後ほど説明しますが、CPU 負荷が高いとマシンの動作が非常に遅くなり、一部のアウトプロセス COM サーバーが適切に初期化されなくなる可能性があるということです。ただし、このエラーが不適切な構成やサーバー コードのバグによって発生するケースは限られています。』


> Excelが(手動で)起動されているかをプログラムから知ることができないか?
起動済み判定された直後に、終了されてしまうというパターンも無いとは言えないですが、
ひとまず起動の有無だけなら、Process クラスで得られます。
この場合、非表示で起動されている Excel.exe も含まれます。

Dim p = Process.GetProcessesByName("Excel")


ただ、起動シーケンス中であったり、その逆に終了処理の最中という可能性もあるでしょうから、
起動していたからといっても、それが COM として使える状態になっているかは別問題ですけれどね。


> できれば、Excelが(手動で)起動されているかをプログラムから知ることができないか?
> を課題と考えます。
COM として取得することを目的とする場合は、GetObject を使うという手もあります。
この場合、取得できなければ例外として取得されます。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim xls As Object = Nothing
    Try
        xls = GetObject(, "Excel.Application")
    Catch 'ex As Exception 
        '起動済みでない場合、例外となる 
    End Try
    If xls Is Nothing Then
        MessageBox.Show(Me"Excel は起動されていません。""起動判定", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Return
    End If

    Dim xlApp = DirectCast(xls, Excel.Application)
    xlApp.Visible = True
    
    '中略 
    
    If Marshal.IsComObject(xlApp) Then
        Marshal.ReleaseComObject(xlApp)
        xlApp = Nothing
    End If
End Sub


また、GetObject(, "Excel.Application") の代わりに
GetObject(excelDocumentFilePath) とする方法もあります。

ファイルパスを指定した場合は、Excel が起動していなかった場合、自動的に Excel を起動してくれます。
(New Excel.Application が失敗する可能性がある環境なので、常時成功するのかどうかは未知数ですが)

ただしパスを指定する方式の場合、得られるインスタンスは Excel.Application 型ではなく
Excel.Workbook 型になる点に注意が必要です。なお、その Workbook インスタンスの
Application プロパティをたどれば、Excel.Application のインスタンスを得ることができます。


一方、GetObject(, "Excel.Application") を使う方法だと、Excel.Application のインスタンスが
複数存在していた場合、どれを拾うことになるのか確証が持てなくなるという欠点があります。
(CreateObject や New で起動した場合は、自身が起動したものであることが明確なのですが)

もし、起動済みの複数のインスタンスを列挙したいなら、GetRunningObjectTable API を使って取得する方法もあり。
https://hanatyan.sakura.ne.jp/vbnetbbs/wforum.cgi?mode=allread&no=10322