Search code examples
c++printingvariantbstrsafearray

Iterating through a VARIANT/SAFEARRAY of BSTR to Assign Values and Print to Console in C++


I'm attempting to fill a SAFEARRAY of 10 indexes of BSTR type with the value "test" and print out to console the value of each index of the SAFEARRAY after it has been assigned to verify correctness. I ran the debugger and got these values below for the first 5 indexes (my SAFEARRAY is called sa). Somehow I am iterating the SAFEARRAY incorrectly or using the wrong types, each index should be "test". Any advice on what im doing wrong?

sa[0] = "test"
sa[1] = "est"
sa[2] = "st"
sa[3] = "t"
sa[4] = ""

....

#include <iostream>
#include <string>
#include <Windows.h>
#include <atlbase.h>
#include <comutil.h>
#include <string.h>
#include <stdio.h>

using namespace std;

void fillVariant(VARIANT& varIn, BSTR &srcArray);

int main()
{

    BSTR *theArray = new BSTR[10];
    for(int i = 0 ; i < 10; i++)
    {

        theArray[i] = SysAllocString(L"test");
    }

    VARIANT variantArray;
    fillVariant(variantArray, *theArray);


    return 0;
}

void fillVariant(VARIANT& varIn, BSTR &srcArray)
{
    VARIANT *variantArray = &varIn;
    VariantInit(variantArray);
    variantArray->vt = VT_ARRAY|VT_BSTR;

    SAFEARRAY* sa;
    SAFEARRAYBOUND aDim[1]; 
    aDim[0].lLbound = 0; 
    aDim[0].cElements = 10;

    sa = SafeArrayCreate(VT_BSTR, 1, aDim);

    BSTR* dwArray = NULL;
    SafeArrayAccessData(sa, (void**)&dwArray);

    for(int i = 0; i < 10; i++)
    {
        dwArray[i] = &srcArray[i];

        BSTR tmp = (BSTR) dwArray[i];
        std::wstring ws(tmp);
        //std::wstring ws(*dwArray[i], SysStringLen(dwArray[i]));
        std::wcout << ws << endl;

    }
    SafeArrayUnaccessData(sa);
    variantArray->parray = sa;
}

Solution

  • You are not filling in the VARIANT correctly. Try this instead:

    #include <iostream>
    #include <string>
    #include <Windows.h>
    #include <atlbase.h>
    #include <comutil.h>
    #include <string.h>
    #include <stdio.h>
    
    using namespace std;
    
    void fillVariant(VARIANT& varIn, BSTR *srcArray, int srcArrayLen);
    
    int main()
    {
        BSTR *theArray = new BSTR[10];
        for(int i = 0 ; i < 10; i++)
        {
            theArray[i] = SysAllocString(L"test");
        }
    
        VARIANT variantArray;
        fillVariant(variantArray, theArray, 10);
    
        // don't forget to free memory when done!
        // note: the VARIANT owns the BSTRs, so DON'T free them!
        VariantClear(&variantArray);
        delete[] theArray;
    
        return 0;
    }
    
    void fillVariant(VARIANT& varIn, BSTR *srcArray, int srcArrayLen)
    {
        VARIANT *variantArray = &varIn;
        VariantInit(variantArray);
    
        SAFEARRAYBOUND aDim[1]; 
        aDim[0].lLbound = 0; 
        aDim[0].cElements = srcArrayLen;
    
        SAFEARRAY* sa = SafeArrayCreate(VT_BSTR, 1, aDim);
        if (sa)
        {    
            BSTR* dwArray = NULL;
            SafeArrayAccessData(sa, (void**)&dwArray);
    
            for(int i = 0; i < srcArrayLen; i++)
            {
                // note: passing ownership, NOT making a copy
                dwArray[i] = srcArray[i];
    
                //std::wstring ws(dwArray[i], SysStringLen(dwArray[i]));
                std::wcout << dwArray[i] << endl;
            }
    
            SafeArrayUnaccessData(sa);
    
            variantArray->vt = VT_ARRAY|VT_BSTR;
            variantArray->parray = sa;
        }
    }
    

    Alternatively:

    #include <iostream>
    #include <string>
    #include <Windows.h>
    #include <atlbase.h>
    #include <comutil.h>
    #include <string.h>
    #include <stdio.h>
    
    using namespace std;
    
    void fillVariant(VARIANT& varIn, BSTR *srcArray, int srcArrayLen);
    
    int main()
    {
        BSTR *theArray = new BSTR[10];
        for(int i = 0 ; i < 10; i++)
        {
            theArray[i] = SysAllocString(L"test");
        }
    
        VARIANT variantArray;
        fillVariant(variantArray, theArray, 10);
    
        // don't forget to free memory when done!
    
        VariantClear(&variantArray);
    
        // note: the VARIANT DOES NOT own the BSTRs, so DO free them!
        for(int i = 0 ; i < 10; i++)
        {
            SysFreeString(theArray[i]);
        }
        delete[] theArray;
    
        return 0;
    }
    
    void fillVariant(VARIANT& varIn, BSTR *srcArray, int srcArrayLen)
    {
        VARIANT *variantArray = &varIn;
        VariantInit(variantArray);
    
        SAFEARRAYBOUND aDim[1]; 
        aDim[0].lLbound = 0; 
        aDim[0].cElements = srcArrayLen;
    
        SAFEARRAY* sa = SafeArrayCreate(VT_BSTR, 1, aDim);
        if (sa)
        {    
            for(LONG i = 0; i < srcArrayLen; i++)
            {
                // note: makes a copy, DOES NOT pass ownership!
                SafeArrayPutElement(sa, &i, srcArray[i]);
    
                //std::wstring ws(srcArray[i], SysStringLen(srcArray[i]));
                std::wcout << srcArray[i] << endl;
            }
    
            variantArray->vt = VT_ARRAY|VT_BSTR;
            variantArray->parray = sa;
        }
    }