2019年7月10日水曜日

キャプチャソフト開発 その3 DLLを作ってみる

DLLってそもそも何?なんか難しそう...
と思ったが、全然そんなことはなかった。

主に参考にしたサイト

これらを参考に実装していきます。
本ページの目的を以下に示します。
  • DLLを明示的に呼び出す
  • DLLからHello Worldする

メインソースファイル
  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include "./util.h"
  4. int main(int argc, char* argv[]) {
  5. (void)argc;
  6. (void)argv;
  7. // Load DLL.
  8. HINSTANCE hDLL = LoadLibrary(L"util.dll");
  9. if (hDLL == NULL) {
  10. fwprintf(stderr, L"Failed to load DLL\n");
  11. return 1;
  12. }
  13. // Load DLL function.
  14. void (*func)() = NULL;
  15. func = (void (*)())GetProcAddress(hDLL, "HelloWorld");
  16. if (func == NULL) {
  17. fwprintf(stderr, L"Failed to get function\n");
  18. } else {
  19. func();
  20. }
  21. // Release DLL.
  22. FreeLibrary(hDLL);
  23. return 0;
  24. }

  • hDLL
    今回作成したDLLへのインスタンスハンドルです。
  • LoadLibraryFreeLibrary
    それぞれ、DLLのロードと解放を行う関数です。
  • GetProcAddress
    DLLから関数のアドレスを取得します。


DLLのソースファイル
  1. #define DLLAPI extern "C" __declspec(dllexport)
  2. #include "./util.h"
  3. #include <stdio.h>
  4. DLLAPI void HelloWorld() { wprintf(L"Hello World!\n"); }
DLLのヘッダファイル
  1. #ifndef _UTIL_H_
  2. #define _UTIL_H_
  3. #include <stdio.h>
  4. #ifndef DLLAPI
  5. #define DLLAPI extern "C" __declspec(dllimport)
  6. #endif
  7. DLLAPI void HelloWorld();
  8. #endif // _UTIL_H_
  • #define DLLAPI extern "C" __declspec(dllexport)
    DLLAPIマクロを、DLLのソースファイルではこのように定義しておきます。
    ヘッダファイルをインクルードする前に定義するのがポイントです。
  • #define DLLAPI extern "C" __declspec(dllimport)
    DLLAPIマクロを、DLLのヘッダファイルではこのように定義しておきます。
  • HelloWorld
    DLLに実装する、ハローワールドする関数です。
  • DLLMainは?
    今回のケースは簡単なので、書かなくても大丈夫です。
    作りたいプログラムによっては、あると便利な場合もあるのでしょう。
DLLAPIマクロのからくりについては、参考サイトの方で詳しく解説されています。

メイクファイル
  1. TARGET = main.exe
  2. PDB = main.pdb
  3. MAP = main.map
  4. RES =
  5. SRC = main.cc util.cc
  6. OBJ = $(OBJDIR)\main.obj
  7. DLL = util.dll
  8. OBJDIR = build
  9. CC = "$(VCINSTALLDIR)\cl.exe"
  10. LINK = "$(VCINSTALLDIR)\link.exe"
  11. CPPFLAGS = /nologo /W4 /Zi /O2 /MT /EHsc /Fd"$(TMPDIR)/" /DUNICODE /D_UNICODE \
  12. /DDEBUG
  13. LFLAGS = "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" \
  14. "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" \
  15. "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" "libcmt.lib" \
  16. /NOLOGO /SUBSYSTEM:CONSOLE /DEBUG
  17. ALL: $(TARGET)
  18. $(TARGET): $(OBJ) $(DLL) $(RES)
  19. $(LINK) $(LFLAGS) /OUT:$(TARGET) /PDB:"$(PDB)" /MAP:"$(MAP)" \
  20. $(OBJ) $(RES)
  21. .cc{$(OBJDIR)}.obj:
  22. @[ -d $(OBJDIR) ] || mkdir $(OBJDIR)
  23. $(CC) $(CPPFLAGS) /Fo"$(OBJDIR)\\" /c $<
  24. .cc.dll:
  25. $(CC) $(CPPFLAGS) /LD $<
  26. clean:
  27. rm $(OBJ) $(TARGET) *.map *.pdb *.ilk *.obj *.lib *.dll *.exp

  • SUBSYSTEM:CONSOLE
    コンソールアプリケーションとしてビルドすることを、リンカオプションで設定します。
  • .cc.dll
    CCファイルからDLLファイルを作るルールを指定します。
    clコンパイラでは、LDオプションを付けることでDLLを作れます。
DLLを含むプロジェクトのためのMakefileは初出ですが、.cc.dllのルールが加わったこと以外、あまり変わりませんね。

備考

意外とあっさりしていて驚いた。
DLLを扱ったことで、Windowsに対する理解が深まった気がする。

プロジェクト一式については、GitHubにも上げておきます

0 件のコメント:

コメントを投稿

コメント表示は承認制に設定しています