автоматизация c # и excel - завершение работающего экземпляра - PullRequest
13 голосов
/ 25 июня 2009

Я пытаюсь автоматизировать Excel через C #. Я следовал всем инструкциям Microsoft о том, как это сделать, но я все еще изо всех сил пытаюсь отбросить окончательную ссылку (и) в Excel, чтобы она закрылась, и чтобы GC мог ее собрать.

Ниже приведен пример кода. Когда я закомментирую блок кода, который содержит строки, похожие на:

Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();

затем файл сохраняется и Excel выходит. В противном случае файл сохраняется, но Excel остается запущенным как процесс. В следующий раз, когда этот код запускается, он создает новый экземпляр, и они в конечном итоге собираются.

Любая помощь приветствуется. Спасибо.

Это скелеты моего кода:

        Excel.Application xl = null;
        Excel._Workbook wBook = null;
        Excel._Worksheet wSheet = null;
        Excel.Range range = null;

        object m_objOpt = System.Reflection.Missing.Value;

        try
        {
            // open the template
            xl = new Excel.Application();
            wBook = (Excel._Workbook)xl.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate, false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
            wSheet = (Excel._Worksheet)wBook.ActiveSheet;

            int iRowCount = 2;

            // enumerate and drop the values straight into the Excel file
            while (data.Read())
            {

                wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();
                wSheet.Cells[iRowCount, 2] = data["brand"].ToString();
                wSheet.Cells[iRowCount, 3] = data["agency"].ToString();
                wSheet.Cells[iRowCount, 4] = data["advertiser"].ToString();
                wSheet.Cells[iRowCount, 5] = data["product"].ToString();
                wSheet.Cells[iRowCount, 6] = data["comment"].ToString();
                wSheet.Cells[iRowCount, 7] = data["brief"].ToString();
                wSheet.Cells[iRowCount, 8] = data["responseDate"].ToString();
                wSheet.Cells[iRowCount, 9] = data["share"].ToString();
                wSheet.Cells[iRowCount, 10] = data["status"].ToString();
                wSheet.Cells[iRowCount, 11] = data["startDate"].ToString();
                wSheet.Cells[iRowCount, 12] = data["value"].ToString();

                iRowCount++;
            }

            DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
            _report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
            wBook.Close(true, _report.ReportLocation, m_objOpt);
            wBook = null;

        }
        catch (Exception ex)
        {
            LogException.HandleException(ex);
        }
        finally
        {
            NAR(wSheet);
            if (wBook != null)
                wBook.Close(false, m_objOpt, m_objOpt);
            NAR(wBook);
            xl.Quit();
            NAR(xl);
            GC.Collect();
        }

private void NAR(object o)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
    }
    catch { }
    finally
    {
        o = null;
    }
}

Обновление

Независимо от того, что я пытаюсь, «чистый» метод или «уродливый» метод (см. Ответы ниже), экземпляр Excel все еще зависает, как только эта строка нажата:

wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();

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

Я думаю, мне нужно будет проверить, есть ли работающий экземпляр, прежде чем присваивать переменную xl, и вместо этого подключиться к ней. Я забыл упомянуть, что это служба Windows, но это не должно иметь значения, не так ли?


Ответы [ 14 ]

0 голосов
/ 30 июня 2009

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

