在DLL客户端中使用dllimport时出错

Error when using dllimport in a DLL client

本文关键字:dllimport 出错 DLL 客户端      更新时间:2023-10-16

我目前正在创建一个DLL和客户端,该客户端使用互联网上很多地方提到的存储过程。基本上,创建一个DLL项目,该项目在project.h文件中实际定义了project_EXPORTS。

类似这样的东西:

// 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中时的声明;导入";用作客户端时的声明。例如,假设我们有一个带有货币类的头文件:

// 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;
}

假设所有的引用/设置都已经在VisualStudio项目中完成,我在尝试编译客户端时会出现以下错误:

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)

毫不奇怪,当我从SanProj.h文件中删除dllimport部分并创建可执行文件时,这个错误就会消失。

我的问题是,如果我们不能根据头编译客户端,那么IDE生成的dllimport有什么意义?有没有一种方法可以继续将标头与dllimportdllexports一起使用并删除链接器错误?此外,它为什么试图从LIB文件中解析具有dllimport的符号?

TIA,
/佐助

EDIT:VisualStudio使用的链接器命令;正如您所看到的,它有LIB文件。

/OUT:";E: \vsprojects\SomeSln\Release \testdll.exe"增量:否/NOLOGO";E: \vsprojects\SomeSln\Release \SanProj.lib"quot;kernel32.lib";"user32.lib"gdi32.lib"winspool.lib"comdlg32.lib"advapi32.lib";"shell32.lib"ole32.lib"oleaut32.lib"uuid.lib"odbc32.lib";"odbccp32.lib"/舱单/ManifestFile:"Release \testdll.exe.intermediate.manifest"/允许隔离/歧管:";level='asInvoker'uiAccess='false'"/DEBUG/PDB:;E: \vsprojects\SomeSln\Release \testdll.pdb"/子系统:控制台/OPT:REF/OPT:ICF/PGD:";E: \vsprojects\SomeSln\Release \testdll.pgd"LTCG/TLBID:1/DYNAMICBASE/NXCOMPAT/MACHINE:X86/ERRORREPORT:队列

编辑:当然我错了,因为jcopenha的答案就是答案。链接器抱怨没有在DLL中导出的构造函数和析构函数丢失。然而,其余部分仍然有效。

[…]

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

第一个目标将生成DLL。此目标需要构建的文件基于您报告的内容:

currency.hpp
allccy.hpp

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

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

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

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

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

在何处以及如何添加此设置取决于所使用的工具链/环境和编译器。

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

删除_dllimport部分将构建项目,这可能是因为编译器找到了头和要导出的函数的实现,并将所有函数静态构建到可执行文件中。

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

它特别抱怨USDCurrency类的构造函数和析构函数,但您的代码没有显示这些方法是用SANPROJ_API装饰的。

由于您在头中定义了USDCurrency构造函数,所以当您从类USDCurrency中删除dllimport时,您得到的是在当前项目中定义的实现,而不是对DLL中定义的实施的引用。

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

对于添加库的编译器,编译器只会执行以下操作:编译。链接器链接(doh)。您的似乎是一个链接器配置问题,有很多方法可以解决这个问题。

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

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

如果需要,所有这些的屏幕截图都是可用的。这已经成为一个老习惯,设置后几次周转。在这一点上,我相当肯定我可以做到盲目折叠。

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