Редактировать
См. Также Как правильно очистить объекты взаимодействия Excel? . Недавно я столкнулся с этим вопросом, и он дал много понимания проблемы правильной утилизации COM-объектов. Обязательно проверяйте не только первый (отмеченный) ответ, потому что остальные ответы выходят за рамки простого совета «не используйте две точки» и «используйте ReleaseComObject
для каждого ком-объекта».
Я снова вернулся к этому вопросу, потому что понял, что, несмотря на тщательную регистрацию и удаление всех моих COM-объектов, мои экземпляры Excel все еще не были должным образом уничтожены. Оказывается, есть способы создания COM-объектов, которые совершенно неочевидны (т. Е. Вы можете пропустить COM-объекты, даже если вы никогда не используете две точки). Кроме того, даже если вы тщательно, если ваш проект выходит за рамки определенного размера, вероятность пропустить COM-объект приближается к 100%. И может быть очень трудно найти того, кого вы пропустили, когда это произойдет. Ответы на вопрос, связанный выше, предоставляют некоторые другие методы, позволяющие убедиться, что экземпляр Excel определенно будет закрыт. Тем временем я сделал небольшое (но существенное) обновление моего ComObjectManager
(ниже), чтобы отразить то, что я узнал из вопроса, связанного выше.
Оригинальный вопрос
Я видел несколько примеров, где Marshal.ReleaseComObject()
используется с объектами Excel Interop (т.е. объектами из пространства имен Microsoft.Office.Interop.Excel), но я видел, что он использовался в различной степени.
Мне интересно, смогу ли я сойти с рук что-то вроде этого:
var application = new ApplicationClass();
try
{
// do work with application, workbooks, worksheets, cells, etc.
}
finally
{
Marashal.ReleaseComObject(application)
}
Или, если мне нужно освободить каждый созданный объект, как в этом методе:
public void CreateExcelWorkbookWithSingleSheet()
{
var application = new ApplicationClass();
var workbook = application.Workbooks.Add(_missing);
var worksheets = workbook.Worksheets;
for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
{
var worksheet = (WorksheetClass)worksheets[worksheetIndex];
worksheet.Delete();
Marshal.ReleaseComObject(worksheet);
}
workbook.SaveAs(
WorkbookPath, _missing, _missing, _missing, _missing, _missing,
XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
workbook.Close(true, _missing, _missing);
application.Quit();
Marshal.ReleaseComObject(worksheets);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(application);
}
Что побудило меня задать этот вопрос, так это то, что, будучи преданным LINQ, я действительно хочу сделать что-то вроде этого:
var worksheetNames = worksheets.Cast<Worksheet>().Select(ws => ws.Name);
... но я обеспокоен тем, что в конечном итоге у меня возникнут утечки памяти или процессы-призраки, если я не отпущу каждый объект рабочего листа (ws
).
Любое понимание этого будет оценено.
Обновление
Судя по ответам, похоже, мне действительно нужно выпустить каждый созданный мной ком-объект. Я воспользовался возможностью, чтобы создать класс ComObjectManager
, чтобы немного облегчить эту головную боль. Не забывайте использовать метод Get()
каждый раз, когда создаете новый объект com, но если вы это сделаете, он позаботится обо всем остальном за вас. Пожалуйста, дайте мне знать, если вы видите какие-либо проблемы с ним (или отредактируйте и оставьте комментарий, если можете). Вот код:
public class ComObjectManager : IDisposable
{
private Stack<object> _comObjects = new Stack<object>();
public TComObject Get<TComObject>(Func<TComObject> getter)
{
var comObject = getter();
_comObjects.Push(comObject);
return comObject;
}
public void Dispose()
{
// these two lines of code will dispose of any unreferenced COM objects
GC.Collect();
GC.WaitForPendingFinalizers();
while (_comObjects.Count > 0)
Marshal.ReleaseComObject(_comObjects.Pop());
}
}
Вот пример использования:
public void CreateExcelWorkbookWithSingleSheet()
{
using (var com = new ComObjectManager())
{
var application = com.Get<ApplicationClass>(() => new ApplicationClass());
var workbook = com.Get<Workbook>(() => application.Workbooks.Add(_missing));
var worksheets = com.Get<Sheets>(() => workbook.Worksheets);
for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
{
var worksheet = com.Get<WorksheetClass>(() => (WorksheetClass)worksheets[worksheetIndex]);
worksheet.Delete();
}
workbook.SaveAs(
WorkbookPath, _missing, _missing, _missing, _missing, _missing,
XlSaveAsAccessMode.xlExclusive, _missing, _missing, _missing, _missing, _missing);
workbook.Close(true, _missing, _missing);
application.Quit();
}
}