Делает *каждый*, Excel interop возражает потребности, которая будет выпущена с помощью Маршала. ReleaseComObject?

Править

См. также, Как я правильно очищаю Excel interop объекты?. Я недавно столкнулся с этим вопросом, и он обеспечил большое понимание проблемы того, как правильно избавиться от COM-объектов. Определенно проверьте вне первого (отмеченного) ответа, потому что другие ответы идут вне простого, "не используют две точки" и "использование ReleaseComObject поскольку каждый com возражает" совету.

Я пересмотрел этот вопрос во-первых, потому что я понял, что, несмотря на то, чтобы быть очень полным о регистрации и расположении всех моих 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().Select(ws => ws.Name);

... но я обеспокоен, что закончу с утечками памяти или повторю процессы, если я не выпущу каждый рабочий лист (ws) объект.

Любое понимание на этом ценилось бы.

Обновление

На основе ответов до сих пор, это кажется, что я действительно должен выпустить каждый объект com, который что я создаю. Я воспользовался возможностью для создания a ComObjectManager класс для создания немного легче иметь дело с этой головной болью. Необходимо не забыть использовать Get() метод каждый раз, когда Вы инстанцируете нового объекта com, но если Вы делаете, это будет заботиться обо всем остальном для Вас. Сообщите мне, видите ли Вы какие-либо проблемы с ним (или отредактируйте и оставьте комментарий, если Вы можете). Вот код:

public class ComObjectManager : IDisposable
{
    private Stack _comObjects = new Stack();

    public TComObject Get(Func 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(() => new ApplicationClass());
        var workbook = com.Get(() => application.Workbooks.Add(_missing));
        var worksheets = com.Get(() => workbook.Worksheets);
        for (var worksheetIndex = 1; worksheetIndex < worksheets.Count; worksheetIndex++)
        {
            var worksheet = com.Get(() => (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();
    }
}

28
задан Community 23 May 2017 в 11:45
поделиться

2 ответа

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

13
ответ дан 28 November 2019 в 03:52
поделиться

Вы должны вызывать Marshal.ReleaseComObject для каждого COM-объекта, который вы используете в своем коде, а не только для основного объекта приложения.

6
ответ дан 28 November 2019 в 03:52
поделиться
Другие вопросы по тегам:

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