経緯
前は下記記事で関数のエクスポートとインポートについてメモしました。
上記記事の内容を用いて、更に関数がどのようにエクスポートされているかを確認したく、色々テストしてみました。
少しレベルアップ感の内容があるので、新しい記事でメモします。
DUMPBINというツール
visual studioのインストーラに同梱されているEXEファイルです。
図にあるvisual studioのコマンドプロンプトを起動してから使えます。
EXEやDLLが他のDLLへの依存性などを確認したり、DLL自身の情報を確認したり、色々情報の調査で役に立つツールです。
Googleすれば色々情報があるので詳細な説明を割愛します。
シンプルな例のDLLのソース
①DLLの外から構造体が渡されると想定
②渡されている構造体の中身を出力
#pragma once
#include<stdio.h>
#ifdef CPPDLL_EXPORTS
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif
typedef struct {
int id;
char name[20];
}st_info;
extern "C" {
DECLSPEC int output(st_info*);
}
ここに「extern "C"」を用いて関数をCの方式で出力するよ、ということで、後で説明します。
#include "Hello.h"
int output(st_info* info) {
printf("id: %d\nname: %s\n", info->id, info->name);
return 0;
}
DUMPBINでDLLの情報を確認
①Visual StudioのコマンドプロンプトでDLLの場所に移動してDUMPBINを実行
C:\Users\pirlo\source\repos\CPPDll\Debug>dumpbin /exports CPPDll.dll
Dump of file CPPDll.dll
File Type: DLL
Section contains the following exports for CPPDll.dll
.....
ordinal hint RVA name
1 0 000111A4 output = @ILT+415(_output)
Summary
.....
エクスポートされている関数(output)は、以下のように記載されています。
ordinal hint RVA name
1 0 000111A4 output = @ILT+415(_output)
他のEXEやDLLがoutputという関数を使用する時に、一般的に「ordinal」(順番)や「name」(名前)列の値から関数を検索します。
この例では、「1」または「output」が参照されます。
extern "C"が使用されない場合
エクスポート対象の関数の実名がソースコードだけで確認できませんが、extern "C"を使用しない場合、DUMPBINの結果は以下のようになります。
ordinal hint RVA name
1 0 0001112C ?output@@YAHPAUst_info@@@Z = @ILT+295(?output@@YAHPAUst_info@@@Z)
結局、エクスポートされている関数の名前が、想定の「output」と異なり、「?output@@YAHPAUst_info@@@Z」に変更されました。
何故かというと、C++の仕様です。
C言語では同名の関数の存在はできず、関数名の唯一性がありますが、C++では関数のオーバーロードができるため、同名の関数を区別するためにDLLの作成段階で関数名が勝手に変更されています。
※詳しい情報を割愛します。
確実にソースコードにある関数の名前「output」を使うために、extern "C"を用いて、コンパイル時にこの関数の名前を変更しないで、という指示を出します。
extern "C"が使用されない時に発生する問題
関数名が裏で変更されたので、他のEXEやDLLでこのDLLのソースにある「output」関数を使用しようとしたら、「output」という関数が見つからないよ、というエラーが発生します。
C++のEXEなどでコンパイル時エラーが発生する場合は多いですが、VBやC#などでDllImportで実行時にDLLをインポートしようとしたら「System.EntryPointNotFoundException」という例外が実行時に発生します。
extern "C"を使用しなくても問題にならない方法
①DLLをC言語としてコンパイル
以下は設定画面
②DEFファイルを使用して関数をエクスポート
下記記事を参考