Взаимодействие C #:процесс Excel не завершается после добавления нового листа в существующий файл [дубликат]

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

  •  20-08-2019
  •  | 
  •  

Вопрос

Возможный Дубликат:
Как правильно очистить объекты взаимодействия Excel на C#

Я прочитал много других тем здесь об управлении ссылками COM при использовании взаимодействия .Net-Excel, чтобы убедиться, что процесс Excel завершается правильно при выходе, и до сих пор методы работали очень хорошо, но недавно я столкнулся с проблемой при добавлении новых листов в существующий файл рабочей книги.

Приведенный ниже код оставляет процесс 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();
}
Это было полезно?

Решение

У меня нет под рукой нужного кода, но я действительно столкнулся с аналогичной проблемой.Если я правильно помню, в итоге я извлек идентификатор процесса экземпляра Excel и уничтожил его (после подходящего периода ожидания и когда другой метод потерпел неудачу).

Я думаю, что я использовал:

GetWindowThreadProcessId (через P / Invoke) в свойстве excel object hwnd, чтобы получить идентификатор процесса, а затем использовать Process.GetProcessById чтобы получить объект процесса.Как только я это сделаю, я бы позвонил Kill о процессе.

Редактировать:Я должен признать, что это не идеальное решение, но если вы не можете найти rogue-интерфейс, который еще не выпущен, то это исправит это в стиле eggshell / sledgehammer.;)

РЕДАКТИРОВАТЬ 2:Тебе не обязательно звонить 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();

Я тестировал это со многими различными вариантами, и у меня еще не было сбоя.

вот мой полный код для уничтожения Excel, созданного вами с помощью библиотеки взаимодействия Office12 .Net:Наслаждайся, -Алан.

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 не запущен.
Может ли версия взаимодействия быть причиной, по которой я задаюсь вопросом?Я тестировал с 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