Техническая статья

PDFlibPas: DLL, ActiveX, and Dylib integration в Delphi

losLab PDF Library предоставляет командам Delphi и C++Builder PDF-движок с доступным исходным кодом для настольных, серверных, DLL, ActiveX и Dylib процессов, включая встроенные проверки PDF/A и PDF/UA, подписи PAdES и выбор рендерера без отправки документов во внешний PDF-сервис.

Эта статья предназначена для teams exposing PDF functionality across Delphi, C++Builder, scripting, legacy automation, or cross-platform components. Она рассматривает DLL, ActiveX, and Dylib integration как промышленную инженерию документов, а не как одиночный вызов компонента.

Практический риск состоит в том, что native integration bugs often appear as memory corruption, string encoding issues, bitness mismatches, or exception-boundary failures rather than clear PDF errors. Поэтому процессу нужны письменный контракт, наблюдаемая диагностика и реалистичные регрессионные файлы.

Архитектурные решения

Define a binary contract before feature code. calling convention, bitness, thread model, and supported host languages / string encoding, path encoding, stream ownership, and buffer lifetime rules

  • calling convention, bitness, thread model, and supported host languages
  • string encoding, path encoding, stream ownership, and buffer lifetime rules
  • error reporting style, exception translation, and diagnostic callback behavior
  • deployment layout, dependency versioning, registration, and update policy

Порядок реализации

Keep ownership and errors explicit at the boundary. The order below keeps the workflow reviewable for Delphi and C++Builder teams.

  1. publish a minimal binary contract before wrapping high-level PDF operations
  2. return explicit handles or result objects rather than sharing unmanaged pointers
  3. translate exceptions into stable error codes and diagnostic messages
  4. validate bitness and dependency versions during initialization
  5. ship sample calls that exercise Unicode paths, large buffers, and failure paths

Доказательства проверки

Integration evidence for support cases. Keep these fields with the output or support record.

  • module version, host process bitness, calling convention, and dependency path
  • function name, input sizes, output buffer ownership, and returned status code
  • encoded path or string policy used for the call
  • diagnostic trace that does not cross memory ownership boundaries unsafely

The PDF API is only half of the contract

A DLL, ActiveX, or Dylib layer needs stable calling conventions, buffer ownership rules, string encoding, version reporting, error codes, and deployment checks. Treating it as a thin wrapper without those rules makes support difficult.

Decision table for DLL, ActiveX, and Dylib integration

A decision table keeps product ownership visible when the same workflow is reused by a desktop tool, service job, and support utility.

DecisionEngineering reasonEvidence
calling convention, bitness, thread model, and supported host languagespublish a minimal binary contract before wrapping high-level PDF operationsmodule version, host process bitness, calling convention, and dependency path
string encoding, path encoding, stream ownership, and buffer lifetime rulesreturn explicit handles or result objects rather than sharing unmanaged pointersfunction name, input sizes, output buffer ownership, and returned status code
error reporting style, exception translation, and diagnostic callback behaviortranslate exceptions into stable error codes and diagnostic messagesencoded path or string policy used for the call

Замечания для инженерного ревью по DLL, ActiveX, and Dylib integration

Используйте эти замечания, чтобы убедиться, что функция вышла за рамки демо и может быть обоснована на релизе, в поддержке и при эскалации клиента

  • Решение: calling convention, bitness, thread model, and supported host languages. Точка приложения при реализации: return explicit handles or result objects rather than sharing unmanaged pointers. Доказательство приемки: encoded path or string policy used for the call. Триггер регрессии: uncaught native exceptions can terminate hosts that cannot inspect Delphi state
  • Решение: string encoding, path encoding, stream ownership, and buffer lifetime rules. Точка приложения при реализации: translate exceptions into stable error codes and diagnostic messages. Доказательство приемки: diagnostic trace that does not cross memory ownership boundaries unsafely. Триггер регрессии: ANSI paths may work in tests and fail for customer names or localized folders
  • Решение: error reporting style, exception translation, and diagnostic callback behavior. Точка приложения при реализации: validate bitness and dependency versions during initialization. Доказательство приемки: module version, host process bitness, calling convention, and dependency path. Триггер регрессии: ActiveX registration can succeed for one bitness and fail for another host
  • Решение: deployment layout, dependency versioning, registration, and update policy. Точка приложения при реализации: ship sample calls that exercise Unicode paths, large buffers, and failure paths. Доказательство приемки: function name, input sizes, output buffer ownership, and returned status code. Триггер регрессии: callbacks must not outlive buffers owned by the caller
  • Решение: calling convention, bitness, thread model, and supported host languages. Точка приложения при реализации: publish a minimal binary contract before wrapping high-level PDF operations. Доказательство приемки: encoded path or string policy used for the call. Триггер регрессии: uncaught native exceptions can terminate hosts that cannot inspect Delphi state

Пограничные случаи

  • ANSI paths may work in tests and fail for customer names or localized folders
  • ActiveX registration can succeed for one bitness and fail for another host
  • callbacks must not outlive buffers owned by the caller
  • uncaught native exceptions can terminate hosts that cannot inspect Delphi state

Примечания по Delphi / C++Builder

PDFlibPas should sit behind a small service boundary that receives files, streams, profiles, and credentials, then returns output paths, warnings, metrics, and validation status. Важные термины включают DLL, ActiveX, Dylib, calling convention, buffer ownership, Unicode path.

Пример кода Delphi

Следующий эскиз Delphi показывает практическую границу сервиса для этой темы. Оставляйте проверки политики, журналирование и валидацию вне узкого блока вызова продукта, чтобы сценарий было проще тестировать.

procedure LoadPdfEngineForHost(const LibraryPath: string);
begin
  RequireFileExists(LibraryPath);
  FEngine := TPDFlib.Create;
  FHostAdapter := CreateHostAdapter(FEngine);
  FHostAdapter.RegisterErrorCallback(LogPdfEngineError);
  FHostAdapter.RegisterBufferReleaseCallback(ReleaseReturnedBuffer);
end;

Производственный чек-лист

  • Запускайте сценарий на пустом файле, обычном клиентском файле и файле худшего случая
  • Открывайте сгенерированный PDF в целевом просмотрщике, валидаторе, принтере или downstream-приложении
  • Записывайте версию продукта, версию профиля, хэш входа, путь вывода, затраченное время и число предупреждений
  • Храните пароли, сертификаты, временные файлы и данные клиентов по явным правилам хранения
  • Добавляйте регрессионные документы, когда клиентский файл выявляет новый граничный случай

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

PDFlibPas

Дополнительные примеры кода

var
  Inst, Doc: Integer;
begin
  Inst := DLCreateLibrary;                       // one instance per worker thread
  try
    Doc := DLLoadFromFile(Inst, 'in.pdf', '');   // returns a DocumentID, 0 on failure
    if Doc <> 0 then
    begin
      DLEncrypt(Inst, 'owner-secret', 'user-secret', 3,
        DLEncodePermissions(Inst, 1, 0, 0, 0, 0, 0, 0, 1));
      DLSaveToFile(Inst, 'out.pdf');
    end;
  finally
    DLReleaseLibrary(Inst);                      // frees every document the instance owns
  end;
end;
var
  P: PWideChar;
  PageText: string;
begin
  P := DLGetPageText(Inst, 7);   // pointer into a library-owned buffer
  PageText := P;                 // copy now; a later call may reuse the buffer
end;