Ошибка: «неверный тип переменной» в CComVariant :: Copy при итерации через CComSafeArray - PullRequest
2 голосов
/ 03 ноября 2011
CComSafeArray<VARIANT> fields;
hr = _tab_file->get_Fields(fields.GetSafeArrayPtr());

for ( LONG i = fields.GetLowerBound(), ie = fields.GetUpperBound(); i <= ie; ++i)
{
    CComVariant fld = fields.GetAt(i); // (1) raises DISP_E_BADVARTYPE (0x80020008L)

    // Next code works fine                
    CComQIPtr<ITabField> field = fields.GetAt(i).punkVal; // (2) Ok
    _bstr_t fieldName;
    hr = field->get_Name(fieldName.GetAddress());
    ::OutputDebugString(fieldName + _T("\n")); // Ok
}

Строка (1): fields.GetAt(i) возвращает CComVariant.Когда я пытаюсь присвоить это значение CComVariant fld, вызывается конструктор копирования и метод CComVariant::Copy внутри конструктора копирования.Возникает исключение («неверный тип переменной», DISP_E_BADVARTYPE (0x80020008L)).В то же время линия (2) работает хорошо.Что не так со строкой (1) и как ее исправить.

РЕДАКТИРОВАТЬ: Это код для get_Field (заполнение SAFEARRAY).

STDMETHODIMP TabFile::get_Fields( SAFEARRAY** fields )
{
  if(mapInfoFile_ == 0)
    return E_UNEXPECTED;
  int fieldCount = getFieldCount();
  SAFEARRAY* arr = ::SafeArrayCreateVector(VT_UNKNOWN, 0, fieldCount);
  for(LONG i = 0; i < fieldCount; i++)
  {
    QField* field = getQField(i);
    ITabField* tabField = TabField::CreateInstance();
    tabField->put_Name(_bstr_t(field->GetNameRef()));
    tabField->put_Type(field->GetNativeFieldType(i));
    ::SafeArrayPutElement(arr, &i, tabField);
    tabField->Release();
  }
  *fields = arr;
  return S_OK;
} 

Ответы [ 2 ]

2 голосов
/ 03 ноября 2011

Вы создаете массив IUnknown с, а затем пытаетесь интерпретировать его как массив VARIANT.Это должны быть одинаковые типы, вам нужно либо массив неизвестных, и вы упаковываете интерфейс в CComVariant перед тем, как поместить его в массив в геттере, иначе вызывающий будет иметь дело с массивом интерфейсов.

КакВы обнаружили несоответствие между фактическим типом элемента массива и типом, к которому вы его приводите. Вам нужно обновить реализацию геттера и код вызывающего абонента, чтобы они соответствовали друг другу.

Мое личное предпочтение - создать массив вариантов., VT_ARRAY | VT_VARIANT и поместите массив в аргумент [out] VARIANT*.Вызывающая сторона разматывает его обратно от варианта к массиву, проверяет тип массива и затем получает элементы.Это минимальные издержки, и код с типом VARIANT лучше всего - в среднем - с точки зрения взаимодействия (в вашем конкретном случае вам определенно может быть хорошо с необработанными типами и вообще без вариантов).

0 голосов
/ 03 ноября 2011

Я думаю, что вы имеете типичную ошибку по одному.ie - это один элемент после последнего, поэтому fields.GetAt(i), когда i == ie вернет вам несуществующий элемент.Попробуйте заменить i <= ie на i < ie в условии завершения цикла for и посмотрите, работает ли он.

Даже если конструктор копирования CComVariant не работает для i == ie, может показаться, что следующее назначение работаетпотому что конструктор копирования CComQIPtr<ITabField> может быть доволен тем, что извлекается в поле punkVal ложного CComVariant.

...