共有ライブラリは、コンパイルされたコードで、実行可能ファイルを走らせるとき、ロードされ、後で使われることができます。
コンパイラが、実行可能ファイルを作るときに、最初に、基本のソースファイルが、オブジェクト・ファイルに変換されます。
関係するオブジェクト・ファイルは、実行ファイルを作るために、一緒に結合されます。
共有ライブラリが、共有ライブラリの中にオブジェクト・ファイルを含むという点で、静的ライブラリに、似ています。
しかし、共有ライブラリは、実行可能ファイルが走るときだけ、共有ライブラリがロードされる点で、実行可能ファイルにも似ています。
このライブラリは、「共有」と呼ばれます。というのは、ライブラリのコードは、実行時に、実行可能ファイルによって、ロードされますが、複数の実行可能ファイルによって同時にロードされることもできます。このとき、共有ライブラリは、一つあるだけでよいのです。
一度、ライブラリを作っておけば、次に、私たちは、まるでまさしく直接私たちのプログラムで、ライブラリが含むコードのソースをコンパイルしているように、使用できます。
目次:
0. 共有ライブラリと変数を共有
1. 共有ライブラリの例
2. Windows で共有ライブラリを使う
3. リナックスで共有ライブラリを使う
Using Shared Libraries on DOS => see its own page:
Shared Libraries - DOS
4. シンボルをエクスポートする実行ファイル
5. 動的に共有ライブラリをロードする
6. Windows での共有ライブラリとの変数の交換/共有
7. Windows での定義ファイルからのインポート ライブラリの作成、または DLL ファイルからのインポート ライブラリまたは定義ファイルの作成
0. 共有ライブラリと変数を共有
共有ライブラリでは、
Common や
Extern キーワードを使って変数を直接共有することはできません。
それ以外では、ライブラリ手続きにパラメータ(値または参照)を渡したり、ライブラリ関数から変数(値または参照)を返すことで、共有ライブラリと間接的にデータを交換できます。
1. 共有ライブラリの例
下の 3 つのファイルを使って、共有ライブラリを作成する、簡単な例を紹介します:
- mylib2.bas - ライブラリのためのソース → mylib2.dll、libmylib2.dll.a
- mylib2.bi - ライブラリへのヘッダー
- mytest2.bas - テスト・プログラム →mytest2.exe
この例のライブラリは、一つの関数を提供する、一つのモジュールです:
'' mylib2.bas
'' 右のオプションでコンパイルします:fbc -dll mylib2.bas
'' 2つの数を合計して、結果を返します
Public Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer Export
Return( x + y )
End Function
ライブラリを、下のようにコンパイルしてください:
fbc -dll mylib2.bas
注意:FbEdit を使う場合は、オプションのリストから「Windows dll」を選択します。
-dll オプションは、コンパイラに、ソースコード mylib2.bas をとって、オブジェクトファイル mylib2.o に変換して、次に、オブジェクトファイルを、共有ライブラリに蓄えるように、伝えます。
共有ライブラリの名前は、プラットホーム依存して、linux の場合は、.so 拡張子に、Windows の場合は、.dll 拡張子に、なります。
ライブラリは、一般に、それぞれ多くの関数を持つ、多くのモジュール(ソースファイル)を含みます。しかし、この簡単な例では、モジュールと関数は、それぞれ1つだけです。
共有ライブラリを作るのは、
Export 宣言指定子を追加する以外は、静的ライブラリを作るのと、ほとんど同じです。
Export は、共有ライブラリをロードする他の実行可能ファイルに、関数を見えるようにするように、コンパイラに、伝えまます。
他のソースコードでライブラリを使うためには、何がライブラリにあるかをコンパイラに伝える手段が必要です。
何がライブラリにあるかをコンパイラに伝えるために、ヘッダーファイルの、ライブラリの宣言(インタフェース、とか API と呼ばれる)があります。
'' mylib2.bi
#inclib "mylib2"
Declare Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer
ヘッダーを、コンパイルする必要は、ありません。
ヘッダーは、他のソース・ファイルに含めることができるように、ソースの形式がよいのです。
#inclib 命令文は、実行可能ファイルを走らせるときに、リンクする必要がある、共有ライブラリの名前を、コンパイラに、伝えます。
ここで作ったライブラリ(.dll/.so ファイル) と、ヘッダー(.bi ファイル) を使って、テスト・プログラムで試すことができます:
'' mytest2.bas
'' 右のように、コンパイルします:fbc mytest2.bas
#include once "mylib2.bi"
Print Add2
(1,2)
Sleep
#include 命令文は、
mylib.bi からソースコードを含めるように、コンパイラに伝えます。まるで、私たちが、元のソースに、
mylib.bi の内容を、タイプしたかのようになります。
インクルード・ファイルを書いた方法で、インクルード・ファイルは、ライブラリに関して知らなければならないすべてを、コンパイラに伝えます。
これを、下のようにコンパイルします:
fbc mytest2.bas
そして、
mytest 実行可能ファイルを実行すると、下の結果が表示されます:
3
ライブラリを作るとき、複数のソース・モジュールを使うことができます。
そして、基本プログラムは、それぞれの必要なヘッダーを含めることによって、複数のライブラリを使用できます。
ライブラリの中には、いくつものヘッダーを使う、大きなものもあります。
非常に大きいプロジェクトでは、めったに変更しない、いくつかのコード・モジュールを、共有ライブラリとして作っておくと、コンパイル時間と、リンク時間を、劇的に減らすことができます。
共有ライブラリは、任意で、
-g コマンドライン・オプションを指定するとこによって、デバッグ情報を含めるようにすることができます。
共有ライブラリには、オプションとして、モジュールコンストラクタ、メインコード、モジュールデストラクタを含めることができます。モジュールコンストラクタ、メインコードの順にライブラリロード時に実行されます。モジュールデストラクタはライブラリのアンロード時に実行されます。
オブジェクトファイル、およびしたがって、共有ライブラリは、プラットホームで固有です。そして、いくつかの場合、コンパイラのバージョンと、FreeBASIC ランタイム・ライブラリに、特定されます。
2. Windows で共有ライブラリを使う
Windows では、共有ライブラリ(DLL)を使う実行可能ファイルが、実行時に、共有ライブラリを見つけられる場所に、それを格納しなければなりません。
オペレーティング・システムは、以下のディレクトリを捜します:
- 実行可能ファイルをロードしたディレクトリ。
- カレント・ディレクトリ。
- ウインドウズと、ウィンドウシステム・フォルダ。
- PATH 環境変数に記載された、ディレクトリ・リスト。
ディレクトリを探す順番は、使用中の Windows バージョンによって異なります。そして、オペレーティング・システムが構成している設定によっても、変わります。
3. リナックスで共有ライブラリを使う
デフォルトで、Linux は、通常、カレントディレクトリ や、実行可能ファイルがロードされたディレクトリを、捜しません。
以下のどちらかが必要です:
-
.soファイルを、共有ライブラリ(/例えば、/usr/lib)を持っているディレクトリに、コピーします。そして、ldconfig を実行して、ライブラリを構成します。
-
環境変数 LD_LIBRARY_PATH を変更して、カレントディレクトリか、新たに作成された共有ライブラリの特定のディレクトリを、捜すようにします。
実行可能ファイル
./mytest/ を走らせて、一時的にカレント・ディレクトリを捜すように linux に伝えるには、下のシェル・コマンドを使ってください:
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./mytest
4. シンボルをエクスポートする実行ファイル
実行可能ファイルが、共有ライブラリがロードされるとき、他の共有ライブラリに使われるシンボルを持っている場合は、
Export 手続き宣言指定子を使います。そして、実行可能ファイルを作る(結合する)ときに、
-export コマンドラインを使います。
5. 動的に共有ライブラリをロードする
共有ライブラリは、プログラムの実行時に、ライブラリとそのシンボルをロードすることによって、動的に、ロードして、使用されます。
-
Dylibload は、共有ライブラリへのハンドルを、ロードして、取得するために使用できます。
-
Dylibsymbol は、ロードされた共有ライブラリの、シンボルのアドレスを取得するのに使用されます。
-
Dylibfree は、共有ライブラリが不要になったときに、共有ライブラリを降ろすのに使用されます。
共有ライブラリ中の手続きは、手続き定義の最初の行で
Export 指定子を使って、シンボル名が、共有ライブラリのエクスポート・テーブルに配置されるようにする必要があります。
'' mydll.bas
'' compile as: fbc -dll mydll.bas
'' This will create mydll.dll (and libmydll.dll.a import library) on Windows,
'' and libmydll.so on Linux.
''
'' Note: libmydll.dll.a is an import library, it's only needed when creating
'' an executable that calls any of mydll's functions, only distribute
'' the DLL files with your apps, do not include the import libraries,
'' they are useless to end-users.
'' Simple exported function; the <alias "..."> disables FB's default
'' all-upper-case name mangling, so the DLL will export AddNumbers() instead of
'' ADDNUMBERS().
Function AddNumbers Alias "AddNumbers"( ByVal a As Integer, ByVal b As Integer ) As Integer Export
Function = a + b
End Function
'' load.bas: Loads mydll.dll (or libmydll.so) at runtime, calls one of mydll's
'' functions and prints the result. mydll is not needed at compile time.
'' compile as: fbc test.bas
''
'' Note: The compiled mydll.dll (or libmydll.so) dynamic library is expected
'' to be available in the current directory.
'' Note we specify just "mydll" as library file name; this is to ensure
'' compatibility between Windows and Linux, where a dynamic library
'' has different file name and extension.
Dim As Any Ptr library = DyLibLoad( "mydll" )
If( library = 0 ) Then
Print "Failed to load the mydll dynamic library, aborting program..."
End 1
End If
'' This function pointer will be used to call the function from mydll, after
'' the address has been found. Note: It must have the same calling
'' convention and parameters.
Dim AddNumbers As Function( ByVal As Integer, ByVal As Integer ) As Integer
AddNumbers = DyLibSymbol( library, "AddNumbers" )
If( AddNumbers = 0 ) Then
Print "Could not retrieve the AddNumbers() function's address from the mydll library, aborting program..."
End 1
End If
Randomize Timer
Dim As Integer x = Rnd * 10
Dim As Integer y = Rnd * 10
Print x; " +"; y; " ="; AddNumbers( x, y )
'' Done with the library; the OS will automatically unload libraries loaded
'' by a process when it terminates, but we can also force unloading during
'' our program execution to save resources; this is what the next line does.
'' Remember that once you unload a previously loaded library, all the symbols
'' you got from it via dylibsymbol will become invalid, and accessing them
'' will cause the application to crash.
DyLibFree( library )