Excel 2007 зависает при закрытии через .NET

У меня есть программа Visual Basic .NET, которая должна открывать и закрывать электронную таблицу Excel. Открытие и чтение электронной таблицы работают нормально, но попытка закрыть приложение Excel 2007 приводит к зависанию. Вроде закрывается, но если посмотреть в диспетчере задач, то приложение все еще работает. Код, который я использую для его закрытия,

wbkData.Close(saveChanges:=False)
appExcel.Quit()
wbkData = Nothing
appExcel = Nothing

Как мне заставить Excel правильно закрыться?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
2 517
3

Ответы 3

Я нашел решение в блоге MSDN Excel, который у меня работал. Это объясняется как

There are two problems with the above:

(1) Although the code appears to dispose of the 'wbkData' object first, etc., the code above does not actually enforce this as the .NET Garbage Collection procedure can dispose of its objects in any order. (GC is non-deterministic in order, not just non-deterministic in timing.)

(2) Commands such as 'wsh = wbkData.Workssheets.Item(1)' -- or lines like it -- are very common and will create an RCW object wrapping a 'Worksheets' object. You won't have a variable holding a reference to it, so you don't generally think about it, but this RCW object will not be disposed until the next Garbage Collection. However, the code above calls GC.Collect() last, and so the RCW is still holding a reference to this 'Worksheets' object when appExcel.Quit() is called. Excel hangs as a result.

Окончательный код выглядит как

GC.Collect()
GC.WaitForPendingFinalizers()

wbkData.Close(SaveChanges:=False)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wbkData) : wbkData = Nothing
appExcel.Quit()
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(appExcel) : appExcel = Nothing

Я думаю, здесь был дан ответ на ваш вопрос: Как правильно очистить объекты взаимодействия Excel в c

Я не могу видеть из вашего образца кода, но в основном всегда назначайте свои объекты excel локальным переменным, никогда не опускаясь на две точки, например:

//FAIL

Workbook wkBook = xlApp.Workbooks.Open(@"C:\mybook.xls");

вместо этого ссылайтесь на каждый объект по отдельности:

//WIN

Worksheets sheets = xlApp.Worksheets;
Worksheet sheet = sheets.Open(@"C:\mybook.xls");
...
Marshal.ReleaseComObject(sheets);
Marshal.ReleaseComObject(sheet);

.NET создает оболочку для COM-объекта, которая невидима для вас и не выпускается, пока GC не соткнет свою магию.

Пока я не обнаружил это, я запускал хакерский код ниже в приложении ASP.NET каждый раз, когда я создавал новую книгу, которая проверяет возраст процесса excel.exe и убивает все, что старше минуты:

//force kill any excel processes over one minute old.
try
{
    Process[] procs = Process.GetProcessesByName("EXCEL");
    foreach (Process p in procs)
    {
        if (p.StartTime.AddMinutes(1) < DateTime.Now)
        {
            p.Kill(); 
        }  
    }  
}
catch (Exception)
{}  

Спасибо за указатель на другой вопрос. Кажется, они решают ту же проблему.

Eric Ness 29.10.2008 22:44

Я написал этот пост тебе упоминается в блоге группы разработчиков Excel ...

Я также обсуждал эту проблему ранее в StackOverflow для вопроса Как правильно очистить объекты взаимодействия Excel в C#.

Первый ответ на этот вопрос был отмечен как «правильный» и получил 11 голосов, но я уверяю вас, что эту политику чрезвычайно сложно использовать должным образом на практике. Если кто-нибудь когда-нибудь проскользнет в любом месте и использует «две точки», или перебирает ячейки с помощью цикла для каждого цикла, или любой другой подобной команды, тогда у вас будут объекты COM без ссылок, и вы рискуете зависнуть - и не будет возможности найдите причину этого в коде.

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

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

Eric Ness 30.10.2008 21:15

Нет проблем, Эрик, я рад, что это помогло! :-)

Mike Rosenblum 31.10.2008 18:20

Другие вопросы по теме