🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3433閲覧

外部ライブラリの静的メンバ関数呼び出し方法

JanTh1989

総合スコア87

Visual Studio 2010

Microsoft Visual Studio 2010はMicrosoftが提供している統合開発環境(IDE)です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/09/09 09:44

編集2019/09/09 09:45

前提

外部ライブラリ(DLL)を動的に読み込んで、そのライブラリの関数呼び出し方法を考えております。

現状できていること

以下の方法で、エクスポート済関数のアドレス取得による呼び出しは行えました。

C++

1//呼び出し元(DLL) 2typedef LONG(*SAMPLE_Function)(int index); 3 4LONG GetLONGValue(int index) 5{ 6 HMODULE dllInstance; 7 SAMPLE_Function _SAMPLE_Function; 8 dllInstance = static_cast<HMODULE>(LoadLibrary((LPCTSTR)"DLLパス")); 9 _SAMPLE_Function = (SAMPLE_Function)GetProcAddress(dllInstance, "SAMPLE_Function"); 10 return _SAMPLE_Function(index); 11}

C++

1//呼び出し先(DLL) 2#define SAMPLE_API __declspec(dllexport) 3EXTERN_C SAMPLE_API LONG SAMPLE_Function(int index); 4// ※実処理部分は省略

質問内容

エクスポート済関数に続いて、静的クラスの静的メンバ関数を、同様にアドレス取得で呼び出したいと考えております。
それ自体は実現方法があるのでしょうか。
LoadLibrary関数を使用した方法の調査を実施しましたが、あまり良い情報が入手できませんでした。
GetProcAddress関数は、あくまでエクスポート済アドレスの取得になる認識でいます。
また、静的メンバについては、関数ポインタとすることも可能の認識でおります。

C++

1呼び出し先(DLL) 2namespace sample 3{ 4 static class SampleBase 5 { 6 public: 7 static void GetData(SampleChild* data) { data = new SampleChild; }; 8 }; 9 10 class SampleChild 11 { 12 private: 13 int no; 14 public: 15 int get_no() { return no; }; 16 void set_no(int value) { no = value; }; 17 } 18}

・LoadLibrary関数の使用がそもそもの間違い。
・静的メンバ関数で取るのではなく、静的クラスと同じものを定義して、静的クラスポインタでアドレス上書き。
なども考えているのですが、正解が見つけられずにおります。

開発環境

Visual Studio 2010

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

VS2017にて動作確認

c++

1// ヘッダ 2namespace sample { 3 class SampleChild; 4 5 // 方法1: friend関数をエクスポートする 6 extern "C" __declspec(dllexport) void GetData1(SampleChild* data); 7 8 // 方法2: static関数へのポインタを返す関数をエクスポートする 9 typedef void(*GetDataPtr)(SampleChild* data); 10 extern "C" __declspec(dllexport) sample::GetDataPtr GetData2(); 11 12 class SampleBase 13 { 14 public: 15 // 方法1: friend関数 16 friend void GetData2(SampleChild* data); 17 18 // 方法3: static関数をエクスポートする 19 // 装飾名の解決を行わないとダメなので非推奨 20 __declspec(dllexport) static void GetData3(SampleChild* data); 21 22 // 方法4: pragmaを使った装飾名解除+export 23 // 楽ではあるがVC専用かつVC6以降専用(だったはず) 24 static void GetData4(SampleChild* data); 25 }; 26 27 class SampleChild 28 { 29 private: 30 int no; 31 public: 32 int get_no() { return no; }; 33 inline void set_no(int value) { no = value; }; 34 }; 35}

c++

1// ソース 2#include "SampleBase.h" 3 4sample::GetDataPtr sample::GetData1() 5{ 6 return sample::SampleBase::GetData3; 7} 8 9void sample::GetData2(SampleChild * data) 10{ 11 data = new SampleChild; 12} 13 14void sample::SampleBase::GetData3(SampleChild * data) 15{ 16 data = new SampleChild; 17} 18 19#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__ ) 20sample::SampleBase::GetData4(SampleChild* data){ 21#pragma EXPORT 22 data = new SampleChild; 23}

まず、static classはC#の機能でありC++では無視されます。

DLLから動的に(GetProcAddressにて)呼び出すためには、DLL作成時になんらかの方法で
エクスポートアドレステーブルという場所に関数を登録する必要があります。
そのための代表的な方法が、__declspec(dllexport)ということになります。

方法としては色々ありますが、static関数にこだわらずにfriend関数をエクスポートするのが簡単に思います。

投稿2019/09/10 11:17

編集2019/09/10 11:30
asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

JanTh1989

2019/09/13 01:59

ご回答ありがとうございます。 やはりエクスポート必須になってきますね。 friend関数はあまり手を付けたことがありませんでしたが、static関数、friend関数のいずれも呼び出しせることは見ることができました。 ありがとうございます。
guest

0

DLL そのものの仕組みは名前とアドレスしか管理していません。
C++ ではオーバーロードや名前空間 (namespace)、そしてもちろんクラスの仕組みがあるため、関数自体の名前が同じであっても違うものが存在する可能性があります。 それを DLL で使うときになんとかするために名前マングル (name mangling) という仕組みが使われています。 名前空間や型情報 (シグネチャ) を符号化したものを名前にくっつけることで C++ のプログラムもそのまま DLL に出来るようになっているのです。
関数をエクスポートするときに extern "C" を付けるのは (言語仕様でその仕組みの詳細が決まっているわけではありませんが) そういった付加的な情報を名前にくっつけずに C でやっているのと同じように名前だけで管理するという指定です。
名前マングルというのは、標準的な方法が存在しません。 DLL を呼ぶ側と呼ばれる側が同じ処理系で書いてあれば処理系が同じルールで解決するので上手くいきますが、処理系が異なれば無理です。
そして GetProcAddress は名前マングルの処理を自動でやってくれたりはしません。 なんらかの方法で名前マングル処理された後の名前を知っておく必要があります。 Visual Studio でやる最も簡単な方法は実際に生成された DLL を DUMPBIN コマンドで確認することです。

このように、メンバ関数も DLL でエクスポートすることは可能ですが呼び出す側と呼び出される側が一貫した仕組みで解決されなければならないので、ひとつのアプリケーションを便宜的に分割する場合はともかく汎用性のある DLL を作る場合は C 風のインターフェイス (非メンバ関数に extern "C" を付けたもの) を経由するようにするのが一般的な習慣です。

投稿2019/09/09 12:42

SaitoAtsushi

総合スコア5684

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

JanTh1989

2019/09/13 01:52

ご回答ありがとうございます。 頂いた情報を参考に、検討してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問