読者です 読者をやめる 読者になる 読者になる

DelphiでDLLとdylibを動的にロードする

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.

実行結果

Windows7(32bit)

MacOSX 10.6

ソースコード

ソースコードコンパイル済みのバイナリは以下のURLからダウンロードできます。
test_dynamic_loading.zip(3.9MB)