質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

89.55%

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 785

JanTh1989

score 42

前提

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

現状できていること

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

//呼び出し元(DLL)
typedef LONG(*SAMPLE_Function)(int index);

LONG GetLONGValue(int index)
{
    HMODULE dllInstance;
    SAMPLE_Function _SAMPLE_Function;
    dllInstance = static_cast<HMODULE>(LoadLibrary((LPCTSTR)"DLLパス"));
    _SAMPLE_Function = (SAMPLE_Function)GetProcAddress(dllInstance, "SAMPLE_Function");
    return _SAMPLE_Function(index);
}
//呼び出し先(DLL)
#define SAMPLE_API __declspec(dllexport)
EXTERN_C SAMPLE_API LONG SAMPLE_Function(int index); 
// ※実処理部分は省略

質問内容

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

呼び出し先(DLL)
namespace sample
{
    static class SampleBase
    {
    public:
        static void GetData(SampleChild* data) { data = new SampleChild; };
    };

    class SampleChild
    {
    private:
        int no;
    public:
        int get_no() { return no; };
        void set_no(int value) { no = value; };
    }
}

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

開発環境

Visual Studio 2010

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

VS2017にて動作確認

// ヘッダ
namespace sample {
    class SampleChild;

    // 方法1: friend関数をエクスポートする
    extern "C" __declspec(dllexport) void GetData1(SampleChild* data);

    // 方法2: static関数へのポインタを返す関数をエクスポートする
    typedef void(*GetDataPtr)(SampleChild* data);
    extern "C" __declspec(dllexport) sample::GetDataPtr GetData2();

    class SampleBase
    {
    public:
        // 方法1: friend関数
        friend void GetData2(SampleChild* data);

        // 方法3: static関数をエクスポートする
        // 装飾名の解決を行わないとダメなので非推奨
        __declspec(dllexport) static void GetData3(SampleChild* data);

        // 方法4: pragmaを使った装飾名解除+export
        // 楽ではあるがVC専用かつVC6以降専用(だったはず)
        static void GetData4(SampleChild* data);
    };

    class SampleChild
    {
    private:
        int no;
    public:
        int get_no() { return no; };
        inline void set_no(int value) { no = value; };
    };
}
// ソース
#include "SampleBase.h"

sample::GetDataPtr sample::GetData1()
{
    return sample::SampleBase::GetData3;
}

void sample::GetData2(SampleChild * data)
{
    data = new SampleChild;
}

void sample::SampleBase::GetData3(SampleChild * data)
{
    data = new SampleChild;
}

#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__ )
sample::SampleBase::GetData4(SampleChild* data){
#pragma EXPORT
    data = new SampleChild;
}

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/13 10:59

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

    キャンセル

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/13 10:52

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

    キャンセル

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

  • ただいまの回答率 89.55%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる