小さい頃はエラ呼吸

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


Visual Studio 2015でDLLを作成して動的読み込みしてみる。

はじめに

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

やさしいC++ 第4版 (「やさしい」シリーズ)
高橋 麻奈
ソフトバンククリエイティブ
売り上げランキング: 15,556

環境
  • Windows 8.1
  • Visual Studio 2015 Community
DLLの作成

Visual Studio 2015であたらしいプロジェクトを作成する前に、テンプレートをダウンロードします。「Install Visual C++ 2015 Tool ...」を選択してテンプレートをインストールします。
f:id:replication:20150728214417j:plain
f:id:replication:20150728214444p:plain
f:id:replication:20150728214504p:plain
f:id:replication:20150728214516p:plain
Win32コンソールが表示されたら選択します。
f:id:replication:20150728214604p:plain
f:id:replication:20150728214640p:plain
f:id:replication:20150728214718j:plain

今回は、渡されたアドレスに対して「Hello World!」と書き込む関数を作ってみます。(GetHello関数)
GetHello関数のサンプルプログラムは、以下のようなコードです。

#include "stdafx.h"

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

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

LIBRARY HelloWorld
EXPORTS
  GetHello @1

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

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

C:\Program Files\Microsoft Visual Studio 14.0\VC\bin\dumpbin.exe /exports HelloWorld.dll

GetHello関数が参照できればOKです。
f:id:replication:20150728215203p: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;
}
関連記事