我目前需要为本机C++库(TetGen)编写一个C++/CLI包装器,以便稍后在C#项目中使用。我已经读了很多关于如何做到这一点的文章,但对我的链接器错误感到不知所措。一个叫"Sam Agten"的家伙也试图做到这一点,正如他在博客中所说的那样,这让我看到了一个关于C++互操作的系列(第1部分、第2部分、第3部分、第4部分、第5部分),其中第3部分应该解释如何完成我所完成的任务。
因为Sam想把TetGen和Unity融合在一起,他最后不得不求助于C风格的说唱歌手。现在,如果我没有完全错的话,我已经在以前的一个相关项目中尝试过使用C风格说唱歌手。
因为我包装好的TetGen必须在Windows服务器上运行(我想是2008年),所以我的第一个问题是:C++/CLI包装程序在服务器环境中工作吗
我开始根据前面提到的第3部分编写一个非常简单的包装器,但失败得很惨。不管怎样,我试着按照思路创建了一个简单的包装器库,我想和大家分享一下。我首先创建了一个C#项目,该项目最终应该使用我的包装库,并首先从C++项目"SimpleAdd"的"Win32"小节添加了一个新项目。
//SimpleAdd.h
class _declspec(dllexport) SimpleAdd
{
public:
SimpleAdd();
~SimpleAdd();
int Add(int a,int b);
};
//SimpleAdd.cpp
#include "SimpleAdd.h"
int SimpleAdd::Add(int a, int b)
{
return a+b;
}
在项目设置中,我确保它编译为静态库(*.lib),并且使用了无CLR支持。根据预处理器的定义,我假设lib是针对x86系统的:"WIN32"。所有的东西都是标准值。如果我右键单击=>构建,它将成功地构建一个名为"SimpleAdd.lib"的库。
其他帖子指出了一件事:如果我在DependencyWalker中打开库,我会收到一个错误:"找不到DOS或PE签名。这个文件不是有效的32位或64位Windows模块",这有点令人沮丧我必须做些什么才能让DependencyWalker停止显示此错误,因为我认为这个32/64问题可能会提示解决方案。
不管怎样,我有我的自由,现在我想结束它。因此,我在我的解决方案中创建了一个名为"AddWrapper"的新项目,使用C++分支的CLR ClassLibrary模板。
// AddWrapper.h
#pragma once
#include "..SimpleAddSimpleAdd.h"
using namespace System;
namespace AddWrapper {
public ref class ManagedSimpleAdd
{
private:
SimpleAdd *sa;
public:
ManagedSimpleAdd();
~ManagedSimpleAdd();
int Add(int a, int b);
};
}
//AddWrapper.cpp
#include "AddWrapper.h"
using namespace AddWrapper;
ManagedSimpleAdd::ManagedSimpleAdd() : sa(new SimpleAdd())
{
}
ManagedSimpleAdd::~ManagedSimpleAdd(){delete sa;}
int ManagedSimpleAdd::Add(int a, int b)
{
return sa->Add(a,b);
}
由于两个C++项目都在同一层次级别上的不同文件夹中,我指定了头文件的相对路径。在项目设置中,我现在有了配置标准.dll和/clr-开关。在我的VC++文件夹中,我添加了整个灵魂的Debug目录的路径,因为这是被引用的lib编译到的地方。在"附加依赖项"下的链接器设置中,我增加了名称:"SimpleAdd.lib"。
然而,当我想构建我的包装器时,出现了一个问题,或者更确切地说是其中的5个问题:
Fehler 3错误LNK2028:Nicht aufgel÷stes Token(0A00000B)"public:__thiscall SimpleAdd::SimpleAdd(void)"(??0SimpleAdd@@$$FQAE@XZ)",函数"public中的auf das:__clrcall-AddWrapper::ManagedSimpleAdd::ManagedSimpleAdd(void)"(??0ManagedSimpleAdd@AddWrapper@@$$FQ$AAM@XZ)"verwiesen wird.%path%\AddWrapper\AddWrapper.obj AddWrapperFehler 4错误LNK2028:Nicht aufgel÷stes Token(0A00000C)"public:__thiscall SimpleAdd::~SimpleAdd(void)"(??1SimpleAdd@@$$FQAE@XZ)",函数中的auf das"public:void*__thiscall SimpleAdd::
scalar deleting destructor'(unsigned int)" (??_GSimpleAdd@@$$FQAEPAXI@Z)" verwiesen wird. %path%AddWrapperAddWrapper.obj AddWrapper Fehler 5 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: __thiscall SimpleAdd::SimpleAdd(void)" (??0SimpleAdd@@$$FQAE@XZ)" in Funktion ""public: __clrcall AddWrapper::ManagedSimpleAdd::ManagedSimpleAdd(void)" (??0ManagedSimpleAdd@AddWrapper@@$$FQ$AAM@XZ)". %path%AddWrapperAddWrapper.obj AddWrapper Fehler 6 error LNK2019: Verweis auf nicht aufgel÷stes externes Symbol ""public: __thiscall SimpleAdd::~SimpleAdd(void)" (??1SimpleAdd@@$$FQAE@XZ)" in Funktion ""public: void * __thiscall SimpleAdd::
标量删除析构函数'(unsigned int)"(??_GSimpleAdd@@$$FQAEPAXI@Z)".%path%\AddWrapper\AddWrapper.obj AddWrapperFehler 7错误LNK1120:4 nicht aufgel÷ste Externe%path%\Debug\AddWrapper.dll 1 AddWrapper
现在,这些错误的原因是什么是lib的平台混乱吗?我是否遗漏了其他内容?
我必须做些什么才能使DependencyWalker停止显示此错误
不要编译为lib文件!它不是可执行文件,也不是动态链接库,所以它没有PE签名。您可以构建一个静态库并将其链接到您的C++/CLI程序集中(如果您愿意的话),也可以简单地将所有代码放在您的C++/CLI程序集中。由你决定。
这些错误的原因可能是什么?
你直接包含了头文件,让我强调一下你的代码:
class _declspec(dllexport) SimpleAdd
正如您所看到的,您实际上是在使用_declspec(dllexport)
进行编译,您并没有告诉编译器从库中导入此类,而是在导出它(链接器会抱怨,因为您声明了该类,但没有任何实现)。当您包含一个类时,它必须用_declspec(dllimport)
声明。
class _declspec(dllimport) SimpleAdd
通常,这个技巧是用预处理器宏来完成的。让我举一个简单的例子。在SimpleAdd.h
文件中,您可以声明:
#if defined(SIMPLEADD_LIB)
#define SIMPLEADD_EXPORTED _declspec(dllexport)
#else
#define SIMPLEADD_EXPORTED _declspec(dllimport)
#endif
然后将您的代码更改为
class SIMPLEADD_EXPORTED SimpleAdd
在SimpleAdd
项目中,可以定义一个名为SIMPLEADD_LIB
的宏(来自项目设置)。这将导致您的库使用dllexport
构建(因为您从库中导出该类),但在C++/CLI包装器中声明为dllimport
(因为SIMPLEADD_LIB
没有声明,而您正在导入该类)。