У меня есть программа Visual Basic .NET, которая должна открывать и закрывать электронную таблицу Excel. Открытие и чтение электронной таблицы работают нормально, но попытка закрыть приложение Excel 2007 приводит к зависанию. Вроде закрывается, но если посмотреть в диспетчере задач, то приложение все еще работает. Код, который я использую для его закрытия,
wbkData.Close(saveChanges:=False)
appExcel.Quit()
wbkData = Nothing
appExcel = Nothing
Как мне заставить Excel правильно закрыться?





Я нашел решение в блоге 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)
{}
Я написал этот пост тебе упоминается в блоге группы разработчиков Excel ...
Я также обсуждал эту проблему ранее в StackOverflow для вопроса Как правильно очистить объекты взаимодействия Excel в C#.
Первый ответ на этот вопрос был отмечен как «правильный» и получил 11 голосов, но я уверяю вас, что эту политику чрезвычайно сложно использовать должным образом на практике. Если кто-нибудь когда-нибудь проскользнет в любом месте и использует «две точки», или перебирает ячейки с помощью цикла для каждого цикла, или любой другой подобной команды, тогда у вас будут объекты COM без ссылок, и вы рискуете зависнуть - и не будет возможности найдите причину этого в коде.
Вместо этого процедура очистки, которую вы применяете, определенно подходит.
Привет Майк. Спасибо за невероятно полезный пост в блоге группы разработчиков Excel. Это была единственная информация, которую я смог найти в Интернете об этой проблеме. Это спасло меня от многих часов разочарования.
Нет проблем, Эрик, я рад, что это помогло! :-)
Спасибо за указатель на другой вопрос. Кажется, они решают ту же проблему.