1

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;
}
cosmo479
  • 89
  • 2
  • 9
  • Must you use a raw `SAFEARRAY`? [CComSafeArray](http://msdn.microsoft.com/en-us/library/3xzbsee8.aspx) provides a wrapper that tends to be much easier to use. – lcs Jul 28 '14 at 21:12
  • You should use SafeArrayPutElement to put an element to the SAFEARRAY – Matt Jul 28 '14 at 21:21
  • `BSTR *theArray = new BSTR[10];` could be simplified to `BSTR theArray[10];` – M.M Jul 28 '14 at 22:38

2 Answers2

3

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;
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

&srcArray[i] doesn't do what you think it does. srcArray is a single BSTR, which is a typedef for WCHAR*. It is not an array of BSTRs. srcArray[i] refers to the ith character in theArray[0], and &srcArray[i] is the address of that character. That's how you got "test", "est" and so on.

Have fillVariant take BSTR* as its second parameter, and pass theArray, not *theArray.

On an unrelated note, your program leaks a bunch of BSTRs and a SAFEARRAY.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85