8

重複の可能性:
C# で Excel 相互運用オブジェクトを適切にクリーンアップする方法

終了時に Excel プロセスが正しく終了することを確認するために .Net-Excel 相互運用機能を使用しながら COM 参照を管理することについて、ここで他の多くのスレッドを読みました。新しいワークシートを既存のワークブック ファイルに追加するときの問題。

以下のコードは、ゾンビの Excel プロセスを残します。

新しく作成したワークブック ファイルにワークシートを追加すると、正常に終了します。行を除いてコードを実行すると、正常に.Add()終了します。(私が読んでいる既存のファイルは、コメントアウトされたコードによって作成された空のファイルです)

何か案は?

//using Excel = Microsoft.Office.Interop.Excel;
//using System.Runtime.InteropServices;
public static void AddTest()
{
  string filename = @"C:\addtest.xls";
  object m = Type.Missing;
  Excel.Application excelapp = new Excel.Application();
  if (excelapp == null) throw new Exception("Can't start Excel");
  Excel.Workbooks wbs = excelapp.Workbooks;

  //if I create a new file and then add a worksheet,
  //it will exit normally (i.e. if you uncomment the next two lines
  //and comment out the .Open() line below):
  //Excel.Workbook wb = wbs.Add(Excel.XlWBATemplate.xlWBATWorksheet);
  //wb.SaveAs(filename, m, m, m, m, m, 
  //          Excel.XlSaveAsAccessMode.xlExclusive,
  //          m, m, m, m, m);

  //but if I open an existing file and add a worksheet,
  //it won't exit (leaves zombie excel processes)
  Excel.Workbook wb = wbs.Open(filename,
                               m, m, m, m, m, m,
                               Excel.XlPlatform.xlWindows,
                               m, m, m, m, m, m, m);

  Excel.Sheets sheets = wb.Worksheets;

  //This is the offending line:
  Excel.Worksheet wsnew = sheets.Add(m, m, m, m) as Excel.Worksheet; 

  //N.B. it doesn't help if I try specifying the parameters in Add() above

  wb.Save();
  wb.Close(m, m, m);

  //overkill to do GC so many times, but shows that doesn't fix it
  GC();
  //cleanup COM references
  //changing these all to FinalReleaseComObject doesn't help either
  while (Marshal.ReleaseComObject(wsnew) > 0) { } 
  wsnew = null;
  while (Marshal.ReleaseComObject(sheets) > 0) { }
  sheets = null;
  while (Marshal.ReleaseComObject(wb) > 0) { }
  wb = null;
  while (Marshal.ReleaseComObject(wbs) > 0) { }
  wbs = null;
  GC();
  excelapp.Quit();
  while (Marshal.ReleaseComObject(excelapp) > 0) { }
  excelapp = null;
  GC();
}

public static void GC()
{
  System.GC.Collect();
  System.GC.WaitForPendingFinalizers();
  System.GC.Collect();
  System.GC.WaitForPendingFinalizers();
}
4

7 に答える 7

13

私も同様のことをしました。Excel ファイルを作成するか、既存のファイルを開きます。すべてのシートを削除して、独自のシートを追加します。すべての参照が閉じていることを確認するために使用するコードは次のとおりです。

            workbook.Close(true, null, null);
            excelApp.Quit();

            if (newSheet != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(newSheet);
            }
            if (rangeSelection != null)
            {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(rangeSelection);
            }
            if (sheets != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
            }
            if (workbook != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
            }
            if (excelApp != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
            }

            newSheet = null;
            rangeSelection = null;
            sheets = null;
            workbook = null;
            excelApp = null;

            GC.Collect();

これをさまざまなオプションでテストしましたが、まだ失敗していません。

于 2008-12-10T15:56:51.410 に答える
4

渡すコードはありませんが、同様の問題が発生しました。正しく思い出せば、ExcelインスタンスのプロセスIDを取得し、それを強制終了することになりました(適切な待機期間の後、他のメソッドが失敗したとき)。

私が使用したと思います:

