Процесс Excel остается открытым после взаимодействия;традиционный метод не работает - PullRequest
8 голосов
/ 23 января 2012

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

private void TestExcel()
    {
        Excel.Application excel = new Excel.Application();
        Excel.Workbooks books = excel.Workbooks;
        Excel.Workbook book = books.Open("C:\\test.xlsm");

        book.Close();
        books.Close();
        excel.Quit();

        Marshal.ReleaseComObject(book);
        Marshal.ReleaseComObject(books);
        Marshal.ReleaseComObject(excel);
    }

Даже этот простой фрагмент кода поддерживает выполнение процесса с несколькими файлами (xlsm, xlsx, xls). Прямо сейчас у нас есть обходной путь, чтобы убить процессы Excel, которые мы открыли, но я бы предпочел, чтобы это работало для моего здравого смысла.

Я должен добавить, что он сузился до переменной Workbook. Если я уберу вызов books.Open() и все ссылки на book, он будет успешно завершен.

Ответы [ 4 ]

12 голосов
/ 24 января 2012

Это успешно сработало для меня:

        xlApp.Quit();

        //release all memory - stop EXCEL.exe from hanging around.
        if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); } //release each workbook like this
        if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); } //release each worksheet like this
        if (xlApp != null) { Marshal.ReleaseComObject(xlApp); } //release the Excel application
        xlWorkBook = null; //set each memory reference to null.
        xlWorkSheet = null;
        xlApp = null;
        GC.Collect();
3 голосов
/ 29 января 2016

Этот код работает для меня.

//Declare separate object variables
Excel.Application xlApp = new Excel.Application();
Excel.Workbooks xlWorkbooks = xlApp.Workbooks;
Excel.Workbook xlWorkbook = xlWorkbooks.Add(Missing.Value);
Excel.Worksheet xlWorksheet = (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1);

//Create worksheet

xlWorkbook.Close(false, Missing.Value, Missing.Value);
xlWorkbooks.Close();
xlApp.Quit();

Marshal.FinalReleaseComObject(xlWorksheet);
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(xlWorkbooks);
Marshal.FinalReleaseComObject(xlApp);

xlWorksheet = null;
xlWorkbook = null;
xlWorkbooks = null;
xlApp = null;

GC.Collect();

Эта статья от Microsoft содержит несколько полезных сведений по этой проблеме.

3 голосов
/ 24 января 2012

Я абсолютный любитель COM, использовал его для мелочей в одном проекте довольно давно, но вот фрагмент, который я использовал там.Я, наверное, нашел это где-то в Интернете, не помню.В любом случае, я вставляю его в полную силу;)

public static class ComBlackBox
{
    public static void ReleaseObject(object obj)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
            obj = null;
        }
        catch (ArgumentException ex)
        {
            obj = null;
            MessageBox.Show("Unable to release the Object " + ex.Message);
        }
        finally
        {
            GC.Collect();
        }
    } 
}

Я не могу попробовать это сейчас, но, вероятно, это сработало (я, честно говоря, не помню никаких деталей).Может быть, это поможет вам.Не стесняйтесь указывать на любые очевидные проблемы с этим кодом, я действительно далек от COM-грамотности;)

2 голосов
/ 16 января 2014

Вот как я обошёл эту проблему:

// Store the Excel processes before opening.
Process[] processesBefore = Process.GetProcessesByName("excel");

// Open the file in Excel.
Application excelApplication = new Application();
Workbook excelWorkbook = excelApplication.Workbooks.Open(Filename);

// Get Excel processes after opening the file.
Process[] processesAfter = Process.GetProcessesByName("excel");

// Now find the process id that was created, and store it.
int processID = 0;
foreach (Process process in processesAfter)
{
    if (!processesBefore.Select(p => p.Id).Contains(process.Id))
    {
        processID = process.Id;
    }
}

// Do the Excel stuff

// Now close the file with the COM object.
excelWorkbook.Close();
excelApplication.Workbooks.Close();
excelApplication.Quit();

// And now kill the process.
if (processID != 0)
{
    Process process = Process.GetProcessById(processID);
    process.Kill();
}
...