小さい頃はエラ呼吸

いつのまにやら肺で呼吸をしています。


Visual C++ 2013でDLLを作成して動的読み込みしてみる。

はじめに

Visual Studio Express 2013 for Windows DesktopでDLLを作成して、コンソールアプリケーションからDLLを動的読み込みして、関数を実行してみたいと思います。

新版 明解C言語 入門編
柴田望洋
ソフトバンククリエイティブ
売り上げランキング: 2,942

DLLの作成

VS2013で空のプロジェクトを作成して、DLLを選択します。
f:id:replication:20140502093452p:plain
今回は、渡されたアドレスに対して「Hello World!」と書き込む関数を作ってみます。(GetHello関数)
GetHello関数のサンプルプログラムは、以下のようなコードです。

#include "stdafx.h"

int GetHello(char *message)
{
  memcpy(message, "Hello World!", 12);
  return 0;
}

続いて、作成したGetHello関数は、外部に対して公開しなければならないため、.defファイルを作ります。
f:id:replication:20140502093852p:plain
defファイルの中には、以下のような定義を行います。LIBRARYにはDLLの名前を指定し、EXPORTSセクションで外部に対して公開する関数名を指定します。

LIBRARY HelloWorld
EXPORTS
  GetHello @1

作成したdefファイルは、「リンカ」→「入力」→「モジュール定義」のところで、ファイル名を指定します。
f:id:replication:20140502094232p:plain

ソリューションをリビルドして、DLLができたら、以下のコマンドを実行して関数が見えるかどうかを確認します。

dumpbin /exports HelloWorld.dll

GetHello関数が参照できればOKです。
f:id:replication:20140502094810p:plain

DLLのロードと関数の呼び出し

DLLの読み込みには、LoadLibrary関数を使います。DLLが見つからない場合などは、ハンドルがNULLで返ってくるので、エラーハンドリングを行います。

  // DLLのロード
  HMODULE hModule = LoadLibrary(_T("HelloWorld.dll"));
  if (hModule == NULL)
  {
    printf("%s", "DLLのロードに失敗しました。");
    return 0;
  }

LoadLibraryが正常にいったら、次に呼び出す関数のアドレスを取得する必要があります。関数のアドレスを取得するには、GetProcAddress関数を使います。
GetProcAddressに失敗した場合は、必ずFreeLibraryを呼び出し、DLLを解放します。

  // 関数のアドレス取得
  FUNC lpFunc = (FUNC)GetProcAddress(hModule, "GetHello");
  if (lpFunc == NULL)
  {
    printf("%s", "関数のアドレス取得に失敗しました。");
    FreeLibrary(hModule);
    return 0;
  }

GetHello関数の呼び出すには、まずtypedefでGetHello関数を定義します。

typedef int (*FUNC)(char *);

続いて、GetProcAddressから取得したアドレスに対して、引数を渡します。

  // 関数の呼び出し
  int ret = (*lpFunc)(tmp);
  if (ret != 0)
  {
    printf("%s", "関数の呼び出しに失敗しました。");
    FreeLibrary(hModule);
    return 0;
  }
DLLを読み込むサンプルプログラム
#include "stdafx.h"
#include <Windows.h>
#include <conio.h>

typedef int(*FUNC)(char *);

int _tmain(int argc, _TCHAR* argv[])
{
  char tmp[12 + 1];
  memset(tmp, 0, sizeof(tmp));

  // DLLのロード
  HMODULE hModule = LoadLibrary(_T("HelloWorld.dll"));
  if (hModule == NULL)
  {
    printf("%s", "DLLのロードに失敗しました。");
    return 0;
  }

  // 関数のアドレス取得
  FUNC lpFunc = (FUNC)GetProcAddress(hModule, "GetHello");
  if (lpFunc == NULL)
  {
    printf("%s", "関数のアドレス取得に失敗しました。");
    FreeLibrary(hModule);
    return 0;
  }

  // 関数の呼び出し
  int ret = (*lpFunc)(tmp);
  if (ret != 0)
  {
    printf("%s", "関数の呼び出しに失敗しました。");
    FreeLibrary(hModule);
    return 0;
  }
  // DLLの解放
  FreeLibrary(hModule);

  printf("%s\n", tmp);
  _getch();

  return 0;
}