-2

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?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
maynull
  • 1,936
  • 4
  • 26
  • 46
  • 1
    Your `varArrData` appears to contain a VT_ARRAY|VT_VARIANT variant type, which means its a SAFEARRAY of VARIANT. Sooner or later a copy is going to come into play unless you retool your container to hold raw BSTR (or CComBSTR via detatch+attach), "steal" the VT_BSTR from each item, and shove it into your retooled container. Frankly I don't see it worth the trouble. Two things, btw: (1) You really should init that varArritem before sending it to the GetItemArr instance, and (2), That cast is hideous, There are ways to "properly" rehome ownsership of that SAFEARRAY into a COleSafeArray. – WhozCraig Jan 06 '20 at 16:25

1 Answers1

1

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);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you for your explanation and example code! Would it be possible to get the content of ```varArrayData``` without for loop or adding each value to ```arrItem``` seperately? I tried casting the array like: ```string arr_data = *((string*)&varArrayData);```, but it doesn't work. – maynull Jan 07 '20 at 06:18
  • 1
    @maynull no, that will not work. You have a SAFEARRAY of VARIANTs containing BSTRs. BSTR is itself a pointer, so you need to access the strings individually – Remy Lebeau Jan 07 '20 at 07:11