c# и автоматизация Excel - окончание рабочего экземпляра

Что, если вы создали группу div шириной 1 "x1" и добавили их один за другим к родительскому div, пока ограничивающий прямоугольник не перепрыгнул с 1 дюйма до 2 дюймов? Дюйм на мини - это тот же размер, что и на iPad, верно?

13
задан Sean 25 June 2009 в 20:02
поделиться

11 ответов

ОБНОВЛЕНИЕ (ноябрь 2016 г.)

Я только что прочитал убедительный аргумент Ханса Пассанта о том, что использование GC.Collect на самом деле правильный путь. Я больше не работаю с Office (слава богу), но если бы я это сделал, я бы, вероятно, хотел бы попробовать еще раз - это, безусловно, упростило бы большую часть (тысячи строк) кода, который я написал, пытаясь сделать что-то "правильным" "путь (как я тогда увидел).

Я оставлю свой исходный ответ для потомков ...


Как Майк говорит в своем ответе, есть простой и трудный способ справиться с этим. Майк предлагает использовать простой способ, потому что ... он проще. Я не Я лично считаю, что это достаточно веская причина, и я не верю, что это правильный путь. Для меня это похоже на «выключите и снова включите».

У меня есть несколько лет опыта разработки приложений автоматизации Office на .NET, и эти проблемы взаимодействия с COM мучили меня в течение первых нескольких недель и месяцев, когда я впервые запустил не в последнюю очередь из-за того, что Microsoft очень скромно признает наличие проблемы, а в то время в Интернете было трудно найти хороший совет.

У меня есть способ работы, который я сейчас использую практически без думаю об этом, и прошло много лет с тех пор, как у меня была проблема. По-прежнему важно быть в курсе всех скрытых объектов, которые вы можете создавать - и да, если вы пропустите один, у вас может быть утечка, которая станет очевидной только намного позже. Но это' не хуже, чем было в старые плохие времена malloc / free .

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

В любом случае, метод, который я использую заключается в использовании класса-оболочки, который реализует IDisposable и который в своем методе Dispose вызывает ReleaseComObject . Таким образом, я могу использовать с помощью операторов , чтобы гарантировать, что объект будет удален (и COM-объект освобожден), как только я закончу с ним.

Что особенно важно, он будет удален / освобожден даже если моя функция вернется раньше, или там ' s исключение и т. д. Кроме того, он будет удален / освобожден только , если он был фактически создан изначально - назовите меня педантом, но предлагаемый код, который пытается освободить объекты, которые на самом деле могут не иметь Созданный мне кажется небрежным кодом. У меня аналогичное возражение против использования FinalReleaseComObject - вы должны знать, сколько раз вы вызывали создание ссылки COM, и, следовательно, должны иметь возможность выпускать ее такое же количество раз.

