Исключение при передаче SAFEARRAY в VARIANT на C # с C ++ COM-сервера - PullRequest
2 голосов
/ 22 февраля 2012

Я потратил последний день на поиск документации, просмотр сообщений на форуме и поиск в Google, чтобы попытаться сделать что-то, что, как я предполагаю, может быть легко сделано с правильной информацией.

У меня очень большое существующее приложение C ++на котором уже определен COM-сервер со многими методамиЯ пытаюсь использовать эти методы COM в приложении C # (у меня есть опыт работы с C ++, но я новичок в C #).

Так что в моем приложении VS2010 C # я добавляю сервер COM в качестве ссылки.Методы COM видны в браузере объектов, и передача однозначных строк, чисел с плавающей запятой и целых чисел, кажется, работает нормально.

Но я в тупике, пытаясь прочитать значения SAFEARRAY, переданные из COM-сервера C ++, в C #приложение.В конце концов мне нужно передать строковые массивы с сервера C ++ в приложение C #, но при простом тестировании передачи массива с плавающей точкой у меня есть код, который строит, но происходит сбой со следующим исключением, когда я пытаюсь привести System.Object, содержащий массив с плавающей точкойto (float []),

"exception {System.InvalidCastException: невозможно преобразовать объект типа 'System.Object []' в тип 'System.Single []'."

С помощью intellisence я вижу, что Object содержит правильный массив чисел с плавающей запятой длиной 8760, но я не могу получить доступ к этим данным в C #.

Вот код на стороне C # (d2RuleSet - это интерфейс, определенный вCOM-сервер DOE2Com).Выше приведено исключение в последней строке ниже.

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;

Ниже приведен раздел в файле idl, определяющий метод C ++ COM

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

Ниже приведен код C ++, где VARIANTданные загружены.

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, но, возможно, я там тоже что-то пропустил).

----------------- Конец оригинального вопроса ------------------------------------------------------ Я редактирую, чтобы опубликовать код из успешного решения 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 Ответ

1 голос
/ 22 февраля 2012

маршаллированный 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();
}
...