fehlt C#interop: Excel-Prozess wird nicht beendet, nachdem ein neues Arbeitsblatt zur vorhandenen Datei hinzugefügt wurde



microsoft office 2016: primary interop assemblies redistributable (5)

Mögliche Duplikate:
Wie bereinigen Excel Interop-Objekte in C # ordnungsgemäß

Ich habe viele der anderen Threads hier über das Verwalten von COM-Referenzen gelesen, während Sie die .Net-Excel-Interoperabilität verwenden, um sicherzustellen, dass der Excel-Prozess beim Beenden korrekt ausgeführt wird. Bis jetzt funktionierten die Techniken sehr gut, aber ich stieß kürzlich auf eine Problem beim Hinzufügen neuer Arbeitsblätter zu einer vorhandenen Arbeitsmappendatei.

Der folgende Code hinterlässt einen Zombie-Excel-Prozess.

Wenn ich einer neu erstellten Arbeitsmappendatei ein Arbeitsblatt hinzufüge, wird es ordnungsgemäß beendet. Wenn ich den Code ohne die .Add() Zeile ausführe, wird die .Add() . (Die vorhandene Datei, aus der ich lese, ist eine leere Datei, die durch den auskommentierten Code erstellt wurde.)

Irgendwelche Ideen?

//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();
}

https://ffff65535.com


Nicht sehr konstruktiv Ich weiß, aber ich testete den Code genau wie oben gezeigt und mein Excel-Prozess beendet wie erwartet, meine C: \ addtest.xls sitzt mit 8 neuen Blättern und kein Excel-Prozess läuft.
Könnte die Interop-Version die Ursache sein, die ich mich frage? Ich habe mit 11 & 12 getestet.


Ich habe den Code nicht zur Hand, aber ich stieß auf ein ähnliches Problem. Wenn ich mich richtig erinnere, landete ich schließlich die Prozess-ID der Excel-Instanz und tötete sie (nach einer geeigneten Wartezeit und wenn die andere Methode fehlschlug).

Ich glaube, ich habe benutzt:

GetWindowThreadProcessId (über P / Invoke) für die Eigenschaft hwnd des Excel-Objekts, um die Prozess-ID Process.GetProcessById , und anschließend Process.GetProcessById , um ein Prozessobjekt Process.GetProcessById . Sobald ich das getan hatte, rief ich Kill an.

EDIT: Ich muss zugeben, das ist nicht die ideale Lösung, aber wenn Sie nicht die Rogue-Schnittstelle finden, die nicht veröffentlicht wird, dann wird dies in echter Eierschale / Vorschlaghammer Mode zu beheben. ;)

EDIT2: Sie müssen Kill nicht sofort auf dem Prozessobjekt aufrufen ... Sie könnten zuerst versuchen, Close aufzurufen, bevor Sie zu Kill .


Ich verwende VB.NET 3.5 SP1 und der folgende Code STILL lässt EXCEL.EXE geöffnet:

        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)

Andrew, hier ist der Code, den ich gefunden habe, der funktioniert. Ich dachte, ich poste es hier für andere, die rüberkommen:

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;
    }
}
}

Dies funktioniert sehr gut für mich, ohne Ausnahmen.

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




pia