Visual Studio .NET クライアントからの自動化後に Office アプリケーションが終了しない

適用先
Visual Studio 2015

この記事が適用される他の製品をご覧ください。

現象

Microsoft Visual Basic .NET または Microsoft Visual C# .NET から Microsoft Office アプリケーションを自動化する場合、Quit メソッドを呼び出しても Office アプリケーションは終了しません。

原因

Visual Studio .NET によってマネージコードから COM オブジェクトが呼び出されると、ランタイム呼び出し可能ラッパー (RCW) が自動的に作成されます。 RCW は、.NET アプリケーションと COM オブジェクトの間の呼び出しをマーシャリングします。 RCW は、COM オブジェクトの参照カウントを保持します。 したがって、RCW ですべての参照が解放されていない場合、COM オブジェクトは終了しません。

解決策

Office アプリケーションが確実に終了するには、オートメーション コードが次の条件を満たしているかどうかを確認します。

  • 各オブジェクトを新しい変数として宣言します。 たとえば、次のコード行を変更します。

    oBook = oExcel.Workbooks.Add()
    
    

    これを次のように変更します。

    dim oBooks as Excel.Workbooks
    oBooks = oExcel.Workbooks
    oBook = oBooks.Add()
    
    
  • オブジェクトの使用が終了すると 0 が返されるまで、ループ内で System.Runtime.InteropServices.Marshal.ReleaseComObject を使用します。 System.Runtime.InteropServices.Marshal.ReleaseComObject は RCW の参照カウントをデクリメントし、CLR に再入った回数に関係なく、基になる COM コンポーネントがループによって確実に解放されます。

  • 変数への参照を解放するには、変数を Nothing または Null に設定します。

  • Office アプリケーションオブジェクトの Quit メソッドを使用して、サーバーのシャットダウンを通知します。

状態

この動作は仕様です。

追加情報

動作を再現する手順

  1. Visual Studio .NET を起動します。

  2. [ファイル] メニューの [新規作成] をクリックし、[プロジェクト] をクリックします。 [Visual Basic プロジェクト] で [Windows アプリケーション] を選択し、[OK] をクリックします。

    注釈 デフォルトで Form1 が作成されます。

  3. Microsoft Excel オブジェクト ライブラリへの参照を追加します。 手順は次のとおりです。

    1. [プロジェクト] メニューの [参照の追加] をクリックします。

    2. [COM] タブで、Excel 用オブジェクト ライブラリを見つけて、[選択] をクリックします。

      Microsoft Excel 2002 の場合: Microsoft Excel 10.0 オブジェクト ライブラリ

      注釈 この手順をまだ行っていない場合は、Microsoft Office XP Primary Interop Assembly (PIA) をダウンロードしてインストールすることをお勧めします。

      Office XP PIA の詳細については、以下のマイクロソフト サポート技術情報を参照してください。

      328912 Microsoft Office XP プライマリ相互運用機能アセンブリ (PIA) をダウンロードできます
       Microsoft Office Excel 2003 の場合: Microsoft Excel 11.0 オブジェクト ライブラリ

    3. [参照の追加] ダイアログ ボックスで [OK] をクリックして、選択を確定します。

  4. [表示] メニューの [ツールボックス] をクリックし、Button コントロールを Form1 にドラッグします。

  5. Button1 をダブルクリックします。

    注釈 フォームのコード ウィンドウが表示されます。

  6. 次のコードを Form1.vb の先頭に追加します。

    Imports Microsoft.Office.Interop
    
    
  7. コード ウィンドウ内の次のコードを置き換えます。

        Private Sub Button1_Click(ByVal sender As System.Object, _
           ByVal e As System.EventArgs) Handles Button1.Click
        End Sub
    
    

    次のコードを代用します。

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim oApp As New Excel.Application()
        Dim oBook As Excel.Workbook = oApp.Workbooks.Add
        Dim oSheet As Excel.Worksheet = oApp.ActiveSheet
    
        oSheet = Nothing
        oBook.Close(False)
        oBook = Nothing
        oApp.Quit()
        oApp = Nothing
    
        Debug.WriteLine("Sleeping...")
        System.Threading.Thread.Sleep(5000)
        Debug.WriteLine("End Excel")
    End Sub
    
    
  8. F5 キーを押してアプリケーションを実行します。

  9. Windows タスク マネージャーを開きます。 Visual Studio で、[出力] ウィンドウを表示してデバッグ メッセージを表示します。 コマンド ボタンをクリックします。 Excel.exe のインスタンスが [プロセス] の一覧に表示されます。

  10. アプリケーションがスリープ状態を終了した後でも、タスクリストで Excel のインスタンスが実行されます。 ダイアログ ボックスを閉じると、Excel がプロセス リストに表示されなくなったことがわかります。

  11. 「解決方法」セクションの手順を実行すると、Office アプリケーションは最後の変数を解放した後に終了します。 次のコードを使用して、手順 5 の関数を置き換えます。

      Private Sub NAR(ByVal o As Object)
        Try
          While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
          End While
        Catch
        Finally
          o = Nothing
        End Try
      End Sub
    
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim oApp As New Excel.Application()
        Dim oBooks As Excel.Workbooks = oApp.Workbooks
        Dim oBook As Excel.Workbook = oBooks.Add
        Dim oSheet As Excel.Worksheet = oApp.ActiveSheet
    
        NAR(oSheet)
        oBook.Close(False)
        NAR(oBook)
        NAR(oBooks)
        oApp.Quit()
        NAR(oApp)
    
        Debug.WriteLine("Sleeping...")
        System.Threading.Thread.Sleep(5000)
        Debug.WriteLine("End Excel")
    End Sub
    
    

Visual C# .NET を使用している場合は、NAR() 関数のコードを参照してください。


private void NAR(object o)
{
    try 
    {
        while (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0) ;
    }
    catch {}
    finally 
    {
        o = null;
    }
}

注: .NET Framework 2.0 以降では、System.Runtime.InteropServices.Marshal.ReleaseComObject を呼び出すwhileループの代わりに System.Runtime.InteropServices.Marshal.FinalReleaseComObject を使用して同じ結果を得ることができます。
 

トラブルシューティング

注釈 「動作を再現する手順」で説明されている手順を実行してもサーバーがシャットダウンしない場合は、GC を使用できます。Collect() メソッドと GC。最後のオブジェクトを解放した後の WaitForPendingFinalizers() メソッド。 ランタイムは GC である RCW でガベージ コレクションを実行するため、Collect() メソッドは、ガベージ コレクターを強制的に実行し、RCW にまだある参照を解放する場合があります。 GC。collect() メソッドは、使用可能な最大メモリを再利用しようとします。 これは、すべてのメモリが再利用されることを保証するわけではないことに注意してください。

適用対象

この記事は、以下にも適用されます。

  • Microsoft Visual Basic .NET (すべてのエディション)
  • Microsoft Visual C# .NET (すべてのエディション)
  • Microsoft Office 2016 (すべてのエディション)
  • Microsoft Office 2013 (すべてのエディション)