DelphiからDLLを動的にロードする場合、WindowsではLoadLibraryとGetProcAddressを使っていましたが、MacOSXのdylibではどうすればいいのか調べてました。
MacOSX(Posix)だとdlopen, dlsymを使うことになるらしいです。
dlopen(3) Mac OS X Developer Tools Manual Page
dlsym(3) Mac OS X Developer Tools Manual Page
System.SysUtilsのコードを見てみたら、どうやら最近のDelphiのバージョンでは、MacOSXでもLoadLibraryとGetProcAddressからこれらを使うようラップしているようでした。
IFDEFがたくさん書かれていて読みづらいですが、SafeLoadLibraryという関数がSysUtilsにあり、これはWindows, MacOSX両方で使用可能のようです。
System.SysUtils.SafeLoadLibrary - RAD Studio API Documentation
ただし、GetProcAddressについてはXE4の段階ではWindowsだとSysUtilsのほうには定義がなく、WinApi.Windows経由を使うことになりそうです。
また、dylibを作成する際MacOSXの場合は、exportsはユニット側に書き、アンダースコアを先頭に付けた名前でエクスポートしないと認識されないそうです。
クロスプラットフォームの共有ライブラリ - RAD Studio
この情報を基に、WindowsではDLLを、MacOSXではdylibを動的にロードするようなコードを書いてみます。
試したバージョンはDelphi XE4です。
DLL/dylib側
mylib.dpr
LIBPREFIXに空文字列を指定しているのは、dccosxだとデフォルトが"lib"になっていたためです。
library mylib; uses MyLibrary; {$LIBPREFIX ''} begin end.
MyLibrary.pas
足し算の結果を返すAdd関数と、メッセージボックスでテキストを表示するSay手続きをエクスポートしています。
呼び出し規約は他の言語からも使いやすいように、とりえあえずcdeclを指定しています。
Say関数では引数の文字列はUTF8エンコードされたバイト列のポインタを前提にしています。
unit MyLibrary; interface uses FMX.Dialogs; function Add(X, Y: Integer): Integer; cdecl; procedure Say(Text: PAnsiChar); cdecl; exports {$IFDEF MACOS} Add name '_Add', Say name '_Say' {$ELSE} Add, Say {$ENDIF} ; implementation function Add(X, Y: Integer): Integer; cdecl; begin Result := X + Y; end; procedure Say(Text: PAnsiChar); cdecl; begin ShowMessage(UTF8ToString(Text)); end; end.
動的にライブラリをロードするコード
dynamic_loading.dpr
コンソールアプリケーションで、SafeLoadLibraryを使ってライブラリをロードし、Add関数、Say手続きを呼び出しています。
MacOSの場合はmylib.dylib、Windowsの場合はmylib.dllを使います。
ファイルの文字コードはUTF8です(日本語を含むので)。
program dynamic_loading; {$APPTYPE CONSOLE} uses System.SysUtils {$IFDEF MSWINDOWS} , WinApi.Windows {$ENDIF} ; const {$IFDEF MacOS} LIB_NAME = 'mylib.dylib'; {$ELSE} LIB_NAME = 'mylib.dll'; {$ENDIF} type TAdd = function(X, Y: Integer): Integer; cdecl; TSay = procedure(Text: PAnsiChar); cdecl; var Handle: THandle; Add: TAdd; Say: TSay; begin Handle := SafeLoadLibrary(LIB_NAME); try if Handle <> 0 then begin @Add := GetProcAddress(Handle, 'Add'); if @Add <> nil then Writeln(Add(10, 20)); @Say := GetProcAddress(Handle, 'Say'); if @Say <> nil then Say('こんにちは'); end; finally FreeLibrary(Handle); end; end.
ソースコード
ソースコードとコンパイル済みのバイナリは以下のURLからダウンロードできます。
test_dynamic_loading.zip(3.9MB)