无法对托管代码运行非托管 CPP 单元测试

failed to run unmanaged cpp unit test for managed code

本文关键字:CPP 单元测试 运行 托管代码      更新时间:2023-10-16

我有以下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 获取异常消息。