-1

C++ で dll を動的にロードする必要があります。

このチュートリアルhttp://msdn.microsoft.com/en-us/library/ms235636.aspxに従ってdll を作成すると、すべて正常に機能しました。

次に、このhttp://msdn.microsoft.com/en-us/library/64tkc9y5.aspxに従い、コンソール アプリケーションを次のように調整しました。

typedef DOUBLE(CALLBACK* DllFunc)(DOUBLE, DOUBLE);

int _tmain(int argc, _TCHAR* argv[])
{
    HINSTANCE hDLL;               // Handle to DLL
    DllFunc dllFunc1;
    DOUBLE p1 = 1.0, p2 = 2.0, r;
    hDLL = LoadLibrary(L"MathFuncsDLL");
    if (hDLL != NULL)
    {
        cout <<  "DLL loaded: " << hDLL << endl;
        dllFunc1 = (DllFunc)GetProcAddress(hDLL, "MyMathFuncs@MathFuncs@Multiply");
        if (!dllFunc1)
        {
            // handle the error
            FreeLibrary(hDLL);
            cout << "Function not found!" << endl;
            return -1;
        }
        else
        {
            // call the function
            r = dllFunc1(p1, p2);
            cout << "The result is: " << r << endl;
        }               
    }
    else {
        cout << "Dll not found" << endl;
        return -1;
    }
    cout << "Press any key to exit." << endl;
    int i;
    cin >> i;
    return 0;
}

DLL は正しく読み込まれ、null ではありません。問題は、常に 0 を返す GetProcAddress() 関数です。

名前空間、クラス名、メソッド名のすべての組み合わせを試しました。関数名の @ の代わりにスコープ演算子 (::) を使用しようとしました。

名前空間全体を extern "C" として定義しようとしましたが、何も変わりません。コンソール アプリケーションを実行またはデバッグするたびに、「乗算」機能が見つかりません。

私は何かが足りないと思います...どこが間違っていますか?


編集

Dependency Walker により、次のエクスポート テーブルが公開されました。

DLL エクスポート テーブル

ここで、関数名の最後の部分が何を意味するのだろうか... __declspec(dllexports) がこれらのシンボルを追加するのはなぜですか?

4

2 に答える 2

3

C++ の名前マングリングへようこそ。コンパイラが関数名に追加するすべての問題は、一意の署名を持つ各関数が一意の名前を持つようにすることです。Microsoft は 1 つの方法でマングリングを命名し、GCC/Clang は別の方法でマングリングを行います。標準化されていませんが、特定の OS のコンパイラはすべて同じ方法で名前マングリングを行っているため、すべてうまくいくようになっています。

名前を人間が予測できるようにする唯一の方法は、エクスポートされた DLL 関数を として宣言することextern "C"です。ただし、これは C 関数のエクスポートに限定されます。

私が取り組んできたライブラリには、ライブラリを初期化し、インターフェイス ポインタを取得するためのエクスポートされた C 関数が 1 つあります。この場合、「インターフェース」は、データを持たず、仮想関数のみを持つ抽象仮想基本クラスを意味します。

これは、インターフェイス クラスを宣言する適切な方法です。

struct my_interface
{
    virtual int my_function1( int param1, int param2 ) = 0;
    virtual int my_function2( const char *param ) = 0;

#if CHOOSE_DESTRUCTION_TYPE

protected:
    // Protected non-virtual destructor prevents the use of delete on the interface pointer.
    // Must be defined because it will be used as objects of derived types are destructed.
    ~my_interface() {};

#else

    // Public virtual destructor allows the use of delete on the interface pointer.
    virtual ~my_interface() = 0;

#endif

private:
    // Private copy assignment prevents "*ptr1 = *ptr2".
    // This odd syntax CAN be supported, but you probably shouldn't.
    // Only needs to be declared because nobody can call it.
    my_interface & operator=( const my_interface & );

    // Ditto for move assignment.
    my_interface & operator=( my_interface && );
};
于 2014-10-14T18:00:05.977 に答える
2

dumpbin や Dependency Viewer などのツールを使用して、エクスポートされた関数名を調べます。これにより、どの障害モードが自分のケースに当てはまるかを診断できます。

  1. 関数がエクスポートされていない、または
  2. 関数はエクスポートされますが、名前は装飾されています。または
  3. 関数は予期した名前でエクスポートされますが、入力が間違っています。
于 2014-10-14T16:40:37.793 に答える