无法对托管代码运行非托管 CPP 单元测试
failed to run unmanaged cpp unit test for managed code
我有以下C#代码(sample.dll)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sample
{
public class Worker
{
/// <summary>
/// Callback method signature to send array of values.
/// </summary>
/// <param name="values">The values.</param>
public delegate void FloatValuesReady(float[] values);
/// <summary>
/// Occurs when [float values are ready].
/// </summary>
public event FloatValuesReady ReadFloatValues;
/// <summary>
/// Sums the specified i.
/// </summary>
/// <param name="i">The i.</param>
/// <param name="j">The j.</param>
/// <returns></returns>
public int Sum(int i, int j)
{
return i + j;
}
/// <summary>
/// Gets the Student object.
/// </summary>
/// <returns></returns>
public ManagedStudent GetStudent()
{
Console.WriteLine("Enter a Name:");
string text = Console.ReadLine();
if (text != string.Empty)
{
return new ManagedStudent { Name = text };
}
return new ManagedStudent { Name = "NoName" };
}
/// <summary>
/// Read some float values from Console and give it back to the caller using ReadFloatValues callback.
/// So Caller should add event handler to ReadFloatValues event.
/// </summary>
public void GetSomeFloatValues()
{
List<float> values = new List<float>();
Console.WriteLine("Enter 4 valid float values for the Native App");
while (values.Count < 4)
{
string valueText = Console.ReadLine();
float value;
if(float.TryParse(valueText, out value))
{
values.Add(value);
}
}
if (this.ReadFloatValues != null)
{
this.ReadFloatValues(values.ToArray());
}
}
}
/// <summary>
/// A Managed Class
/// </summary>
public class ManagedStudent
{
public string Name { get; set; }
}
}
我已经为上面的示例创建了C++包装器.dll以在非托管代码(wrapper.dll)中访问托管代码。下面是 SampleWrapper.h,SampleWrapper.cpp,NativeInterface.h 的代码。
/*Sample Wrapper.h*/
using namespace System;
using namespace Sample;
namespace Wrapper
{
public ref class SampleWrapper
{
/* Private Constructor to achieve signle ton*/
SampleWrapper(void)
{
workerObj = gcnew Worker();
workerObj->ReadFloatValues += gcnew Worker::FloatValuesReady(this, &Wrapper::SampleWrapper::FloatArrayReadyMethod);
}
public:
Worker ^ workerObj;
/* Static reference to single ton instace.
In order to use applications built in C.*/
static SampleWrapper ^ Instance = gcnew SampleWrapper();
void FloatArrayReadyMethod(array<float> ^ values);
};
}
/*NativeInterface.cpp/*
#ifdef __cplusplus
extern "C"
{
#endif
/* Un managed type definition of student equalent to the Managed*/
typedef struct
{
char* name;
}UnManagedStudent;
/* A simple interface using the premitive types. Accepts 2 paramters and retun*/
__declspec(dllexport) int SumFromCSharp(int i, int j);
/* An interface to get the Student Information in a Structure.
This function calls the C# class method and gets the managed Student Object
and converts to unmanged student*/
__declspec(dllexport) UnManagedStudent GetStudent();
/* Function pointer to a native function to achieve call back*/
typedef void (*GetFloatArrayCallback) (float values[], int length);
/* An interface that makes call to C# to read some float values.
The C# worker respond back in event call back.
In order to pass the information back to the native app
it should give the callback pointer*/
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb);
#ifdef __cplusplus
}
#endif
/*sampleWrapper.cpp*/
#include "SampleWrapper.h"
#include "NativeInterface.h"
using namespace Sample;
namespace Wrapper
{
#ifdef __cplusplus
extern "C"
{
#endif
void copyManagedStringToCharPointer(char target[], System::String ^ inputString)
{
int maxSize = inputString->Length;
if ( maxSize > 0)
{
for (int index = 0; index < maxSize; ++index )
{
target[index] = (char)inputString->default[index];
}
target[maxSize] = ' ';
}
}
void copyManagedFloatToUnfloatArray(float target[], array<float> ^ values)
{
int maxSize = values->Length;
if ( maxSize > 0)
{
for (int index = 0; index < maxSize; index++ )
{
target[index] = (float)values[index];
}
}
}
__declspec(dllexport) int SumFromCSharp(int i, int j)
{
Worker ^ worker = SampleWrapper::Instance->workerObj;
return worker->Sum(i,j);
}
__declspec(dllexport) UnManagedStudent GetStudent()
{
Worker ^ worker = SampleWrapper::Instance->workerObj;
ManagedStudent ^ studentObj = worker->GetStudent();
String ^ mName = studentObj->Name;
UnManagedStudent studentStruct;
studentStruct.name = new char[mName->Length];
copyManagedStringToCharPointer(studentStruct.name,mName);
return studentStruct;
}
GetFloatArrayCallback floatArrayCallback;
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb)
{
floatArrayCallback = cb;
Worker ^ worker = SampleWrapper::Instance->workerObj;
worker->GetSomeFloatValues();
}
void SampleWrapper::FloatArrayReadyMethod(array<float> ^ values)
{
float *nativeValues = new float[values->Length];
copyManagedFloatToUnfloatArray(nativeValues, values);
floatArrayCallback(nativeValues, values->Length);
}
#ifdef __cplusplus
}
#endif
}
然后创建了小应用程序,该应用程序将通过如下所示的非托管代码访问托管代码(本机.dll)。
#include "DemoNativeDll.h"
#include "NativeInterface.h"
using namespace nsNativeDll;
CDemoNativeDll::CDemoNativeDll(void)
{ }
CDemoNativeDll::~CDemoNativeDll(void)
{
}
int CDemoNativeDll::GetSum(int value1, int value2)
{
return SumFromCSharp(value1,value2);
}
然后我为本机创建了单位大小写.dll如下所示
#include "stdafx.h"
#include "CppUnitTest.h"
#include "..NativeDllDemoNativeDll.h"
#include "..WrapperNativeInterface.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace nsNativeDll;
namespace UnitTest1
{
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(TestMethod1)
{
CDemoNativeDll* obj = new CDemoNativeDll();
int sum = obj->GetSum(10,15);
Assert::AreEqual(sum,15);
// TODO: Your test code here
}
};
}
我收到EEFileLoadException异常。如何重新考虑此异常。我正在使用Visual Studio 2012 cppunit。
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb);
使用 __declspec(dllexport) 是向本机代码公开托管代码的简单方法。 但它太简单了,没有合理的方法来检测托管代码中的任何事故。 这喜欢在出现任何问题时抛出异常。 您只能看到一个"它不起作用"异常,没有任何方法可以查找有关托管异常的任何信息。 没有异常消息,无法访问神圣堆栈跟踪。
您可以预览支持客户的难度。 谁会打电话给你并告诉你"它没有用"。 除非你可以让他的机器上运行调试器,否则你无能为力。 这往往很难实现。
好吧,你有一个,你至少可以使用它来诊断单元测试失败。 项目 + 属性,调试,将调试器类型设置从"自动"更改为"混合"。 这将启用托管调试引擎,您现在可以查看异常详细信息。 通常是一个沼泽标准的,"找不到文件"是一个非常常见的事故。 特别是由于 CLR 没有充分的理由在当前存储托管程序集的目录中查找托管程序集,因此配置主应用程序域是您无法执行的其他操作。 您可能必须将其放在 GAC 中或移动 cppunit 测试运行程序,使其与 DLL 位于同一目录中。 Fuslogvw.exe 是诊断程序集解析问题的良好实用程序,它向您显示 CLR 在何处查找程序集以及它使用的配置。
但请随意将此单元测试结果归入"主要失败"类别。 考虑使用 COM 互操作或 CLR 托管,它们都为您提供 HRESULT 错误代码并支持 IErrorInfo 获取异常消息。
- 有什么好的方法可以让系统调用代理允许在单元测试中进行模拟
- 在子目录中使用target_sources()命令时用于单元测试(qtest)的项目结构
- VC++本机单元测试,找不到调试符号
- 用于交叉编译和CMake的预处理器宏的单元测试
- C++ 用于单元测试的模板模板
- 提升 1.64 单元测试编译失败
- 单元测试欧拉到四元数实现失败
- 运行 C++ 单元测试时LNK2005链接错误
- 禁用自动捕获 Googletest 单元测试中的C++异常
- 有没有办法在不使用 #ifdef 的情况下不编译发布版本中的单元测试函数体?
- 使用 Google Test 对自定义断言函数进行单元测试
- 如何将我的 CMake 项目配置为运行所有单元测试?
- 在Qt C++单元测试中动态加载QQuickWindow而不是QQuickWidget
- C++ gmock - 我们如何在单元测试 cpp 文件中读取/获取 cpp 文件函数的参数值
- 在单元测试项目中包括 .c 文件,并从多个 cpp 文件访问它而不会出现链接问题
- 无法对托管代码运行非托管 CPP 单元测试
- 奔步Cpp单元测试解析器未报告任何测试
- 黄瓜CPP构建错误:is_initialized()不是单元测试的成员
- 从cpp单元测试用例中为规范生成参考测试文件
- 用cpp编写的单元测试代码