Как отобразить значения из VARIANT с SAFEARRAY BSTR - PullRequest
8 голосов
/ 19 декабря 2011

Я работаю над библиотекой COM-объектов с функцией, которая возвращает VARIANT с SAFEARRAY из BSTR с. Как я могу отобразить значения из этого VARIANT экземпляра и сохранить его внутри TStringList? Я попытался найти в сети без четкого ответа.

Я попробовал следующее безуспешно:

Variant V;
String mystr;

VarClear(V);
TVarData(V).VType = varOleStr;
V = ComFunction->GetValues();  //<<<<----- V is empty
mystr = (wchar_t *)(TVarData(V).VString);
Memo1->Lines->Add(mystr);
VarClear(V);

Ответы [ 3 ]

4 голосов
/ 19 декабря 2011
uses ActiveX;

var
  VSafeArray: PSafeArray;
  LBound, UBound, I: LongInt;
  W: WideString;
begin
  VSafeArray := ComFunction.GetValues();
  SafeArrayGetLBound(VSafeArray, 1, LBound);
  SafeArrayGetUBound(VSafeArray, 1, UBound);
  for I := LBound to UBound do
  begin
    SafeArrayGetElement(VSafeArray, I, W);
    Memo1.Lines.Add(W);
  end;
  SafeArrayDestroy(VSafeArray); // cleanup PSafeArray

если вы создаете ComFunction с помощью позднего связывания (CreateOleObject), вы должны использовать:

var
  v: Variant;
v := ComFunction.GetValues;
for i := VarArrayLowBound(v, 1) to VarArrayHighBound(v, 1) do
begin 
  W := VarArrayGet(v, [i]);
  Memo1.Lines.Add (W);
end;
4 голосов
/ 19 декабря 2011

Вы можете использовать TWideStringDynArray и позволить Delphi выполнить преобразование:

procedure LoadStringsFromVariant(const Values: TWideStringDynArray; Strings: TStrings);
var
  I: Integer;
begin
  Strings.BeginUpdate;
  try
    for I := Low(Values) to High(Values) do
      Strings.Add(Values[I]);
  finally
    Strings.EndUpdate;
  end;
end;

Когда вы вызываете это с помощью своего Variant safearray BSTR, оно автоматически преобразуется в TWideStringDynArray.Несовместимый вариант вызовет ошибку времени выполнения EVariantInvalidArgError.

Чтобы проверить, содержит ли вариант безопасный массив BSTR, вы можете сделать следующее:

IsOK := VarIsArray(V) and (VarArrayDimCount(V) = 1) and (VarType(V) and varTypeMask = varOleStr);
2 голосов
/ 20 декабря 2011

Как я могу отобразить значения из этого экземпляра VARIANT и сохранить его в TStringList?

Структура COM VARIANT имеет элементы данных parray и pparray, которые являются указателями на SAFEARRAY, например:

VARIANT V;
LPSAFEARRAY sa = V_ISBYREF(&V) ? V_ARRAYREF(&V) : V_ARRAY(&V);

Класс VCL Variant, с другой стороны, имеет определенный оператор преобразования LPSAFEARRAY, так что вы можете назначить его напрямую (но только если в поле Variant.VType, в котором отсутствует флаг varByRef, есть), например:

Variant V;
LPSAFEARRAY sa = V;

В любом случае, когда у вас есть указатель SAFEARRAY, используйте API SafeArray для доступа к значениям BSTR, например:

bool __fastcall VariantToStrings(const Variant &V, TStrings *List)
{
    // make sure the Variant is holding an array
    if (!V_ISARRAY(&V)) return false;

    // get the array pointer
    LPSAFEARRAY sa = V_ISBYREF(&V) ? V_ARRAYREF(&V) : V_ARRAY(&V);

    // make sure the array is holding BSTR values
    VARTYPE vt;
    if (FAILED(SafeArrayGetVartype(sa, &vt))) return false;
    if (vt != VT_BSTR) return false;

    // make sure the array has only 1 dimension
    if (SafeArrayGetDim(sa) != 1) return false;

    // get the bounds of the array's sole dimension
    LONG lBound = -1, uBound = -1;
    if (FAILED(SafeArrayGetLBound(sa, 0, &lBound))) return false;
    if (FAILED(SafeArrayGetUBound(sa, 0, &uBound))) return false;

    if ((lBound > -1) && (uBound > -1))
    {
        // access the raw data of the array
        BSTR *values = NULL;
        if (FAILED(SafeArrayAccessData(sa, (void**)&values))) return false;
        try
        {
            List->BeginUpdate();
            try
            {
                // loop through the array adding the elements to the list
                for (LONG idx = lBound; l <= uBound; ++idx)
                {
                    String s;
                    if (values[idx] != NULL)
                        s = String(values[idx], SysStringLen(values[idx]));
                    List->Add(s);
                }
            }
            __finally
            {
                List->EndUpdate();
            }
        }
        __finally
        {
            // unaccess the raw data of the array
            SafeArrayUnaccessData(sa);
        }
    }

    return true;
}

VarClear (V); TVarData (V) .VType = varOleStr;

Тебе это совсем не нужно. Класс VCL Variant инициализирует себя в пустом состоянии, и нет необходимости присваивать VType, поскольку вы сразу присваиваете новое значение всему Variant.

V = ComFunction-> GetValues ​​(); // <<<< ----- V пусто </strong>

Если V пусто, то GetValues() возвращает пустое Variant для начала.

mystr = (wchar_t *) (TVarData (V) .VString);

TVarData::VString является ссылкой AnsiString&, а не указателем wchar_t*. Чтобы преобразовать VCL Variant (не COM VARIANT) в String, просто назначьте его как есть и пусть RTL проработает детали для вас:

String mystr = V;
...