Search code examples
c++windowsvariant

How to directly access to what's in VARIANT variables in C++?


My program uses an external ocx library and receives data through it. The code below shows how it works.

    VARIANT    varArrItem, varArrData;

    ocx_instance.GetItemArr(real, &varArrItem);      // the library provides GetItemArr
                                                     // 1) receives data 
    long lLBound, lUBound;
    VARIANT varItem, varData;

    long index[2];
    index[0] = 0;
    index[1] = 0;

    COleSafeArray* pSafeItemArr = (COleSafeArray*)&varArrItem;    // 2) casts varArrItem to COleSafeArray
    CString strItem;
    CStringArray arrItem;

    pSafeItemArr->GetLBound(1, &lLBound);
    pSafeItemArr->GetUBound(1, &lUBound);

    int nItemCnt = (lUBound - lLBound + 1);
    for (int i = 0; i < nItemCnt; i++)
    {
        index[0] = i;
        VariantInit(&varItem);

        pSafeItemArr->GetElement(index, (void *)&varItem);    // 3) gets its values using GetElement
        strItem = varItem.bstrVal;
        arrItem.Add(strItem);

        VariantClear(&varItem);
    }

A big problem of the program is that this code is run whenever new data arrives, which is quite often, and it consumes a lot of resources. So, I'd like to simplify the code and get just contents of varArrItem, as strings or an array of structs of strings, for example.

varArrItem.vt gives me 8204 and it's said that it consists of 8192(VT_ARRAY) and 12(VT_VARIANT). I'm stuck here and don't know what to do after this. How can I simply get what's in them? Is it possible to access to them without using COleSafeArray?


Solution

  • You don't NEED to use COleSafeArray, it is just a wrapper for convenience. You could just extract the SAFEARRAY* pointer directly from varArrItem and then use the SafeArray APIs directly: SafeArrayGet(L|U)Bound(), SafeArrayGetElement(), etc, though if performance is important then consider using SafeArrayAccessData() to access the VARIANT[] array directly, and thus its BSTR pointers. The less copying of data you do, the faster the code will run. The only copy of data this code actually needs to make is the assignment of the initial VARIANT and each CString you add to the CStringArray:

    VARIANT varArray;
    ocx_instance.GetItemArr(real, &varArray);
    
    LPSAFEARRAY psa = varArrar.parray;
    
    LONG lLBound, lUBound;
    SafeArrayGetLBound(psa, 1, &lLBound);
    SafeArrayGetUBound(psa, 1, &lUBound); 
    
    CStringArray arrItem;
    
    VARIANT *varArrayData;
    if (SUCCEEDED(SafeArrayAccessData(psa, (void**) &varArrayData)))
    {
        int nItemCnt = (lUBound - lLBound + 1);
    
        for (int i = 0; i < nItemCnt; i++)
        {
            CString strItem = varArrayData[i].bstrVal;
            arrItem.Add(strItem);
        }
    
        SafeArrayUnaccessData(psa);
    }