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 有什么意义?有没有办法我可以继续使用标头dllimportdllexports删除链接器错误?另外,为什么要尝试解析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 中导出的构造函数和析构函数。但是其余的仍然有效。

[...]

您应该有两个构建目标(或项目,取决于您使用的环境)。

第一个目标将构建 DLL。根据您报告的内容,此目标需要构建的文件是:

currency.hpp
allccy.hpp

并且可能是基类“货币”的实现。您必须在预处理器定义中定义 SANPROJ_EXPORTS 才能使用 currency.hpp 文件作为 DLL 导出的函数的定义。该目标将生成一个 .DLL 文件,并且可能(取决于配置)一个 .lib 文件。它还可以生成其他文件,例如库导出的文本表示(.DEF 文件)。

然后,您需要构建您的应用程序(第二个目标/项目):您需要的头文件与#include 部分的库完全相同。请务必不要定义 SANPROJ_EXPORTS 否则编译器将尝试再次导出符号而不是导入它们。然后您需要将以下设置添加到编译器和链接器:

  • 将包含 .hpp 标头的目录添加到包含路径。

  • 将包含 .lib 文件的目录添加到链接器 (lib) 的库路径。

  • 告诉链接器也链接到您刚刚创建的 .lib(添加 lib 文件的全名,假设 DLL 被命名为“currency”,它可能是“currency.lib”。

添加此设置的位置和方式取决于您使用的工具链/环境和编译器。

最后,请确保编译后的可执行文件能够在项目文件夹或系统目录(在 PATH 中)中找到 DLL,否则将无法启动。如果可执行文件位于与用于构建 DLL 的文件夹不同的文件夹中,只需使用构建后步骤复制 DLL。

删除 _dllimport 部分将构建项目的事实可能是由于编译器找到了头文件和您要导出的函数的实现,并将它们全部静态构建到可执行文件中。

假设您不在 .NET “托管世界”中,并且我们正在谈论 Windows,如果您想分发您的库,还有一些要点需要考虑,但这是另一个答案。

于 2012-09-07T22:30:55.940 回答
2

它特别抱怨USDCurrency类的构造函数和析构函数,但您的代码并未将这些方法显示为装饰有SANPROJ_API.

而且由于您USDCurrency在标头中定义了构造函数,因此当您dllimport从类中删除时,USDCurrency您将获得当前项目中定义的实现,而不是对 DLL 中定义的实现的引用。

于 2012-09-07T20:52:44.990 回答
2

其他人都打败了这个,我也会。

重新编译器添加库,编译器就是这样做的:编译。链接器链接 (doh)。您的似乎是链接器配置问题,有多种方法可以解决此问题。

如果您有一个包含 DLL 项目和 EXE 项目的多项目解决方案文件 (.sln),则可以通过将 DLL 项目设置为 EXE 项目中的 EXE 项目“引用”来建立显式依赖关系。在此,确保“链接库依赖项”标记为“真”。

References 配置实际上是从 VS2005 开始的 .NET 添加,尽管它仍然适用于标准 C/C++ 项目。您可以跳过它并将导入库配置为在 EXE 项目的链接器/输入设置上隐式链接。一个名为“链接库依赖项”的设置也可以在那里标记为 true。这还需要您配置解决方案项目依赖项(Build/Project Dependencies...)。在您的情况下,您选择您的 EXE 项目作为“取决于..”并检查 DLL 项目。这样可以确保在重建 DLL 项目并创建新的导入库时重新链接您的 EXE。

如果需要,可以提供所有这些的屏幕截图。经过几次复飞后设置它已成为旧习惯。在这一点上,我相当确定我可以蒙住眼睛。

于 2012-09-07T21:05:19.240 回答
1

似乎没有解决这个问题的办法。我最终放弃了在dllimport客户端代码中使用并受到性能影响。:(

于 2012-09-16T09:25:24.340 回答