【发布时间】:2012-09-01 17:01:39
【问题描述】:
我目前正在使用互联网上很多地方提到的存储过程创建一个 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 中使用 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)
毫不奇怪,当我从 SanProj.h 文件中删除 dllimport 部分并创建可执行文件时,此错误就会消失。
我的问题是,如果我们不能针对标头编译客户端,那么 IDE 生成 dllimport 的意义何在?有没有办法可以继续使用带有dllimport 和dllexports 的标头并删除链接器错误?另外,它为什么要尝试从 LIB 文件中解析具有 dllimport 的符号?
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”/清单 /ManifestFile:"Release\testdll.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"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:QUEUE
【问题讨论】:
-
您所要做的就是用
dllexport编译DLL,用dllimport编译客户端并为链接器提供正确的库。错误消息表明链接器缺少库,因此实际上无法链接符号(import/export不是问题)。 -
您似乎没有将可执行文件链接到为您的 .dll 创建的导入库。
-
@peachykeen:这就是我现在正在做的事情。我知道路径/配置足够好,因为从
SanProj.h文件中删除dllimport成功创建了可执行文件。如果是链接器找不到导入库的问题,即使我删除了dllimport,它也应该抱怨,不是吗? -
@drescherjm:请阅读以上评论。
-
我不相信编译器会自动将导入库添加到任何使用 dll 的可执行文件的链接设置中。在我编写 dll 的 15 年多的时间里,它从来没有为我这样做过。您可以使用您的 dll 将导入库添加到所有可执行文件的链接设置中的一种简单方法是使用编译指示,但我在您的代码中没有看到。
标签: c++ visual-studio visual-c++ dll