简体   繁体   English

从C ++ COM Server将VARIANT中的SAFEARRAY传递给C#的异常

[英]Exception Passing SAFEARRAY in VARIANT to C# From C++ COM Server

I have spent the last day searching documentation, reviewing forum posts, and googling to try to do something that I am guessing could be easily done with the right information. 最后一天,我一直在搜索文档,查看论坛帖子并进行谷歌搜索以尝试做一些我认为可以通过正确的信息轻松完成的事情。

I have a very large existing C++ application that has an already defined COM server with many methods exposed. 我有一个非常大的现有C ++应用程序,它已经定义了COM服务器,并且公开了许多方法。 I am trying to use those COM methods in a C# application (I am experienced in C++, but a C# newbie). 我正在尝试在C#应用程序中使用那些COM方法(我在C ++中很有经验,但在C#中是新手)。

So in my VS2010 C# application, I add the COM server as a reference. 因此,在我的VS2010 C#应用程序中,我将COM服务器添加为参考。 COM Methods are visible in the object browser, and passing single-valued strings, floats, and ints seems to work fine. COM方法在对象浏览器中可见,并且传递单值字符串,浮点数和整数似乎可以正常工作。

But I am stumped trying to read SAFEARRAY values passed out of the C++ COM server into the C# application. 但是我很困惑,试图读取从C ++ COM服务器传递到C#应用程序中的SAFEARRAY值。 Eventually I need to pass string arrays from C++ server to the C# app, but in just testing passing an array of floats, I have code that builds but fails with the following exception when I try to cast the System.Object containing the array of floats to (float[]) , 最终,我需要将字符串数组从C ++服务器传递到C#应用程序,但是在仅测试传递浮点数数组的过程中,我的代码可以生成,但是当我尝试转换包含浮点数数组的System.Object时失败,并出现以下异常漂浮[]) ,

"exception {System.InvalidCastException: Unable to cast object of type 'System.Object[]' to type 'System.Single[]'." “异常{System.InvalidCastException:无法将类型为“ System.Object []”的对象转换为类型为“ System.Single []”。”

With intellisence I can see that the Object contains the correct 8760 long array of floats, but I am unable to access that data in C#. 通过智能,我可以看到对象包含正确的8760长浮点数组,但是我无法在C#中访问该数据。

Here is the code on the C# side (d2RuleSet is an interface defined in the DOE2Com COM server). 这是C#端的代码(d2RuleSet是DOE2Com COM服务器中定义的接口)。 The exception above is thrown on the last line below. 上面的异常抛出在下面的最后一行。

DOE2ComLib.DOE2Com d2RuleSet;

d2RuleSet = new DOE2ComLib.DOE2Com();

System.Int32 i_Series =0;

System.Object pv_WeatherData;
float[] faWeatherData;

iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);
Type typeTest;
typeTest = pv_WeatherData.GetType();
int iArrayRank = typeTest.GetArrayRank();
Type typeElement = typeTest.GetElementType();                    
faWeatherData = (float[])pv_WeatherData;

Below is the section in the idl file defining the C++ COM method 以下是idl文件中定义C ++ COM方法的部分

HRESULT GetWeatherData( [in] int iSeries, [out] VARIANT* pvWeatherData,    [out,retval]      int * piErrorCode);

Below is the C++ code where the VARIANT data is loaded. 以下是加载VARIANT数据的C ++代码。

