OS X の共有ライブラリ
OS X アプリケーション開発 への移動
目次
ライブラリを OS X アプリケーションで共有する
Delphi でマルチデバイスの共有ライブラリを開発する際は、プラットフォーム固有のいくつかの要件に従う必要があります。特に、OS X をターゲットとするライブラリで動的に読み込まれ呼び出されることを想定している関数の場合は、以下を行う必要があります。
- 動的に読み込まれる関数の名前をアンダースコア('_')で始める。
- "exports" 文を .dpr ファイルから .pas ファイルに移動する。
このトピックでは、これら 2 つの推奨事項についてさらに説明します。
動的に読み込まれる関数の OS X 版の名前をアンダースコア('_')で始める
これは、OS X でエクスポートされる関数の名前は必ずアンダースコア('_')で始めるという、OS X ターゲット プラットフォームの命名規則に従う必要があるという意味です。
標準ライブラリ関数 dlsym()
(WinAPI の GetProcAddress()
とほぼ同等)では動的にアクセスされる関数の名前の先頭にアンダースコアを付けるため、OS X アプリケーションの場合には、先頭のアンダースコアが必要になります。ライブラリは期待どおりに作成され、コンパイル時に静的にリンクされますが、その中の関数は、プログラム内で動的に呼び出されたときに、関数名の先頭にこのアンダースコアがない限り、アクセスできません。
exports 文は .dpr ファイルでは認識されない
OS X をターゲットとするライブラリの場合は、関係のある "exports" 文を、ライブラリに使用される .dpr ファイルから、エクスポートされる関数を定義する .pas ファイルに移動します。
詳細は、「動的に読み込まれるライブラリの作成」および「モジュール定義ファイル」を参照してください。
コード例
たとえば、以下のユニットは、関数を次のようなプラットフォーム固有の関数名でエクスポートするように設計されています。
- Windows の場合:
TestProc()
- OS X の場合:
_TestProc()
unit uTestLib;
interface
{$IFDEF MACOS}
function _TestProc(var I: Integer): Integer; cdecl;
{$ELSE}
function TestProc(var I: Integer): Integer; cdecl;
{$ENDIF}
exports
{$IFDEF MACOS}
_TestProc;
{$ELSE}
TestProc;
{$ENDIF}
implementation
{$IFDEF MACOS}
function _TestProc(var I: Integer): Integer; cdecl; export;
{$ELSE}
function TestProc(var I: Integer): Integer; cdecl; export;
{$ENDIF}
begin
Result := I;
end;
end.
OS X でこの関数が以下のいずれかを使用して参照される場合は、
System.SysUtils.GetProcAddress()
Posix.Dlfcn.dlsym()
先頭のアンダースコア('_')は使用されません。OS X では、動的に読み込まれる関数の名前の先頭には必ずアンダースコアがあると見なし、それを自動的に名前に付加するからです。
メモ: この要件は、動的に読み込まれる関数にのみ影響を及ぼし、ソースコードで宣言に "external" 指令が付いている静的リンク関数には影響しません。ライブラリ内の静的リンク関数も名前の先頭にアンダースコアを付けることができ、OS X ではそれがよく行われますが、動的に読み込まれる関数の場合とは異なり、それは必須ではありません。
また、OS X をターゲットとするライブラリについては、"exports" 句がライブラリのメイン ソース ファイル(.dpr ファイル)に記述された場合にはそれが認識されないことにも注意してください。エクスポートされる関数の "exports" 句は、上記の例のように、ユニット ファイルに記述される必要があります。
その他の考慮事項
ライブラリは実行時パッケージを使ってビルドする
ライブラリ(DLL)もアプリケーションも、実行時パッケージを使ってビルドする必要があります。そうでない場合は、Platform や Application のような共通グローバル オブジェクトの独自のコピーがそれぞれに含まれることになります。
LoadLibrary を呼び出す
アプリケーションの起動後、LoadLibrary を呼び出す必要があります。ユニットの初期化中に DLL を読み込んでも、うまくいきません。