单元测试混合C++;/Visual Studio中的CLI/C#代码失败
我们得到了一个C#DLL,其中包含加载和操作专有数据格式所需的代码。我们的代码库主要是原生C++。为了能够访问提供的库,我编写了一个DLL,其中包括单元测试混合C++;/Visual Studio中的CLI/C#代码失败,c#,c++,visual-studio,unit-testing,c++-cli,C#,C++,Visual Studio,Unit Testing,C++ Cli,我们得到了一个C#DLL,其中包含加载和操作专有数据格式所需的代码。我们的代码库主要是原生C++。为了能够访问提供的库,我编写了一个DLL,其中包括 实现必要的API 的本地C++包装器类 一个C++/CLI桥接类,用于封送数据进出,并将调用委托给C#DLL 我编写了一个基本的(原生)C++控制台应用程序,测试API是否符合我的需要。是的。然后,我编写了一个(本地的,MicrosoftVisualStudio)单元测试项目来调用API。令人惊讶的是,在构造接口对象时,它会崩溃,并出现一些未声
- 实现必要的API 的本地C++包装器类
- 一个C++/CLI桥接类,用于封送数据进出,并将调用委托给C#DLL
我编写了一个基本的(原生)C++控制台应用程序,测试API是否符合我的需要。是的。然后,我编写了一个(本地的,MicrosoftVisualStudio)单元测试项目来调用API。令人惊讶的是,在构造接口对象时,它会崩溃,并出现一些未声明的异常
一些最小代码: PAPI.h:#pragma once
#pragma unmanaged
#if defined(INTEROP_API_EXPORTS)
#define INTEROP_API __declspec(dllexport)
#else
#define INTEROP_API __declspec(dllimport)
#endif
#include <string>
class InteropBridgeArchive;
struct InteropRecord
{
std::string Name;
int Id;
std::string Data;
};
class INTEROP_API InteropAPI
{
public:
InteropAPI();
~InteropAPI();
InteropRecord GetRecord(int id) const;
private:
InteropBridgeArchive * impl_;
};
内桥
#pragma once
#pragma unmanaged
#include "InteropAPI.h"
#pragma managed
#include <vcclr.h>
class InteropBridge
{
public:
InteropArchive();
InteropRecord GetRecord(System::Int32 id) const;
private:
gcroot<InteropCSharpDll::Archive^> archive_;
};
显然,如果这种混合结构不能作为本机代码进行单元测试,我们就无法测试任何涉及它的内容,因此弄清它崩溃的原因是非常重要的。这个代码运行得很好,作为一个本地C++项目:
#include <cstdio>
#include <iostream>
#include "../InteropCppCliBridgeDll/InteropAPI.h"
#pragma comment(lib, "../x64/Debug/InteropCppCliBridgeDll.lib")
int main(int , char ** )
{
const InteropAPI archive;
const auto record = archive.GetRecord(0);
std::cout << "Name: " << record.Name << "\n";
std::cout << "Id: " << record.Id<< "\n";
std::cout << "Data: " << record.Data<< "\n\n";
}
托管代码喜欢抛出异常,当未捕获异常并将其转换为本机代码时,您会像蝙蝠一样盲目。使用项目>属性>调试>调试类型进行调试=混合。启用本机和托管调试引擎后,您现在也可以看到托管异常。调试>窗口>异常设置并勾选“公共语言运行时异常”,以便在引发异常时调试器中断。在可识别代码中不一定存在FileNotFoundException,当抖动无法在执行之前编译代码时,FileNotFoundException非常常见。这有点帮助:我现在看到多个异常:第一个是System.BadImageFormatException:'无法加载文件或程序集'Microsoft.VisualStudio.Coverage.Interop'或其依赖项之一。试图加载格式不正确的程序。然后System.IO.FileNotFoundException:“无法加载文件或程序集”BoostTestAdapter.XmlSerializers。。。System.IO.FileNotFoundException:“无法加载文件或程序集”GoogleTestAdapter.XmlSerializers。。。XmlSerializer异常是正常的,就XML序列化的实现方式而言,BadImageFormat是您的敌人。我不会冒险去猜测这是怎么出错的。VisualStudio的Google Test runner似乎没有遇到这个问题,所以我把我的测试转移到了那个问题上。这不是一个很好的解决方案,因为我们的大部分测试代码都是MSTEST。不过,总比什么都没有好。哦,我已经向微软报告了这个问题。
#pragma unmanaged
#include <string>
#pragma managed
#include "InteropBridge.h"
#include <msclr/marshal_cppstd.h>
InteropBridge::InteropBridge()
{
archive_ = gcnew InteropCSharpDll::Archive;
}
InteropRecord InteropBridge::GetRecord(const System::Int32 id) const
{
auto return_value = archive_->GetRecord(id);
InteropRecord record;
record.Name = msclr::interop::marshal_as<std::string>(return_value.Name);
record.Id = return_value.Id;
record.Data = msclr::interop::marshal_as<std::string>(return_value.Data);
return record;
}
using System.Linq;
namespace InteropCSharpDll
{
public struct Record
{
public string Name;
public int Id;
public string Data;
}
public class Archive
{
public Record GetRecord(int id)
{
return _records.First(r => r.Id == id);
}
private readonly Record[] _records =
{
new Record {Name = "Foo", Id = 0, Data = "Foo.Data"},
new Record {Name = "Bar", Id = 2, Data = "Bar.Data"},
new Record {Name = "Egg", Id = 7, Data = "Egg.Data"},
};
}
}
#include <cstdio>
#include <iostream>
#include "../InteropCppCliBridgeDll/InteropAPI.h"
#pragma comment(lib, "../x64/Debug/InteropCppCliBridgeDll.lib")
int main(int , char ** )
{
const InteropAPI archive;
const auto record = archive.GetRecord(0);
std::cout << "Name: " << record.Name << "\n";
std::cout << "Id: " << record.Id<< "\n";
std::cout << "Data: " << record.Data<< "\n\n";
}
archive_ = gcnew InteropCSharpDll::Archive;