Pergunta

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
}

Line (1): fields.GetAt(i) returns CComVariant. When I try to assign this value to CComVariant fld called copy constructor and method CComVariant::Copy inside the copy constructor. It raise an exception ("bad variable type", DISP_E_BADVARTYPE (0x80020008L)). At the same time the line (2) works well. What's wrong with line (1), and how to fix it.

EDIT: This is code for get_Field (filling 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;
} 
Foi útil?

Solução

You create an array of IUnknowns and then you are trying to interpret is as array of VARIANT. Those should be the same types, you want either array of unknowns and you pack interface into CComVariant before putting it into array in the getter, or otherwise caller will deal with array of interfaces.

As you discovered a mismatch between actual array element type and the type you are casting it to, you will need to update your getter implementation and caller code to match one another.

My personal preference is to create an array of variants, VT_ARRAY | VT_VARIANT and put the array into [out] VARIANT* argument. The caller would unwind it back from variant to array, check array type, and then obtain the elements. This is a minimal overhead, and the code around VARIANT type is best - on the average - in terms of interoperability (in your particular case you definitely might be good with raw types, and without variants at all).

Outras dicas

I think you are having the typical off-by-one error. ie is one element past the last one, so fields.GetAt(i) when i == ie will return you a non-existent element. Try replacing i <= ie with i < ie in the for loop termination condition and see if it works.

Even though the CComVariant copy constructor fails for i == ie, the next assignment might seem to work because the CComQIPtr<ITabField> copy constructor may be happy with whatever is retrieved at the punkVal field of the spurious CComVariant.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top