GetWindowThreadProcessId(P / Invokeを介して)ExcelオブジェクトのhwndプロパティでプロセスIDProcess.GetProcessByIdを取得し、プロセスオブジェクトを取得するために使用します。それが終わったらKill、プロセスを呼び出します。

編集:私は認めなければなりません、これは理想的な解決策ではありませんが、リリースされていない不正なインターフェースが見つからない場合、これは真の卵殻/ハンマーの方法でそれを修正します。;)

KillEDIT2:プロセスオブジェクトをすぐに呼び出す必要はありません...Closeに頼る前に、まず呼び出しを試みることができKillます。

于 2008-12-08T19:14:31.480 に答える
4

Office12 .Net 相互運用ライブラリで作成した Excel を強制終了するための完全なコードを次に示します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Office.Interop.Excel;

class Program
{

    /// <summary> 
    /// Win32 API import for getting the process Id. 
    /// The out param is the param we are after. I have no idea what the return value is. 
    /// </summary> 
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId); 

    static void Main(string[] args)
    {
        var app = new Application();
        IntPtr hwnd = new IntPtr(app.Hwnd);
        IntPtr processId;
        IntPtr foo = GetWindowThreadProcessId(hwnd, out processId);
        Process proc = Process.GetProcessById(processId.ToInt32());
        proc.Kill(); // set breakpoint here and watch the Windows Task Manager kill this exact EXCEL.EXE
        app.Quit(); // should give you a "Sorry, I can't find this Excel session since you killed it" Exception.
    }
}
于 2010-02-16T15:27:50.273 に答える
1

これは、例外なく、私にとっては非常にうまく機能します。

Public Class ExcelHlpr

    Declare Function EndTask Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal ShutDown As Boolean, ByVal Force As Boolean) As Integer

    Dim cXlApp As Microsoft.Office.Interop.Excel.Application

    Public Function GetExcel() As Microsoft.Office.Interop.Excel.Application
        cXlApp = New Microsoft.Office.Interop.Excel.Application
        Return cXlApp
    End Function

    Public Function EndExcel() As Integer
        Dim xlHwnd As New IntPtr(cXlApp.Hwnd)
        Return EndTask(xlHwnd, False, True)
    End Function

End Class
于 2011-08-09T21:02:49.927 に答える
0

あまり建設的ではありませんが、上記のようにコードを正確にテストし、Excelプロセスが期待どおりに終了し、C:\ addtest.xlsに8枚の新しいシートが表示され、Excelプロセスが実行されていません。
相互運用バージョンが原因でしょうか?11と12でテストしました。

于 2008-12-08T20:22:00.440 に答える
0

アンドリュー、これが私が見つけたコードです。私は出くわした他の人のためにここに投稿することを考えました:

namespace WindowHandler
{
using System;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;

/// <summary>
/// Window class for handling window stuff.
/// This is really a hack and taken from Code Project and mutilated to this small thing.
/// </summary>
public class Window
{
    /// <summary>
    /// Win32 API import for getting the process Id.
    /// The out param is the param we are after. I have no idea what the return value is.
    /// </summary>
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId);

    /// <summary>
    /// Gets a Window's process Id.
    /// </summary>
    /// <param name="hWnd">Handle Id.</param>
    /// <returns>ID of the process.</returns>
    public static IntPtr GetWindowThreadProcessId(IntPtr hWnd)
    {
        IntPtr processId;
        IntPtr returnResult = GetWindowThreadProcessId(hWnd, out processId);

        return processId;
    }
}
}
于 2009-03-16T14:44:49.727 に答える
0

私はVB.NET 3.5 SP1を使用していますが、次のコードはまだEXCEL.EXEを開いたままにしています:

        xlWorkbook.Close(SaveChanges:=False)
        xlApplication.Quit()

        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorksheet)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkbook)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)

        xlRange = Nothing
        xlWorksheet = Nothing
        xlSheets = Nothing
        xlWorkbook = Nothing
        xlApplication = Nothing

        GC.GetTotalMemory(False)
        GC.Collect()
        GC.WaitForPendingFinalizers()

        GC.Collect()
        GC.WaitForPendingFinalizers()
        GC.Collect()
        GC.GetTotalMemory(True)
于 2009-02-19T15:04:04.383 に答える