Как я писал раньше, Delphi 2010 научилась вызывать функций из DLL новым способом – с помощью отсроченной загрузки DLL.
Одним из недостатков этого способа является то, что если при вызове функции произойдет ошибка, то пользователь не узнает ее причину. В случае если DLL не существует, так же как и если в DLL нет нужной функции, то сообщение в программе будет однотипным:
Язык программирования самого высокого уровня содержит всего несколько команд для управления программистами
Показаны сообщения с ярлыком Delphi 2010. Показать все сообщения
Показаны сообщения с ярлыком Delphi 2010. Показать все сообщения
27 апреля 2010
14 апреля 2010
Delphi 2010: Отсроченная загрузка DLL
Долгие годы программы на Delphi умели загружать DLL двумя способами: статически и динамически. В Delphi 2010 появился третий способ – отсроченная загрузка.
Сделаем простенькую DLL, которая экспортирует некую функцию, например, CalcFee:
library TestDLL;
Function CalcFee(const iID: Integer): Currency;
begin
Result := ...;
end;
Exports
CalcFee;
end.
и рассмотрим все три способа.
1. Статическая загрузка.
Это самый простой способ вызвать функцию из DLL. Для его реализации необходимо всего лишь описать внешнюю функцию:
Function CalcFee (const iID: Integer): Currency; external 'TestDLL.dll';
И после этого её можно вызывать:
fFee := CalcFee(1234)
Наша DLL загружается при запуске программы и остается загруженной до завершения ее работы.
За этой простотой скрывается большая проблема – если DLL удалить или испортить, то программа просто не запустится.
2. Динамическая загрузка.
Этот способ – более продвинутый, но требует написания значительно большего количества кода:
Var
CalcFee: function(const iID: Integer): Currency;
hDLL: THandle;
begin
hDLL := LoadLibrary('TestDLL.dll');
If hDLL <> 0
then try
@CalcFee := GetProcAddress(hDLL, 'CalcFee');
If @CalcFee = nil
then ShowMessage('В TestDLL.dll не найдена функция CalcFee')
else fFee := CalcFee(1234);
finally
FreeLibrary(hDLL);
end
else ShowMessage('Ошибка при загрузке TestDLL.dll')
В этом коде используются функции Win32 API из Windows.pas:
- LoadLibrary – загружаем DLL;
- GetProcAddress – получаем адрес функции по её имени;
- FreeLibrary – выгружаем DLL из памяти.
Динамическая загрузка позволяет загружать DLL только при необходимости и выгружать её, если она больше не нужна. Второе преимущество и более важное – это возможность обработать ошибки при загрузке DLL и вызове функции. Поэтому даже без DLL или с испорченной DLL программа будет работать. Если перед загрузкой DLL ее удалить, то получим нормальное сообщение:
3. Отсроченная загрузка
В Delphi 2010 появился третий способ вызова функций из DLL – отсроченная загрузка. Как и динамическая загрузка, она позволяет загружать DLL только при необходимости (например, для экономии ресурсов), но также как при статической загрузке, функцию достаточно лишь описать, добавив директиву delayed:
Function CalcFee(const iID: Integer): Currency; external 'TestDLL.dll' delayed;
Недостатки этого способа – это то, что DLL уже нельзя выгрузить и то, что если при вызове функции произойдет ошибка, то пользователь не узнает ее причину. В справке написано "Trying to call a delayed routine that cannot be resolved results in a run-time error (or an exception, if the SysUtils unit is loaded)". Если DLL удалить или если в DLL нет нужной функции, сообщение будет однотипным:
Если функция будет использована в консольной программе, то сообщение об ошибке будет тоже не информативным:
"Runtime error 255 at 7C812AFB "
Но спасение есть. При желании ошибки, возникающие во время загрузки DLL или вызове её функций можно перехватить и обработать с помощью процедур SetDliNotifyHook и SetDliFailureHook из юниты System.
Не знаю, как вам, но мне идея отсроченной загрузки DLL понравилась, и я буду её в будущем использовать.
Сделаем простенькую DLL, которая экспортирует некую функцию, например, CalcFee:
library TestDLL;
Function CalcFee(const iID: Integer): Currency;
begin
Result := ...;
end;
Exports
CalcFee;
end.
и рассмотрим все три способа.
1. Статическая загрузка.
Это самый простой способ вызвать функцию из DLL. Для его реализации необходимо всего лишь описать внешнюю функцию:
Function CalcFee (const iID: Integer): Currency; external 'TestDLL.dll';
И после этого её можно вызывать:
fFee := CalcFee(1234)
Наша DLL загружается при запуске программы и остается загруженной до завершения ее работы.
За этой простотой скрывается большая проблема – если DLL удалить или испортить, то программа просто не запустится.
2. Динамическая загрузка.
Этот способ – более продвинутый, но требует написания значительно большего количества кода:
Var
CalcFee: function(const iID: Integer): Currency;
hDLL: THandle;
begin
hDLL := LoadLibrary('TestDLL.dll');
If hDLL <> 0
then try
@CalcFee := GetProcAddress(hDLL, 'CalcFee');
If @CalcFee = nil
then ShowMessage('В TestDLL.dll не найдена функция CalcFee')
else fFee := CalcFee(1234);
finally
FreeLibrary(hDLL);
end
else ShowMessage('Ошибка при загрузке TestDLL.dll')
В этом коде используются функции Win32 API из Windows.pas:
- LoadLibrary – загружаем DLL;
- GetProcAddress – получаем адрес функции по её имени;
- FreeLibrary – выгружаем DLL из памяти.
Динамическая загрузка позволяет загружать DLL только при необходимости и выгружать её, если она больше не нужна. Второе преимущество и более важное – это возможность обработать ошибки при загрузке DLL и вызове функции. Поэтому даже без DLL или с испорченной DLL программа будет работать. Если перед загрузкой DLL ее удалить, то получим нормальное сообщение:
3. Отсроченная загрузка
В Delphi 2010 появился третий способ вызова функций из DLL – отсроченная загрузка. Как и динамическая загрузка, она позволяет загружать DLL только при необходимости (например, для экономии ресурсов), но также как при статической загрузке, функцию достаточно лишь описать, добавив директиву delayed:
Function CalcFee(const iID: Integer): Currency; external 'TestDLL.dll' delayed;
Недостатки этого способа – это то, что DLL уже нельзя выгрузить и то, что если при вызове функции произойдет ошибка, то пользователь не узнает ее причину. В справке написано "Trying to call a delayed routine that cannot be resolved results in a run-time error (or an exception, if the SysUtils unit is loaded)". Если DLL удалить или если в DLL нет нужной функции, сообщение будет однотипным:
Если функция будет использована в консольной программе, то сообщение об ошибке будет тоже не информативным:
"Runtime error 255 at 7C812AFB "
Но спасение есть. При желании ошибки, возникающие во время загрузки DLL или вызове её функций можно перехватить и обработать с помощью процедур SetDliNotifyHook и SetDliFailureHook из юниты System.
Не знаю, как вам, но мне идея отсроченной загрузки DLL понравилась, и я буду её в будущем использовать.
24 марта 2010
Delphi 2010: TDataSet.FreeBookmark – рудимент
Часто бывает полезно отметить текущее положение курсора в DataSet'е так, чтобы позже можно было быстро возвратиться к этому месту. Delphi обеспечивает эту функциональную возможность с помощью закладок (Bookmark), для работы с которыми используются процедуры:
Var
q: TSDQuery;
bm: TBookmark;
begin
Try
bm := q.GetBookmark; // делаем закладку
// обрабатываем ds
Finally
If bm <> nil then
begin
If q.BookmarkValid(bm) then
q.GotoBookmark(bm);
q.FreeBookmark(bm);
end;
End;
end;
В Delphi 7 код FreeBookmark выглядит так:
procedure TDataSet.FreeBookmark(Bookmark: TBookmark);
begin
FreeMem(Bookmark);
end;
В Delphi 2010 он – просто заглушка:
procedure TDataSet.FreeBookmark(Bookmark: TBookmark);
begin
// No longer need to free bookmark since it's a TBytes now.
end;
Т.е. FreeBookmark в Delphi 2010 – это уже пережиток прошлого и вызывать его больше не нужно. Таким образом, исходный код становится проще:
Var
ds: TSDQuery;
bm: TBookmark;
begin
Try
bm := q.GetBookmark; // делаем закладку
// обрабатываем ds
Finally
If (bm <> nil) and q.BookmarkValid(bm) then
q.GotoBookmark(bm);
End;
end;
P.S. А вот справочную систему Delphi 2010 подправить, как всегда, забыли. В ней есть раздел "DB.TDataSet.FreeBookmark" с описанием процедуры и рекомендацией ее использования. А раздел "Marking and Returning to Records" содержит строку "FreeBookmark frees the memory allocated for a specified bookmark when you no longer need it. You should also call DB.TDataSet.FreeBookmark before reusing an existing bookmark." и пример, где используется FreeBookmark :)
- GetBookmark – устанавливает закладку на текущую запись;
- BookmarkValid – проверяет, существует ли запись, на которую ссылается закладка;
- GotoBookmark – позиционирует курсор на запись, на которую ссылается закладка;
- FreeBookmark – освобождает системные ресурсы, используемые методом GetBookmark.
Var
q: TSDQuery;
bm: TBookmark;
begin
Try
bm := q.GetBookmark; // делаем закладку
// обрабатываем ds
Finally
If bm <> nil then
begin
If q.BookmarkValid(bm) then
q.GotoBookmark(bm);
q.FreeBookmark(bm);
end;
End;
end;
В Delphi 7 код FreeBookmark выглядит так:
procedure TDataSet.FreeBookmark(Bookmark: TBookmark);
begin
FreeMem(Bookmark);
end;
В Delphi 2010 он – просто заглушка:
procedure TDataSet.FreeBookmark(Bookmark: TBookmark);
begin
// No longer need to free bookmark since it's a TBytes now.
end;
Т.е. FreeBookmark в Delphi 2010 – это уже пережиток прошлого и вызывать его больше не нужно. Таким образом, исходный код становится проще:
Var
ds: TSDQuery;
bm: TBookmark;
begin
Try
bm := q.GetBookmark; // делаем закладку
// обрабатываем ds
Finally
If (bm <> nil) and q.BookmarkValid(bm) then
q.GotoBookmark(bm);
End;
end;
P.S. А вот справочную систему Delphi 2010 подправить, как всегда, забыли. В ней есть раздел "DB.TDataSet.FreeBookmark" с описанием процедуры и рекомендацией ее использования. А раздел "Marking and Returning to Records" содержит строку "FreeBookmark frees the memory allocated for a specified bookmark when you no longer need it. You should also call DB.TDataSet.FreeBookmark before reusing an existing bookmark." и пример, где используется FreeBookmark :)
16 марта 2010
Delphi 2010: TValue - "тормоз"!
Перевод. Оригинал "TValue is very slow" (© TURBU Tech) дополнен моими тестами и комментариями.
Справочная система Delphi 2010 описывает тип TValue, используемый модулем RTTI для хранения значений произвольных типов, как "облегченная версия типа Variant". Увидев это, я задался вопросом, насколько он легковеснее? Как быстро работает TValue?
К счастью, среди известных мне новых возможностей Delphi 2010 – модуль диагностики (Diagnostics), который предоставляет нам объект TStopwatch – простой таймер позволяющий засечь время выполнения операций и тем самым облегчить написание простого теста скорости.
Я ожидал, что скорость работы TValue будет сравнима со скоростью Variant, или возможно немного больше. Для проверки, я написал следующую программку:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, rtti, diagnostics;
const
HUNDRED_MILLION = 100000000;
procedure tryTValue;
var
i: integer;
j: TValue;
value: integer;
begin
for I := 1 to HUNDRED_MILLION do
begin
j := i;
value := j.AsInteger;
end;
end;
procedure tryVariants;
var
i: integer;
j: variant;
value: integer;
begin
for I := 1 to HUNDRED_MILLION do
begin
j := i;
value := j;
end;
end;
var
stopwatch: TStopWatch;
begin
try
stopwatch := TStopWatch.StartNew;
tryVariants;
stopwatch.Stop;
writeln('Variants: ', stopwatch.ElapsedMilliseconds);
stopwatch := TStopWatch.StartNew;
tryTValue;
stopwatch.Stop;
writeln('TValues: ', stopwatch.ElapsedMilliseconds);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
Конечно, этот тест - не исчерпывающая проверка возможностей TValue, но результаты поучительны. Когда я запустил его на своем рабочем компьютере (высокопроизводительный ноутбук Alienware), тест Variant выполнился почти мгновенно, а тест TValue выполнялся так долго, что я решил, что он завис и остановил его.
Затем я запустил тест снова и получил следующие результаты (в миллисекундах):
От меня.
Вообще то 31131 поделить на 717 будет равно 43.41841004 ;)
Что бы проверить результат, я запустил несколько тестов на своей домашней "dev machine".
Тест #1. Запустил исходный тест и получил такую же разницу скорости - в 43.17657992 раза:
Тест #4. В процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsOrdinal"
Тест #5. AsOrdinal возвращает значение типа Int64, поэтому в процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsInt64" и получил падение скорости в 54,25981308 раза!!!
Напоследок, я проверил с помощью функции SizeOf число байт, которые занимали переменные: переменная типа Variant занимала – 24 байта, а TValue – всего 16. Может в этом проявляется "облегченность" типа TValue? Тогда, храните числа в integer – они будут занимать 4 байта ;)
Справочная система Delphi 2010 описывает тип TValue, используемый модулем RTTI для хранения значений произвольных типов, как "облегченная версия типа Variant". Увидев это, я задался вопросом, насколько он легковеснее? Как быстро работает TValue?
К счастью, среди известных мне новых возможностей Delphi 2010 – модуль диагностики (Diagnostics), который предоставляет нам объект TStopwatch – простой таймер позволяющий засечь время выполнения операций и тем самым облегчить написание простого теста скорости.
Я ожидал, что скорость работы TValue будет сравнима со скоростью Variant, или возможно немного больше. Для проверки, я написал следующую программку:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, rtti, diagnostics;
const
HUNDRED_MILLION = 100000000;
procedure tryTValue;
var
i: integer;
j: TValue;
value: integer;
begin
for I := 1 to HUNDRED_MILLION do
begin
j := i;
value := j.AsInteger;
end;
end;
procedure tryVariants;
var
i: integer;
j: variant;
value: integer;
begin
for I := 1 to HUNDRED_MILLION do
begin
j := i;
value := j;
end;
end;
var
stopwatch: TStopWatch;
begin
try
stopwatch := TStopWatch.StartNew;
tryVariants;
stopwatch.Stop;
writeln('Variants: ', stopwatch.ElapsedMilliseconds);
stopwatch := TStopWatch.StartNew;
tryTValue;
stopwatch.Stop;
writeln('TValues: ', stopwatch.ElapsedMilliseconds);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
Конечно, этот тест - не исчерпывающая проверка возможностей TValue, но результаты поучительны. Когда я запустил его на своем рабочем компьютере (высокопроизводительный ноутбук Alienware), тест Variant выполнился почти мгновенно, а тест TValue выполнялся так долго, что я решил, что он завис и остановил его.
Затем я запустил тест снова и получил следующие результаты (в миллисекундах):
Variants: 717По крайней мере, для этой конкретной операции, TValue в 43.52167832167832 раза медленнее, чем Variant!
TValues: 31131
От меня.
Вообще то 31131 поделить на 717 будет равно 43.41841004 ;)
Что бы проверить результат, я запустил несколько тестов на своей домашней "dev machine".
Тест #1. Запустил исходный тест и получил такую же разницу скорости - в 43.17657992 раза:
Variants: 538Тест #2. Переставил местами вызов tryTValue и tryVariants и снова получил 43.376404494:
TValues: 23229.
TValues: 23163Тест #3. Закомментировав обратное присвоение TValue и Variant целой переменной ("value := j.AsInteger" и "value := j") я получил для TValue более "веселый" результат:
Variants: 534.
Variants: 535Присвоение целого значения TValue медленнее присвоения целого значения Variant всего в 9.65981308 раза. А значит основное падение скорости вызвано AsInteger.
TValues: 5168
Тест #4. В процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsOrdinal"
Variants: 536В результате общее падение скорости всего в 10.93656716 раза!
TValues: 5862
Тест #5. AsOrdinal возвращает значение типа Int64, поэтому в процедуре tryTValue я заменил "value := j.AsInteger" на "value := j.AsInt64" и получил падение скорости в 54,25981308 раза!!!
Variants: 535Вывод: "value := j.AsOrdinal" у TValue работает почти так же быстро, как и "value := j" для Variant. А методы AsInteger и AsInt64 – лучше не использовать. Но все равно, главный вывод: TValue – "тормоз"!
TValues: 29029
Напоследок, я проверил с помощью функции SizeOf число байт, которые занимали переменные: переменная типа Variant занимала – 24 байта, а TValue – всего 16. Может в этом проявляется "облегченность" типа TValue? Тогда, храните числа в integer – они будут занимать 4 байта ;)
04 марта 2010
Delphi 2010: Сносим назойливый Code Formatter
В далекие школьные годы у нас в школе стоял компьютер ДВК-2М. По сравнению с общераспространенными БК - это было чудо техники, с нормальным монитором и даже с винчестером. На нем я писал свои первые программки. Однажды, когда я писал очередной "шедевр", в соседний кабинет вошла уборщица и включила свет. Этот щелчок включателя я запомнил надолго. При включении света, компьютер моргнул экраном и начал перезагружаться, унеся с собой больше часа моей работы. Это послужило для меня уроком. С тех пор, я регулярно сохраняюсь и, раз в несколько минут, мои руки автоматически жмут Ctrl+S.
Когда я начал писать свою первую программу на Delphi 2010, я случайно промахнулся и нажал Ctrl+D вместо Ctrl+S. Каково было мое изумление, когда я заметил, что код программы стал выглядеть совсем не так, как я привык его форматировать за свои 14 лет работы с Delphi.
Когда я начал писать свою первую программу на Delphi 2010, я случайно промахнулся и нажал Ctrl+D вместо Ctrl+S. Каково было мое изумление, когда я заметил, что код программы стал выглядеть совсем не так, как я привык его форматировать за свои 14 лет работы с Delphi.
Подписаться на:
Сообщения (Atom)