Типичный фрагмент моего кода может выглядеть так: это (или было бы, если бы я использовал C # v2 и мог бы использовать дженерики: -)):

using (ComWrapper<Excel.Application> application = new ComWrapper<Excel.Application>(new Excel.Application()))
{
  try
  {
    using (ComWrapper<Excel.Workbooks> workbooks = new ComWrapper<Excel.Workbooks>(application.ComObject.Workbooks))
    {
      using (ComWrapper<Excel.Workbook> workbook = new ComWrapper<Excel.Workbook>(workbooks.ComObject.Open(...)))
      {
        using (ComWrapper<Excel.Worksheet> worksheet = new ComWrapper<Excel.Worksheet>(workbook.ComObject.ActiveSheet))
        {
          FillTheWorksheet(worksheet);
        }
        // Close the workbook here (see edit 2 below)
      }
    }
  }
  finally
  {
    application.ComObject.Quit();
  }
}

Я не собираюсь делать вид, что это не многословно, а отступ, вызванный созданием объекта может выйти из-под контроля, если вы не разделите материал на более мелкие методы. Этот пример - худший случай, поскольку все мы повторение - это создание объектов. Обычно между фигурными скобками происходит гораздо больше, и накладные расходы намного меньше.

Обратите внимание, что в соответствии с приведенным выше примером я всегда передаю "завернутые" объекты между методами, а не голый COM-объект, и это будет ответственность вызывающей стороны за ее удаление (обычно с помощью оператора using ). Точно так же я всегда возвращал завернутый объект, а не голый, и снова вызывающий объект должен был бы освободить его. Вы можете использовать другой протокол, но важно иметь четкие правила, как это было, когда нам приходилось управлять собственной памятью.

Используемый здесь класс ComWrapper , надеюсь, требует небольшое объяснение. Он просто хранит ссылку на завернутый COM-объект, и освобождает его явно (используя ReleaseComObject ) в своем методе Dispose . Метод ComObject просто возвращает типизированную ссылку на обернутый COM-объект.

Надеюсь, это поможет!

ИЗМЕНИТЬ : Я только сейчас перешел по ссылке на Ответ Майка на другой вопрос , и я вижу, что в другом ответе на этот вопрос есть ссылка на класс-оболочку, как я предлагаю выше.

Кроме того, что касается ответа Майка на этот другой вопрос, я должен сказать Меня почти соблазнил аргумент «просто используйте GC.Collect ». Однако меня в основном тянуло к этому на ложной предпосылке; На первый взгляд это выглядело так, как будто нет необходимости вообще беспокоиться о ссылках на COM. Тем не мение, как говорит Майк, вам все равно нужно явно освободить COM-объекты, связанные со всеми вашими переменными в области видимости, и поэтому все, что вы сделали, это уменьшили, а не устранили необходимость в управлении COM-объектами. Лично я лучше пойду на все.

Я также отмечаю тенденцию во многих ответах писать код, в котором все освобождается в конце метода, в большом блоке вызовов ReleaseComObject . Это все очень хорошо, если все работает, как запланировано, но я бы посоветовал любому, кто пишет серьезный код, подумать, что произойдет, если будет выброшено исключение или если у метода будет несколько точек выхода (код не будет выполняться, и, следовательно, объекты COM не будет выпущен). Вот почему я предпочитаю использовать "обертки" и с использованием s. Это многословно, но делает код пуленепробиваемым.

РЕДАКТИРОВАТЬ2 : я обновил приведенный выше код, чтобы указать, где следует закрыть книгу с сохранением или без сохранения изменений. Вот код для сохранения изменений:

object saveChanges = Excel.XlSaveAction.xlSaveChanges;

workbook.ComObject.Close(saveChanges, Type.Missing, Type.Missing);

... и чтобы не сохранить изменения, просто измените xlSaveChanges на xlDoNotSaveChanges .

12
ответ дан 1 December 2019 в 21:53
поделиться

Рассматривали ли вы использование чистого .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
ответ дан 1 December 2019 в 21:53
поделиться

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

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
ответ дан 1 December 2019 в 21:53
поделиться

Для решения похожей проблемы я получил идентификатор процесса и убил его в крайнем случае ...

[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();
0
ответ дан 1 December 2019 в 21:53
поделиться

Боюсь, у меня здесь заканчиваются идеи, Шон. : - (

У Гэри могут быть некоторые мысли, но, хотя его подход к оболочке очень надежен, он на самом деле не поможет вам в этом случае, потому что вы уже все делаете довольно правильно.

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

xlrange.Value2 = "fullname";

, казалось бы, не была затронута ни одной из этих идей, но здесь идет:

(1) Не используйте интерфейсы _Workbook и _Worksheet. Используйте вместо них Workbook и Worksheet. (Подробнее об этом см .: Взаимодействие Excel: _Worksheet или Worksheet? .)

(2) Каждый раз, когда у вас есть две точки ("." ) в той же строке при доступе к объекту Excel, разбейте его на две строки, присвоив каждому объекту именованную переменную. Затем в разделе очистки вашего кода явно освободите каждую переменную с помощью Marshal.FinalReleaseComObject ().

Например, ваш код здесь:

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

может быть разбит на:

Excel.Workbooks wBooks = xl.Workbooks;
wBook = wBooks.Open("@"E:\Development\...\DailyProposalReport.xls", etc...);

А затем позже, в рамках очистки раздел, у вас будет:

Marshal.FinalReleaseComObject(xlrange);
Marshal.FinalReleaseComObject(wSheet);
Marshal.FinalReleaseComObject(wBook);
Marshal.FinalReleaseComObject(wBooks); // <-- Added
Marshal.FinalReleaseComObject(xl);

(3) Я не уверен, что происходит с вашим подходом Process.Kill. Если вы вызовете wBook.Close (), а затем xl.Quit () перед вызовом Process.Kill (), у вас не должно возникнуть проблем. Workbook.Close () не возвращает выполнение вам, пока книга не будет закрыта, а Excel.Quit () не вернет выполнение, пока Excel не завершит завершение работы (хотя он все еще может зависать).

После вызова Process.Kill () вы можете проверить свойство Process.HasExited в цикле или, лучше, вызвать Process. WaitForExit (), который будет приостанавливаться до тех пор, пока не выйдет за вас. Я предполагаю, что это обычно происходит менее чем за секунду. Лучше подождать меньше времени и быть уверенным, чем ждать 5-10 секунд и только догадываться.

(4) Вам следует попробовать эти идеи по очистке, которые я перечислил выше, но я начинаю подозревать, что вы могли бы есть проблема с другими процессами, которые могут работать с Excel, например с надстройкой или антивирусной программой. Эти надстройки могут привести к зависанию Excel, если они не будут выполнены правильно. В этом случае выпуск Excel может быть очень трудным или невозможным. Вам нужно будет определить программу-нарушитель, а затем отключить ее. Другая возможность заключается в том, что работа в качестве службы Windows каким-то образом является проблемой. Я не понимаю, почему это так, но у меня нет опыта автоматизации Excel через службу Windows, поэтому я не могу сказать. Если ваши проблемы связаны с этим, то использование Process.Kill, вероятно, будет вашим единственным прибежищем здесь.

Это все, о чем я могу думать без промедления, Шон. Надеюсь, это поможет. Сообщите нам, как это происходит ...

- Майк

0
ответ дан 1 December 2019 в 21:53
поделиться

Самый простой способ вставить код - задать вопрос - это не значит, что я ответил на свой вопрос (к сожалению). Приношу свои извинения тем, кто пытается мне помочь - до сих пор я не мог вернуться к этому. Это все еще меня озадачивает ... Я полностью выделил код 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
ответ дан 1 December 2019 в 21:53
поделиться

Шон,

Я собираюсь повторно опубликовать ваш код с моими изменениями (см. Ниже). Я избегал слишком частого изменения вашего кода, поэтому я не добавил никакой обработки исключений и т. Д. Этот код не надежен.

private bool GenerateDailyProposalsReport(ScheduledReport report)
{
    Excel.Application xl = null;
    Excel.Workbooks wBooks = null;
    Excel.Workbook wBook = null;
    Excel.Worksheet wSheet = null;
    Excel.Range xlrange = null;
    Excel.Range xlcell = null;

    xl = new Excel.Application();

    wBooks = xl.Workbooks;

    wBook = wBooks.Open(@"DailyProposalReport.xls", false, false, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

    wSheet = wBook.ActiveSheet;

    xlrange = wSheet.Cells;

    xlcell = xlrange[2, 1] as Excel.Range;

    xlcell.Value2 = "fullname";

    Marshal.ReleaseComObject(xlcell);
    Marshal.ReleaseComObject(xlrange);
    Marshal.ReleaseComObject(wSheet);

    wBook.Close(true, @"c:\temp\DailyProposalReport.xls", Type.Missing);

    Marshal.ReleaseComObject(wBook);
    Marshal.ReleaseComObject(wBooks);

    xl.Quit();

    Marshal.ReleaseComObject(xl);

    return true;
}

На заметку:

  1. Рабочие книги метод Класс приложения создает Workbooks объект, содержащий ссылка на соответствующий COM объект, поэтому нам нужно убедиться, что мы впоследствии отпустите эту ссылку, поэтому я добавил переменную wBooks и соответствующий вызов ReleaseComObject .

  2. Точно так же метод Cells объекта Объект рабочего листа возвращает Диапазон объект с другой ссылкой COM, так что нам тоже нужно это очистить. Следовательно, необходимы 2 отдельные переменные Range .

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

  4. Я не звоню GC.Collect и т. д. потому что в этом не должно быть необходимости. Действительно!

  5. Я скорее использую ReleaseComObject чем FinalReleaseComObject, потому что этого должно быть вполне достаточно.

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

  7. Не имеет отношения здесь, но я использую Type.Missing вместо того System.Reflection.Missing.Value для удобство. Катитесь на C # v4, где необязательные параметры будут поддерживается компилятором!

Мне не удалось скомпилировать или запустить этот код, но я почти уверен, что он сработает. Удачи!

0
ответ дан 1 December 2019 в 21:53
поделиться

Добавьте следующее перед ваш вызов xl.Quit ():

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

Вы также можете использовать Marshal.FinalReleaseComObject () в вашем методе NAR вместо ReleaseComObject. ReleaseComObject уменьшает счетчик ссылок на 1, тогда как FinalReleaseComObject освобождает все ссылки, поэтому счетчик равен 0.

Итак, ваш блок finally будет выглядеть так:

finally
{
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    NAR(wSheet);
    if (wBook != null)
        wBook.Close(false, m_objOpt, m_objOpt);
    NAR(wBook);
    xl.Quit();
    NAR(xl);
}

Обновленный метод NAR:

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

Я исследовал это некоторое время назад, и в примерах я обнаружил, что обычно вызовы, связанные с сборщиком мусора, выполняются в конце после закрытия приложения. Тем не мение, есть MVP (Майк Розенблюм), который упоминает, что его следует вызвать в самом начале. Я пробовал оба способа, и они сработали. Я также попробовал это без WaitForPendingFinalizers, и он работал, хотя ничего не должно было повредить. YMMV.

Вот соответствующие ссылки от MVP, о которых я упоминал (они в VB, но не сильно отличаются):

2
ответ дан 1 December 2019 в 21:53
поделиться

Дело в том, что ваш вызов:

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

по сути такой же, как:

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

. Сделав это таким образом, вы можете увидеть, что вы создаете Excel. Range , а затем присвоение ему значения. Этот способ также дает нам именованную ссылку на нашу переменную диапазона, переменную cell , которая позволяет нам напрямую освободить ее, если мы захотим. Таким образом, вы можете очистить свои объекты одним из двух способов:

(1) Сложный и уродливый способ:

while (data.Read())
{
    Excel.Range cell = Sheet.Cells[iRowCount, 1];
    cell.Value = data["fullname"].ToString();
    Marshal.FinalReleaseComObject(cell);

    cell = Sheet.Cells[iRowCount, 2];
    cell.Value = data["brand"].ToString();
    Marshal.FinalReleaseComObject(cell);

    cell = Sheet.Cells[iRowCount, 3];
    cell.Value = data["agency"].ToString();
    Marshal.FinalReleaseComObject(cell);

    // etc...
}

Выше мы освобождаем каждый объект диапазона через вызов Marshal.FinalReleaseComObject (cell) по мере продвижения.

(2) Простой и понятный способ:

Оставьте свой код в том виде, в котором он у вас есть сейчас, а затем, в конце, вы можете очистить его следующим образом:

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

if (wSheet != null)
{
    Marshal.FinalReleaseComObject(wSheet)
}
if (wBook != null)
{
    wBook.Close(false, m_objOpt, m_objOpt);
    Marshal.FinalReleaseComObject(wBook);
}
xl.Quit();
Marshal.FinalReleaseComObject(xl);

Короче , ваш существующий код очень близок к . Если просто добавить вызовы GC.Collect () и GC. WaitForPendingFinalizers () перед вызовом NAR, я думаю, это должно сработать для вас. (Короче говоря, код Джейми и код Ахмада верны. Код Джейми чище, но код Ахмада - более легкое «быстрое исправление» для вас, потому что вам нужно будет только добавить вызовы к вызовам GC.Collect () и GC.WaitForPendingFinalizers () к вашему существующему коду.)

Джейми и Амхад также перечислили ссылки на .NET Automation Forum , в котором я участвую (спасибо, ребята!) Вот пара связанных сообщений, которые я сделал здесь, на StackOverflow :

(1) Как правильно очистить объекты взаимодействия Excel в C #

(2) C # Автоматизировать PowerPoint Excel - PowerPoint не закрывается

Надеюсь, это поможет, Шон ...

Майк

Я думаю, это должно сработать для вас. (Короче говоря, код Джейми и код Ахмада верны. Код Джейми чище, но код Ахмада - более легкое «быстрое исправление» для вас, потому что вам нужно будет только добавить вызовы к вызовам GC.Collect () и GC.WaitForPendingFinalizers () к вашему существующему коду.)

Джейми и Амхад также перечислили ссылки на .NET Automation Forum , в котором я участвую (спасибо, ребята!) Вот пара связанных сообщений, которые я сделал здесь, на StackOverflow :

(1) Как правильно очистить объекты взаимодействия Excel в C #

(2) C # Автоматизировать PowerPoint Excel - PowerPoint не закрывается

Надеюсь, это поможет, Шон ...

Майк

Я думаю, это должно сработать для вас. (Короче говоря, код Джейми и код Ахмада верны. Код Джейми чище, но код Ахмада - более легкое «быстрое исправление» для вас, потому что вам нужно будет только добавить вызовы к вызовам GC.Collect () и GC.WaitForPendingFinalizers () к вашему существующему коду.)

Джейми и Амхад также перечислили ссылки на .NET Automation Forum , в котором я участвую (спасибо, ребята!) Вот пара связанных сообщений, которые я сделал здесь, на StackOverflow :

(1) Как правильно очистить объекты взаимодействия Excel в C #

(2) C # Автоматизировать PowerPoint Excel - PowerPoint не закрывается

Надеюсь, это поможет, Шон ...

Майк

6
ответ дан 1 December 2019 в 21:53
поделиться

I have just answered this question here:

Killing excel process by its main window hWnd

1
ответ дан 1 December 2019 в 21:53
поделиться

Нет необходимости использовать объекты excel com из C#. Вы можете использовать OleDb для модификации листов.

http://www.codeproject.com/KB/office/excel_using_oledb.aspx

0
ответ дан 1 December 2019 в 21:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: