3

私は現在、インターネット上の多くの場所で言及されているストアド プロシージャを使用して、DLL とそれに付随するクライアントを作成しています。基本的に、Project.h ファイルで PROJECT_EXPORTS を実際に定義する DLL プロジェクトを作成します。

このようなもの:

// Assume the name of the project is SanProj and the header file is SanProj.h
#ifdef SANPROJ_EXPORTS
    #define SANPROJ_API __declspec(dllexport)
#else
    #define SANPROJ_API __declspec(dllimport)
#endif

現在、このヘッダーを使用する通常の方法は、API クラスのすべてのヘッダーにこれを含め、DLL 内の場合は「エクスポート」宣言に、クライアントとして使用する場合は「インポート」宣言に SANPROJ_EXPORTS を使用することです。たとえば、通貨クラスを持つヘッダー ファイルがあるとします。

// currency.hpp
#include "SanProj.h"
#include <ostream>
#include <string>

namespace SanProj {

    class SANPROJ_API Currency {

    public:
        Currency();
        const std::string& name();
        const std::string& code();
        bool empty() const;

    protected:
        std::string name_;
        std::string code_;
    };

    SANPROJ_API bool operator==(const Currency&,
                    const Currency&);

    SANPROJ_API bool operator!=(const Currency&,
                    const Currency&);

    SANPROJ_API std::ostream& operator<<(std::ostream& out, Currency& c);
}

そして、特定の通貨を含む別のヘッダー ファイル:

// allccy.hpp
namespace SanProj {

    class SANPROJ_API USDCurrency : public Currency {
    public:
        USDCurrency() {
            name_ = "American Dollar";
            code_ = "USD";
        }
    };


    class SANPROJ_API CADCurrency : public Currency {
    public:
        CADCurrency() {
            name_ = "Canadian Dollar";
            code_ = "CAD";
        }
    };

}

上記のクラスは、DLL プロジェクトの契約を形成します。main次に、関数を持つ単一のクラスであるクライアント プロジェクト ファイルを見てみましょう。

#include "currency.hpp"
#include "allccy.hpp"

#include <iostream>

using namespace SanProj;

int main(int argc, char* argv[])
{
    USDCurrency uccy;
    std::cout << uccy;
}

すべての参照/設定が Visual Studio プロジェクトで既に行われていると仮定すると、クライアントをコンパイルしようとすると次のエラーが発生します。

1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::~USDCurrency(void)" (__imp_??1USDCurrency@SanProj@@QAE@XZ)
1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::USDCurrency(void)" (__imp_??0USDCurrency@SanProj@@QAE@XZ)

驚くことではありませんが、ファイルdllimportからその部分を削除しSanProj.hて実行可能ファイルを作成すると、このエラーはなくなります。

dllimport私の質問は、ヘッダーに対してクライアントをコンパイルできない場合、生成された IDE のポイントは何ですか? ヘッダーを引き続き使用し、リンカー エラーを削除する方法はdllimportありますか? dllexportsまた、dllimportLIB ファイルのシンボルを解決しようとするのはなぜですか?

TIA
/サスケ

編集: VisualStudio で使用されるリンカー コマンド。ご覧のとおり、LIB ファイルがあります。

/OUT:"E:\vsprojects\SomeSln\Release\testdll.exe" /INCREMENTAL:NO /NOLOGO "E:\vsprojects\SomeSln\Release\SanProj.lib" "kernel32.lib" "user32.lib" "gdi32. lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /MANIFEST / ManifestFile:"Release\testdll.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"E:\vsprojects\SomeSln\Release\testdll.pdb" / SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /PGD:"E:\vsprojects\SomeSln\Release\testdll.pgd" /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE

4

4 に答える 4

4

編集: jcopenhaの答えが答えであるため、確かに間違っています。リンカは、DLL でエクスポートしないコンストラクタとデストラクタが欠落していると不平を言います。ただし、残りはまだ有効です。

[...]

2 つのビルド ターゲット (使用する環境によってはプロジェクト) が必要です。

最初のターゲットは DLL をビルドします。このターゲットをビルドする必要があるファイルは、報告内容に基づいて次のとおりです。

currency.hpp
allccy.hpp

