Безопасный код
. . .
try
. . .
// Создаем объект - приложение Excel
Excel := CreateOleObject('Excel.Application');
. . .
// Делаем приложение Excel видимым
Excel.Visible := true;
finally
// Удаляем связь с объектом Excel
Excel := Unassigned;
end;
Все работает нормально, но, если у меня в промежутке между созданием Excel И его отображеним вылетит какая-либо ошибка, сгенерированная Excel-ом (например, OLE error xxxxxx), то секция finally игнорируется, т.е. объект Excel не освобождается, а в процессах остается висеть EXCEL невидимый.
Как отловить эту ошибку? Или как сделать, чтобы Excel сам удалялся при первой же ошибке?
Как отловить эту ошибку? Или как сделать, чтобы Excel сам удалялся при первой же ошибке?
Обычно пишут так:
. . .
Excel := CreateOleObject('Excel.Application');
try
. . .
// Создаем объект - приложение Excel
. . .
// Делаем приложение Excel видимым
Excel.Visible := true;
finally
// Удаляем связь с объектом Excel
Excel := Unassigned;
end;
Обычно пишут так:
. . .
Excel := CreateOleObject('Excel.Application');
try
. . .
// Создаем объект - приложение Excel
. . .
// Делаем приложение Excel видимым
Excel.Visible := true;
finally
// Удаляем связь с объектом Excel
Excel := Unassigned;
end;
Чем этот вариант лучше?
Неважно как пишут, суть вопроса в другом. Читай внимательней.
Появляется ли проблемма если код содержит только те строки что ты вынес в вопрос?
Чем этот вариант лучше?
Дело в том, что ошибка может возникнуть и в процессе создания объекта Excel. Если такое произойдет, блок finally вызываться не будет.
Неважно как пишут, суть вопроса в другом. Читай внимательней.
Таким образом, в моем образце кода в блоке finally существует гарантия, что объект Excel определен.
А у меня вот никак не вылетает подобная ошибка. :( Как ты это делаешь? :)
Появляется ли проблемма если код содержит только те строки что ты вынес в вопрос?
Чтобы вылетела ошибка, вызови несуществующий метод. Например, Excel.Foo
Дело в том, что ошибка может возникнуть и в процессе создания объекта Excel. Если такое произойдет, блок finally вызываться не будет.
Таким образом, в моем образце кода в блоке finally существует гарантия, что объект Excel определен.
В том и дело, что при создании объекта Excel может вылететь ошибка. В реальном коду, у меня еще создаются и инициализируются другие объекты, которые необходимо будет освободить в любом случае, независимо от того, создался объект Excel или нет.
В реальном коду, у меня еще создаются и инициализируются другие объекты, которые необходимо будет освободить в любом случае, независимо от того, создался объект Excel или нет.
Ну, значит, процесс работы с каждым проблемным объектом нужно заключить в собственный блок try-finally.
Ну, значит, процесс работы с каждым проблемным объектом нужно заключить в собственный блок try-finally.
Ты предлагаешь делать вложенные блоки try...finally?
Ты предлагаешь делать вложенные блоки try...finally?
Так всегда так делают. А как по-другому?
Excel, Workbook, Sheet: OleVariant;
begin
Excel := CreateOleObject('Excel.Application');
try
Workbook := OlePropertyGet(Excel, <параметры>);
try
Sheet := Workbook[0]; // или как там еще - не помню уже ;)
try
<...код работы с Sheet...>
finally
Sheet := Null;
end;
Sheet := Workbook[1]; // аналогично предыдущему
try
<...код работы с Sheet...>
finally
Sheet := Null;
end;
finally
Workbook := Null; // принудительное "отпускание" объекта Workbook
end;
finally
Excel.Quit(<не знаю, какие тут должны быть параметры>);
// кстати, присваивать Unassigned или Null необязательно,
// Дельфи автоматом вызывает для всех автоматических переменных типа String, Record или Variant
// процедуру Finalize, которая освобождает всю дополнительно выделенную память
end;
end;
Так всегда так делают.
Я с тобой совершенно согласен.
Насчет автоматического удаления переменных...
Случаем не в курсе, если у меня функция возвращает инициализированный TStringList, его удалять обязательно?
ИМХО. Все же лучше самому удалять все переменные. Это будет правильный тон программирования.
Случаем не в курсе, если у меня функция возвращает инициализированный TStringList, его удалять обязательно?
Естественно.
Кстати, борец за хороший тон программирования, - создавать и возвращать некоторый объект внутри функции - моветон. ;)
Чтобы вылетела ошибка, вызови несуществующий метод. Например, Excel.Foo
Ну вызвал я несуществующий метод... Он мне сказал про это. А потом таки выполнил секцию finally. Чего еще не хватило?
Кстати, обратил внимание, что сама Excel:=unassigned мгновенного эффекта не дала. Пока вся секция не выполнилась, Excel в процессах висел, а потом исчез (я объявлял его глобально, так что речь не об уничтожении локальной переменной при выходе из процедуры).
И ты засунь в эту finally какой-нибудь showmessage('labuda') чтобы убедиться, что он точно не выполняется.
Ну вызвал я несуществующий метод... Он мне сказал про это. А потом таки выполнил секцию finally. Чего еще не хватило?
Кстати, обратил внимание, что сама Excel:=unassigned мгновенного эффекта не дала. Пока вся секция не выполнилась, Excel в процессах висел, а потом исчез (я объявлял его глобально, так что речь не об уничтожении локальной переменной при выходе из процедуры).
И ты засунь в эту finally какой-нибудь showmessage('labuda') чтобы убедиться, что он точно не выполняется.
Я сейчас, к сожалению, точно не помню. Но были такие ситуации, когда несколько таких ошибок приводило к захломлению списка процессов, в котором через некоторое время начинало висеть процессов 20 с названием EXCEL, сильно тормозивших систему.
Насчет Unassigned...
Присвоение Execl значения Unassigned не уничтожает и не закрывает приложения Excel. Excel будет открыт до тех пор, пока пользователь не закроет его сам, главное не забыть показать приложение, а то только в процессах и можно будет найти. Чтобы закрыть приложение надо вызвать метод Quit.
Кстати, борец за хороший тон программирования, - создавать и возвращать некоторый объект внутри функции - моветон. ;)
Ну ладно, не будем придираться к типу возвращаемых значений :) Иногда без этого никак, а объявлять глобальные переменные, как в Basic, совсем не хочется. Не в каменном веке живем ведь ;)
Иногда без этого никак, а объявлять глобальные переменные, как в Basic, совсем не хочется. Не в каменном веке живем ведь ;)
Можно написать процедуру, которая будет записывать результаты в переданный ей экземпляр класса. Например, как TDataSet.GetFieldList.
Можно написать процедуру, которая будет записывать результаты в переданный ей экземпляр класса. Например, как TDataSet.GetFieldList.
Это не вариант. У меня есть универсальная функция, которая дожна возвращать разное количество значений. TStringList для этого, как нельзя лучше, подходит. И к этой функции обращаются разные классы.