我有以下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; }
}
}
我为上面的sample.dll创建了C ++包装器,以便在非托管代码(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] = '\0';
}
}
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
}
然后创建了一个小应用程序,它将通过下面的非托管代码(native.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);
}
然后我为Native.dll创建了单位案例,如下所示
#include "stdafx.h"
#include "CppUnitTest.h"
#include "..\NativeDll\DemoNativeDll.h"
#include "..\Wrapper\NativeInterface.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。
答案 0 :(得分:1)
__declspec(dllexport) void GetFloatArrayFromCSharp(GetFloatArrayCallback cb);
使用__declspec(dllexport)是将托管代码公开给本机代码的简单方法。但它太简单了,没有合理的方法来检测托管代码中的任何不幸事件。当出现问题时,喜欢抛出异常。你只能看到一个&#34;它没有工作&#34;异常无法找到有关托管异常的任何内容。没有异常消息,无法访问Holy Stack Trace。
您正在预览支持客户的难度。谁会打电话给你并告诉你&#34;它不起作用&#34;。除非你能在他的机器上运行调试器,否则你无能为力。这往往很难得到。
嗯,你有一个,你至少可以用它来诊断单元测试失败。 Project + Properties,Debugging,从&#34; Auto&#34;更改调试器类型设置。到&#34;混合&#34;。这启用了托管调试引擎,您现在可以查看异常详细信息。通常是沼泽标准的,&#34;未找到文件&#34;是一个非常常见的事故。特别是因为CLR没有充分的理由在目前存储它的目录中查找托管程序集,所以配置主appdomain是你不能做的事情。您可能必须将其放入GAC或移动cppunit测试运行器,使其与您的DLL位于同一目录中。 Fuslogvw.exe是诊断程序集解析问题的一个很好的实用程序,它显示了CLR查找程序集的位置以及它使用的配置。
但随意将此单元测试结果放入&#34;重大失败&#34;类别。考虑使用COM interop或CLR托管,他们都会给你一个HRESULT错误代码并支持IErrorInfo来获取异常消息。