C# interop : 기존 파일에 새 워크 시트를 추가 한 후 Excel 프로세스가 종료되지 않음 [Duplication

StackOverflow https://stackoverflow.com/questions/350454

  •  20-08-2019
  •  | 
  •  

문제

가능한 복제 :
C#에서 Excel Interop 객체를 올바르게 정리하는 방법

.NET-Excel Interop을 사용하여 Excel 프로세스가 종료시 올바르게 종료되도록하는 동안 COM 참조 관리에 대한 다른 스레드를 많이 읽었으며 지금까지 기술이 매우 잘 작동했지만 최근에 처음 접했습니다. 문제 기존 통합 문서 파일에 새 워크 시트를 추가 할 때 문제.

아래 코드는 좀비 엑셀 프로세스를 남깁니다.

새로 생성 된 통합 문서 파일에 워크 시트를 추가하면 잘 나옵니다. 내가 제외한 코드를 실행하면 .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();
}
도움이 되었습니까?

해결책

나는 손을 잡을 코드가 없지만 비슷한 문제가 발생했습니다. 내가 올바르게 기억한다면, 나는 Excel 인스턴스의 프로세스 ID를 검색하고 그것을 죽였다 (적절한 대기 기간 후, 다른 방법이 실패한시기).

나는 내가 사용했다고 생각한다 :

GetWindowThreadProcessId (p/incoke) Excel Object HWND 속성에서 프로세스 ID를 얻은 다음 사용 Process.GetProcessById 프로세스 개체를 얻으려면. 일단 그렇게하면 전화 할 것입니다 Kill 과정에서.

편집 : 인정해야합니다. 이것은 이상적인 솔루션이 아니지만 출시되지 않은 Rogue 인터페이스를 찾을 수 없다면 True Eggshell/Sledgehammer Fashion에서 수정됩니다. ;)

edit2 : 전화 할 필요가 없습니다 Kill 프로세스 객체에서 즉시 ... 먼저 호출을 시도 할 수 있습니다. Close 의지하기 전에 Kill.

다른 팁

나는 비슷한 일을했다. 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();

나는 이것을 다양한 옵션으로 테스트했으며 아직 실패하지 않았다.

다음은 Office12 .NET Interop 라이브러리로 만든 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.
    }
}

이것은 예외없이 나에게 매우 잘 작동합니다.

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

내가 알고있는 건설적이지는 않지만 위에 표시된대로 정확히 코드를 테스트했으며 예상대로 Excel 프로세스가 종료되었습니다. C : addTest.xls는 8 개의 새로운 시트와 함께 앉아 있으며 Excel 프로세스가 실행되지 않습니다.
Interop 버전이 내가 궁금해 할 수 있습니까? 나는 11 & 12로 테스트했다.

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)

앤드류, 여기에 내가 찾은 코드가 있습니다. 나는 여기에 게시 한 다른 사람들을 위해 여기에 게시했다고 생각했습니다.

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;
    }
}
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top