LoginSignup
5
2

More than 1 year has passed since last update.

C++関数のエクスポート(dllexport + extern "C" 編)

Last updated at Posted at 2022-06-18

経緯

前は下記記事で関数のエクスポートとインポートについてメモしました。

dllexportとdllimportの使い方

上記記事の内容を用いて、更に関数がどのようにエクスポートされているかを確認したく、色々テストしてみました。

少しレベルアップ感の内容があるので、新しい記事でメモします。

DUMPBINというツール

visual studioのインストーラに同梱されているEXEファイルです。
image.png
図にあるvisual studioのコマンドプロンプトを起動してから使えます。
EXEやDLLが他のDLLへの依存性などを確認したり、DLL自身の情報を確認したり、色々情報の調査で役に立つツールです。
Googleすれば色々情報があるので詳細な説明を割愛します。

シンプルな例のDLLのソース

①DLLの外から構造体が渡されると想定
②渡されている構造体の中身を出力

Hello.h
#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の方式で出力するよ、ということで、後で説明します。

Hello.cpp
#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言語としてコンパイル
以下は設定画面
image.png
②DEFファイルを使用して関数をエクスポート
下記記事を参考

C++関数エクスポート(DEFモジュール定義ファイル編)

5
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2