【问题标题】:How to display values from a VARIANT with a SAFEARRAY of BSTRs如何使用 BSTR 的 SAFEARRAY 显示来自 VARIANT 的值
【发布时间】:2024-01-29 12:55:01
【问题描述】:

我正在开发一个带有函数的 COM 对象库,该函数返回 VARIANTSAFEARRAYBSTRs。如何显示来自此 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);

【问题讨论】:

    标签: delphi c++builder bstr safearray


    【解决方案1】:

    您可以使用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;
    

    当您使用 BSTR 的 Variant safearray 调用它时,它将自动转换为 TWideStringDynArray。不兼容的 Variant 会导致运行时错误EVariantInvalidArgError

    要检查 Variant 是否包含 BSTR 的安全数组,您可以这样做:

    IsOK := VarIsArray(V) and (VarArrayDimCount(V) = 1) and (VarType(V) and varTypeMask = varOleStr);
    

    【讨论】:

      【解决方案2】:
      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
      

      如果您通过后期绑定 (CreateOleObject) 创建 ComFunction,您应该使用:

      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;
      

      【讨论】:

        【解决方案3】:

        如何显示此 VARIANT 实例的值并将其保存在 TStringList 中?

        COM VARIANT 结构具有 parraypparray 数据成员,它们是指向 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 指针,就可以使用SafeArray API 访问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 为空,则 GetValues() 将返回一个空的 Variant 开始。

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

        TVarData::VStringAnsiString&amp; 引用,而不是 wchar_t* 指针。要将 VCL Variant(不是 COM VARIANT)转换为 String,只需按原样分配它,然后让 RTL 为您计算细节:

        String mystr = V;
        

        【讨论】: