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

C++ 在DLL客户端中使用dllimport时出错,c++,visual-studio,visual-c++,dll,C++,Visual Studio,Visual C++,Dll,我目前正在创建一个DLL和客户端,它使用internet上很多地方提到的存储过程。基本上,创建一个DLL项目,它实际上在project.h文件中定义了一个项目 大概是这样的: // Assume the name of the project is SanProj and the header file is SanProj.h #ifdef SANPROJ_EXPORTS #define SANPROJ_API __declspec(dllexport) #else #defi

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

大概是这样的:

// 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_导出进行“导出”声明,在用作客户端时使用“导入”声明。例如,假设我们有一个带有货币类的头文件:

// 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);
}
上述类构成DLL项目的契约。现在让我们看一下客户端项目文件,它是一个带有
main
函数的单一类:

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

#include <iostream>

using namespace SanProj;

int main(int argc, char* argv[])
{
    USDCurrency uccy;
    std::cout << uccy;
}
毫不奇怪,当我从
SanProj.h
文件中删除
dllimport
部分并创建可执行文件时,这个错误就消失了

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

TIA,
/佐助

编辑:VisualStudio使用的链接器命令;如您所见,它有LIB文件

/输出:“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 /清单文件:“Release\testdll.exe.intermediate.manifest” /ALLOWISOLATION/MANIFESTUAC:“level='asInvoker'uiAccess='false'” /调试/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:QUEUE


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


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

其他人都克服了这一点,我也会

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

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

从VS2005开始,引用配置实际上是一个.NET添加,尽管它仍然适用于标准C/C++项目。您可以跳过该选项,并将导入库配置为在EXE项目的链接器/输入设置上隐式链接。名为“链接库依赖项”的设置也可以标记为true。这还需要配置解决方案项目依赖项(生成/项目依赖项…)。在本例中,您选择EXE项目作为“依赖于…”,并检查DLL项目。这可确保每当重建DLL项目并创建新的导入库时,都会重新链接EXE


如果需要,可以提供所有这些的屏幕截图。经过几次尝试之后,建立它已成为一种旧习惯。在这一点上,我相当肯定我可以盲着做。

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

[……]

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

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

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

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

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

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

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

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

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

事实上,移除_dllimport部件将构建
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)
currency.hpp
allccy.hpp