技術記事

PDFlibPas: Delphi での DLL, ActiveX, and Dylib integration

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;