异常将VARIANT中的SAFEARRAY传递给C#来自C ++ COM服务器

时间:2012-02-21 23:17:12

标签: c# c++ com variant

我花了最后一天搜索文档,查看论坛帖子,并使用谷歌搜索来尝试做一些我猜可以使用正确的信息轻松完成的事情。

我有一个非常大的现有C ++应用程序,它有一个已定义的COM服务器,暴露了许多方法。我试图在C#应用程序中使用这些COM方法(我在C ++中很有经验,但是C#新手)。

所以在我的VS2010 C#应用程序中,我添加了COM服务器作为参考。 COM方法在对象浏览器中可见,并且传递单值字符串,浮点数和整数似乎工作正常。

但我感到困惑,试图将从C ++ COM服务器传出的SAFEARRAY值读入C#应用程序。最终我需要将字符串数组从C ++服务器传递到C#app,但是在测试传递浮点数组时,我有代码构建但在我尝试转换包含浮点数组的System.Object时出现以下异常而失败to(float []),

“exception {System.InvalidCastException:无法将'System.Object []'类型的对象强制转换为'System.Single []'。”

通过intellisence,我可以看到Object包含正确的8760长浮点数,但我无法在C#中访问该数据。

这是C#端的代码(d2RuleSet是DOE2Com COM服务器中定义的接口)。上面的例外情况将在下面的最后一行引发。

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;

以下是定义C ++ COM方法的idl文件中的部分

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

下面是加载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;
    }
}

提前感谢您对这个问题的帮助,我觉得我花了太多时间在一个简单的问题上。另外,请发布任何链接或书籍,其中包含本地C ++和C#之间的互操作(我想我已经通过大多数Visual Studio / MSDN文档进行了ping操作,但也许我也错过了一些内容)。

-----------------原始问题结束-------------------------- ---------------------------- 我正在编辑,以便从下面的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;
}

1 个答案:

答案 0 :(得分:1)

编组的SAFEARRAY是System.Object[];你无法引用 - 转换为System.Single[]。你必须投射各个元素。这可以工作,假设参数数组的所有元素实际上都是盒装浮点数:

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;
}

你可以使用Linq少打字,但是因为你是C#新手,我认为更基本的解决方案可能会更好。

修改

由于您在评论中表明您的object[]被引用为object,因此以下是使用示例:

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

编辑2

使用Linq的简短解决方案:

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