无法为托管代码运行非托管cpp单元测试

问题描述 投票:0回答:1

我有以下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。

c++ visual-c++ cppunit
1个回答
1
投票
  __declspec(dllexport) void   GetFloatArrayFromCSharp(GetFloatArrayCallback cb);

使用__declspec(dllexport)是将托管代码公开给本机代码的简单方法。但它太简单了,没有合理的方法来检测托管代码中的任何不幸事件。当出现问题时,喜欢抛出异常。所有你能看到的是“它没有工作”的例外,没有任何方法可以找到有关托管异常的任何信息。没有异常消息,无法访问Holy Stack Trace。

您将预览支持客户的难度。谁会打电话给你并告诉你“它没用”。除非你能在他的机器上运行调试器,否则你无能为力。这往往很难得到。

好吧,你有一个在你的,你至少可以用它来诊断单元测试失败。 Project + Properties,Debugging,将Debugger Type设置从“Auto”更改为“Mixed”。这启用了托管调试引擎,您现在可以查看异常详细信息。通常一个沼泽标准的“未找到文件”是一个非常常见的事故。特别是因为CLR没有充分的理由在目前存储它的目录中查找托管程序集,所以配置主appdomain是你不能做的其他事情。您可能必须将其放入GAC或移动cppunit测试运行器,使其与您的DLL位于同一目录中。 Fuslogvw.exe是诊断程序集解析问题的一个很好的实用程序,它显示了CLR查找程序集的位置以及它使用的配置。

但请随意将此单元测试结果置于“主要失败”类别中。考虑使用COM互操作或CLR托管,它们都会为您提供HRESULT错误代码并支持IErrorInfo以获取异常消息。

© www.soinside.com 2019 - 2024. All rights reserved.