private bool GenerateDailyProposalsReport(ScheduledReport report)
{
    // start of test

    Excel.Application xl = null;
    Excel._Workbook wBook = null;
    Excel._Worksheet wSheet = null;
    Excel.Range xlrange = null;
    object m_objOpt = System.Reflection.Missing.Value;

    xl = new Excel.Application();
    wBook = (Excel._Workbook)xl.Workbooks.Open(@"E:\Development\Romain\APN\SalesLinkReportManager\ExcelTemplates\DailyProposalReport.xls", false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
    wSheet = (Excel._Worksheet)wBook.ActiveSheet;
    xlrange = wSheet.Cells[2, 1] as Excel.Range;

    // PROBLEM LINE ************
    xlrange.Value2 = "fullname";
    //**************************

    wBook.Close(true, @"c:\temp\DailyProposalReport.xls", m_objOpt);
    xl.Quit();

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Marshal.FinalReleaseComObject(xlrange);
    Marshal.FinalReleaseComObject(wSheet);
    Marshal.FinalReleaseComObject(wBook);
    Marshal.FinalReleaseComObject(xl);

    xlrange = null;
    wSheet = null;
    wBook = null;
    xl = null;

    // end of test
    return true;

}

Если я закомментирую строку «ПРОБЛЕМА» выше, экземпляр Excel будет освобожден из памяти. Как оно есть, это не так. Я был бы признателен за любую дополнительную помощь в этом, так как время мимолетно и приближается крайний срок (не все ли).

Пожалуйста, спросите, нужна ли вам дополнительная информация. Спасибо в ожидании.

Добавление

Немного больше информации, которая может или не может пролить больше света на это. Я прибегаю к уничтожению процесса (измерение временной задержки) по истечении определенного промежутка времени (5-10 секунд, чтобы дать Excel время завершить свои процессы). У меня запланировано два отчета - первый отчет создается и сохраняется на диск, а процесс Excel завершается, а затем отправляется по электронной почте. Второй создается, сохраняется на диск, процесс останавливается, но внезапно возникает ошибка при попытке электронной почты. Ошибка: Процесс не может получить доступ к файлу '....' и т. Д.

Таким образом, даже если приложение Excel было уничтожено, фактический файл Excel все еще хранится службой Windows. Я должен убить службу, чтобы удалить файл ...

0 голосов
/ 25 июня 2009

Рассматривали ли вы использование чистого решения .NET, такого как SpreadsheetGear for .NET ? Вот что может понравиться вашему коду при использовании SpreadsheetGear:

// open the template            
using (IWorkbookSet workbookSet = SpreadsheetGear.Factory.GetWorkbookSet())
{
    IWorkbook wBook = workbookSet.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate);
    IWorksheet wSheet = wBook.ActiveWorksheet;
    int iRowCount = 2;
    // enumerate and drop the values straight into the Excel file            
    while (data.Read())
    {
        wSheet.Cells[iRowCount, 1].Value = data["fullname"].ToString();
        wSheet.Cells[iRowCount, 2].Value = data["brand"].ToString();
        wSheet.Cells[iRowCount, 3].Value = data["agency"].ToString();
        wSheet.Cells[iRowCount, 4].Value = data["advertiser"].ToString();
        wSheet.Cells[iRowCount, 5].Value = data["product"].ToString();
        wSheet.Cells[iRowCount, 6].Value = data["comment"].ToString();
        wSheet.Cells[iRowCount, 7].Value = data["brief"].ToString();
        wSheet.Cells[iRowCount, 8].Value = data["responseDate"].ToString();
        wSheet.Cells[iRowCount, 9].Value = data["share"].ToString();
        wSheet.Cells[iRowCount, 10].Value = data["status"].ToString();
        wSheet.Cells[iRowCount, 11].Value = data["startDate"].ToString();
        wSheet.Cells[iRowCount, 12].Value = data["value"].ToString();
        iRowCount++;
    }
    DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
    _report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
    wBook.SaveAs(_report.ReportLocation, FileFormat.OpenXMLWorkbook);
}

Если у вас более нескольких рядов, вы можете быть шокированы тем, насколько быстрее он работает. И вам никогда не придется беспокоиться о зависании экземпляра Excel.

Вы можете скачать бесплатную пробную версию здесь и попробовать сами.

Отказ от ответственности: я владею SpreadsheetGear LLC

0 голосов
/ 25 июня 2009

Вот содержимое моего блока "еще не удалось", и, наконец, наконец, для очистки автоматизации Excel. Мое приложение оставляет Excel открытым, поэтому нет вызова Quit. Ссылка в комментарии была моим источником.

finally
{
    // Cleanup -- See http://www.xtremevbtalk.com/showthread.php?t=160433
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    // Calls are needed to avoid memory leak
    Marshal.FinalReleaseComObject(sheet);
    Marshal.FinalReleaseComObject(book);
    Marshal.FinalReleaseComObject(excel);
}
0 голосов
/ 25 июня 2009

То, что я в конечном итоге сделал, чтобы решить подобную проблему, - это получить идентификатор процесса и убить его как последнее средство ...

[DllImport("user32.dll", SetLastError = true)]
   static extern IntPtr GetWindowThreadProcessId(int hWnd, out IntPtr lpdwProcessId);

...

objApp = new Excel.Application();

IntPtr processID;
GetWindowThreadProcessId(objApp.Hwnd, out processID);
excel = Process.GetProcessById(processID.ToInt32());

...

objApp.Application.Quit();
Marshal.FinalReleaseComObject(objApp);
_excel.Kill();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...