void CDOE2BaseClass::GetWeatherData( int iSeries, VARIANT* pvWeatherData, int*   piErrorCode)
{
    *piErrorCode = 0;

    if (iSeries < 0 || iSeries >= D2CWS_NumSeries)
       *piErrorCode = 1;
    else if (m_faWeatherData[iSeries] == NULL)
       *piErrorCode = 3;
    else
   {
       SAFEARRAYBOUND rgsaBound;
       rgsaBound.lLbound = 0;
       rgsaBound.cElements = 8760;

      // First lets create the SafeArrays (populated with VARIANTS to ensure    compatibility with VB and Java)
       SAFEARRAY* pSAData = SafeArrayCreate( VT_VARIANT, 1, &rgsaBound );
       if( pSAData == NULL ) {
 #ifndef _DOE2LIB
          _com_issue_error( E_OUTOFMEMORY);
#else
//RW_TO_DO - Throw custom Lib-version exception
          OurThrowDOE2LibException(-1,__FILE__,__LINE__,0,"OUT OF MEMORY");
 #endif //_DOE2LIB
       }

      for (long hr=0; hr<8760; hr++)
      {
          COleVariant vHrResult( m_faWeatherData[iSeries][hr] );
          SafeArrayPutElement( pSAData, &hr, vHrResult );
       }

      // Now that we have populated the SAFEARRAY, assign it to the VARIANT pointer that we are returning to the client.
       V_VT( pvWeatherData ) = VT_ARRAY | VT_VARIANT;
       V_ARRAY( pvWeatherData ) = pSAData;
    }
}

Thanks in advance for help with this problem, I feel like I spent too much time on what should be a simple problem. 在此先感谢您对这个问题的帮助,我觉得我花了太多时间在一个简单的问题上。 Also please post up any links or books that that cover interop between native C++ and C# well (I think I have already ping-ponged through most of the Visual Studio/MSDN documentation, but maybe I missed something there too). 另外,也请张贴任何涵盖本机C ++和C#之间互操作的链接或书籍(我想我已经在大多数Visual Studio / MSDN文档中进行了简短的评论,但也许我也错过了)。

-----------------End Of Original Question------------------------------------------------------ I am editing to post the code from phoog's successful solution below, so others can read and use it. -----------------原始问题的结尾----------------------------- -------------------------我正在编辑以下来自phoog成功解决方案的代码,以便其他人可以阅读和使用。

int iOut = 0;
System.Int32 i_Series =0;
System.Object pv_WeatherData = null;

iOut = d2RuleSet.GetWeatherData(i_Series, out pv_WeatherData);
Type typeTest;
typeTest = pv_WeatherData.GetType();
int iArrayRank = typeTest.GetArrayRank();
Type typeElement = typeTest.GetElementType();

//float[] faWeatherData = (float[])pv_WeatherData;
float[] faWeatherData = ConvertTheArray((object[])pv_WeatherData);

.... ....

float[] ConvertTheArray(object[] inputArray)
{
   float[] result = new float[inputArray.Length];
   for (var index = 0; index < result.Length; index++)
      result[index] = (float)inputArray[index];
   return result;
}

The marshalled SAFEARRAY is a System.Object[] ; 封送的SAFEARRAY是System.Object[] you can't reference-convert that to a System.Single[] . 您不能将其引用转换为System.Single[] You have to cast the individual elements. 您必须转换单个元素。 This would work, assuming that all the elements of the argument array are in fact boxed floats: 假设参数数组的所有元素实际上都是盒装浮点数,这将起作用:

float[] ConvertTheArray(object[] inputArray)
{
    float[] result = new float[inputArray.Length];
    for (var index = 0; index < result.Length; index++)
        result[index] = (float)inputArray[index];
    return result;
}

You could do that with a lot less typing using Linq, but as you're a C# newbie I thought a more basic solution might be better. 您可以使用Linq进行更少的键入操作来实现此目的,但是由于您是C#新手,因此我认为采用更基本的解决方案可能会更好。

EDIT 编辑

Since you indicated in your comment that your object[] is referenced as an object , here's the usage example: 由于您在注释中指出object[]被引用为object ,因此这是用法示例:

object obj = GetMarshalledArray();
float[] floats = ConvertTheArray((object[])obj);

EDIT 2 编辑2

A shorter solution using Linq: 使用Linq的较短解决方案:

float[] ConvertTheArray(object[] inputArray)
{
    return inputArray.Cast<float>().ToArray();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM