英語版
このページの英語版を見る

Windows DLLの作成

目次
  1. 参考文献
Windows DLL(共有ライブラリとも呼ばれる)は、プロセス間で実行コードとデータのインスタンスを共有する方法である。 Linux、OSX、FreeBSDなどの他のシステムにおける共有ライブラリと同じ役割を果たすが、実装方法は大きく異なる。 本記事の情報はWindows DLLに特化したものである。

DLLをビルドする

DLL用のコード

  1. mydll
  2. .
  3. dファイルを作成する
  4. :
    module mydll;
    
    import core.sys.windows.dll;
    import core.stdc.stdio;
    
    mixin SimpleDllMain;
    
    export void entry()
    {
        printf("called mydll.entry()\n");
    }
    
  5. DLLをコンパイルおよびリンクする
  6. :
  7. dmd -shared mydll
    
  8. これにより、mydll.lib (インポートライブラリ)mydll.dll (dll)のファイルが作成される
  9. mydll.diファイルを作成する
  10. :
  11. module mydll;
    
    export void entry();
    
  12. mydll.diファイルを作成する
  13. :
  14. module myexe;
    
    import mydll;
    
    int main()
    {
        mydll.entry();
        return 0;
    }
    
  15. myexe.d ファイルをコンパイルし、mydll.libファイルとリンクしてmyexe.exe ファイルを作成する:
    dmd myexe.d mydll.lib
    
  16. myexe
  17. を実行する
  18. :
  19. C:> myexe
    called mydll.entry()
    
  20. DllMain

    -

    エントリーポイント Windows DLLには

    、実行可能ファイルのmain 関数と同様に、エントリーポイントが必要である。 以下のような形になる。
    module dllmain;  // 常にこの名前を付けておくといい
    
    import core.sys.windows.windef : HINSTANCE, BOOL, DWORD, LPVOID;
    import core.sys.windows.winnt;
    import core.sys.windows.dll : dll_process_attach, dll_process_detach,
                                  dll_thread_attach, dll_thread_detach;
    
    __gshared HINSTANCE g_hInst;  // DLLのインスタンスハンドルを保存する
    
    /***********************************
     * DLLのエントリーポイント。
     * Params:
     *      hInstance = DLLのインスタンスハンドル
     *      ulReason = なぜDllMainが呼び出されるのか
     *      fImpLoad = Dllが明示的にロードされた場合はnull、暗黙的にロードされた場合は!null
     * Returns:
     *      成功ならtrue、失敗ならfalse
     */
    extern (Windows)
    BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID fImpLoad)
    {
        switch (ulReason)
        {
            case DLL_PROCESS_ATTACH: // DLLが最初にロードされたとき
                g_hInst = hInstance;  // 後で使うために保存しておく
                return dll_process_attach(hInstance, true); // プロセスに関連した初期化を行う
    
            case DLL_PROCESS_DETACH: // DLLがアンロードされるとき
                return dll_process_detach(hInstance, true); // プロセス相対ティアダウンを実行する
    
            case DLL_THREAD_ATTACH:  // 新しいスレッドの初期化を行う
                return dll_thread_attach(true, true); // スレッド相対初期化を実行する
    
            case DLL_THREAD_DETACH:  // スレッドが終了する
                return dll_thread_detach(true, true); // スレッドのティアダウンを実行する
    
            default:
                assert(0);
        }
    }
    
    あるいは、これは単なる定型コードなので、以下のようにしてもよい。
    module dllmain;
    
    import core.sys.windows.dll;
    
    mixin SimpleDllMain;
    
    コンパイラはDllMain を認識し、__acrtused_dll への参照を発行する。これにより、 CランタイムライブラリからDLLサポートコードが取り込まれる。また、 デバッグランタイムライブラリ (シンボリックデバッグコンパイル用)またはデフォルトランタイムライブラリ(それ以外の場合)が リンカーによって検索される。

    定義のエクスポート

    実行可能ファイルがDLL内の名前を参照

    できるようにするには

    、その名前がDLLによってエクスポートされていなければならない。 例えば、このモジュールからシンボルfunc をエクスポートするには、
    module mydll;
    export int func() { return 3; }
    
    コンパイラはオブジェクトファイルに以下のエクスポート定義指令を挿入する。
    EXPDEF expflag=x00, export '__D5mydll4funcFZi', internal '', ordinal=x0
    
    OMFファイル用、およびMSCOFFオブジェクトファイル用の同等物。EXPDEF は、mydll.func がリンクされるDLLのエクスポートテーブルに置かれるべきであることをリンカーに通知する。 オブジェクトファイルへの追加はこれだけである。

    宣言のインポート EXEファイル

    にDLLが添付される場合、

    EXEファイル

    はDLLを呼び出す方法を知っておく必要がある。これは DLLからの宣言のインポートと呼ばれる。インポートファイルを用意するmydll.di :
    module mydll;
    export int func(); // 関数本体がないことに注意する
    
    module myexe;
    import mydll;
    
    int test() { return func(); }
    
    myexe.d をコンパイルすると、マジックが明らかになる。
    extrn   __imp___D5mydll4funcFZi
    
    __D5myexe4testFZi       comdat
            call    dword ptr __imp___D5mydll4funcFZi
            ret
    
    mydll.func() への直接呼び出しは行われず、代わりにmydll.func() への間接呼び出しがmydll.func() へのポインタを介して行われ、そのポインタの名前は__imp___D5mydll4func である

ライブラリをインポートする

dllのオブジェクトファイルから定義をエクスポートし、exeファイルをdllのエクスポートにフックするには、 追加のファイルであるインポートライブラリが必要となる。インポートライブラリは dllがリンクされた際にリンカーによって自動的に作成される。このライブラリは、 実行可能ファイルをリンクする際にリンクステップに追加する必要がある。

参考文献

  1. Win32 DLLs in D
  2. ダイナミックリンクライブラリ ベストプラクティス