おそらく、基本クラス「通貨」の実装です。currency.hpp ファイルを DLL によってエクスポートされる関数の定義として使用するには、プリプロセッサ定義で SANPROJ_EXPORTS を定義しておく必要があります。このターゲットは .DLL ファイルを生成し、おそらく (構成によっては) .lib ファイルを生成します。また、ライブラリのエクスポートのテキスト表現 (.DEF ファイル) のような他のファイルを生成する場合もあります。

次に、アプリケーション (2 番目のターゲット/プロジェクト) をビルドする必要があります。必要なヘッダー ファイルは、#include 部分のライブラリとまったく同じです。SANPROJ_EXPORTS を定義しないように注意してください。そうしないと、コンパイラはシンボルをインポートする代わりに再度エクスポートしようとします。次に、コンパイラとリンカーに次の設定を追加する必要があります。

  • インクルード パスに、.hpp ヘッダーを含むディレクトリを追加します。

  • リンカ (lib) のライブラリ パスに、.lib ファイルを含むディレクトリを追加します。

  • 作成したばかりの .lib に対してもリンクするようにリンカーに指示します (DLL の名前が「currency」であると仮定すると、lib ファイルの完全な名前を追加すると、おそらく「currency.lib」になります。

この設定を追加する場所と方法は、使用しているツールチェーン/環境とコンパイラによって異なります。

最後に、コンパイルされた実行可能ファイルがプロジェクト フォルダーまたはシステム ディレクトリ (PATH 内) で DLL を見つけることができるか、または開始しないことを確認してください。DLL のビルドに使用したフォルダーとは異なるフォルダーに実行可能ファイルがある場合は、ビルド後の手順で DLL をコピーするだけです。

_dllimport 部分を削除するとプロジェクトがビルドされるという事実は、コンパイラがヘッダーと、エクスポートする関数の実装を見つけ、それらすべてを実行可能ファイルに静的にビルドするためです。

あなたが .NET の "管理された世界" にいるわけではなく、Windows について話していると仮定すると、ライブラリを配布したい場合に注意すべき点がいくつかありますが、これは別の答えです。

于 2012-09-07T22:30:55.940 に答える
2

他の誰もがこれを打ち負かしました、私もそうします。

コンパイラがライブラリを追加する場合、コンパイラはまさにそれを行います: コンパイルします。リンカーはリンクします (doh)。これはリンカーの構成の問題のようであり、これを解決する方法はいくつかあります。

DLL プロジェクトと EXE プロジェクトの両方を含むマルチプロジェクト ソリューション ファイル (.sln) がある場合、DLL プロジェクトを EXE プロジェクト内の EXE プロジェクトによって「参照」されるように設定することで、明示的な依存関係を確立できます。この中で、「Link Library Dependencies」が「true」とマークされていることを確認してください。

References 構成は、実際には VS2005 以降の .NET の追加機能ですが、標準の C/C++ プロジェクトでも引き続き機能します。それをスキップして、EXE プロジェクトのリンカー/入力設定で暗黙的にリンクするようにインポート ライブラリを構成できます。「Link Library Dependencies」と呼ばれる設定も、ここで true とマークできます。これには、ソリューション プロジェクトの依存関係 (ビルド/プロジェクトの依存関係...) も構成する必要があります。あなたの場合、EXEプロジェクトを「依存する..」として選択し、DLLプロジェクトを確認します。これにより、DLL プロジェクトが再構築され、新しいインポート ライブラリが作成されるたびに、EXE が再リンクされます。

リクエストがあれば、これらすべてのスクリーンショットを入手できます。数回の周回の後、セットアップするのが古い習慣になります。この時点で、目隠しをしても大丈夫だと確信しています。

于 2012-09-07T21:05:19.240 に答える
2

クラスのコンストラクターとデストラクタについて具体的に不平を言ってUSDCurrencyいますが、コードはそれらのメソッドが で飾られていることを示していませんSANPROJ_API

USDCurrencyまた、ヘッダーでコンストラクターを定義するためdllimport、クラスからを削除するUSDCurrencyと、DLL で定義されたものへの参照ではなく、現在のプロジェクトで定義された実装が取得されます。

于 2012-09-07T20:52:44.990 に答える
1

この問題の解決策はないようです。dllimportクライアントコードでの使用をあきらめてしまい、パフォーマンスが低下しました。:(

于 2012-09-16T09:25:24.340 に答える