SmartQuery /ВОПРОС → ПРИМЕР → ПОНЯТНЫЙ ОТВЕТ
Языки и платформы

Delphi: два способа вызвать функцию из DLL

Delphi может вызывать экспортированные функции Windows DLL. Есть два разных способа: объявление external и явная загрузка через API Windows. Это альтернативы: после external не требуется вручную получать адрес той же функции.

Статический импорт через external

Для консольного проекта Windows объявите импорт до основного begin. В примере ReadSystemTicks — локальное имя функции, GetTickCount64 — имя экспорта в системной DLL.

program StaticDllDemo;
{$APPTYPE CONSOLE}

function ReadSystemTicks: UInt64; stdcall;
  external 'kernel32.dll' name 'GetTickCount64';

begin
  Writeln(ReadSystemTicks);
end.

Программа выводит число миллисекунд с запуска Windows. Оно меняется; это не календарная дата. При обычном external загрузчик разрешает импорт при старте. Если библиотека или экспорт отсутствуют, программа может не запуститься.

Явная загрузка и проверка ошибки

Этот отдельный консольный пример рассчитан на современную Windows и Delphi с Winapi.Windows. Загрузка ограничена системным каталогом; функция вызывается только при успешном получении адреса.

program DynamicDllDemo;
{$APPTYPE CONSOLE}

uses System.SysUtils, Winapi.Windows;

type
  TReadTicks = function: UInt64; stdcall;
var
  Lib: HMODULE;
  ReadTicks: TReadTicks;
begin
  Lib := LoadLibraryEx('kernel32.dll', 0,
    LOAD_LIBRARY_SEARCH_SYSTEM32);
  if Lib = 0 then
    RaiseLastOSError;
  try
    @ReadTicks := GetProcAddress(Lib, 'GetTickCount64');
    if not Assigned(ReadTicks) then
      RaiseLastOSError;
    Writeln(ReadTicks());
  finally
    FreeLibrary(Lib);
  end;
end.

После FreeLibrary вызывать сохранённый указатель нельзя. Для сторонней DLL выбирайте доверенный путь и учитывайте загрузку зависимостей. Наличие DLL не означает безопасность её кода.

Почему правильная сигнатура важнее приведения типа

  • Разрядность DLL и процесса должна совпадать.
  • Тип результата, порядок и типы параметров, соглашение о вызовах должны соответствовать объявлению экспорта.
  • Имя для GetProcAddress чувствительно к регистру; внутреннее имя функции может отличаться от экспортированного.
  • Строки, память и объекты через границу DLL требуют явного соглашения о владении; не передавайте Delphi-объекты в произвольный C API.

Сначала проверьте простой вызов с известным результатом. Падение после возврата часто связано с несовместимой сигнатурой; повторная загрузка библиотеки эту ошибку не исправляет.

Документация