Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert a JavaScript array() to an ATL/COM array?

How can I convert a JavaScript array() to an ATL/COM array without using VBArray?

What I want to convert is a new Array() to a SAFEARRAY.

like image 511
user619322 Avatar asked May 23 '26 00:05

user619322


1 Answers

Here's a code to do just that (considering you already got the JS Array object as a C++ Variant), same way as Sheng Jiang suggested earlier:

bool VariantToArray(__in const CComVariant& var, __out vector<CComVariant>& vecVars)
{
    // convert variant to dispatch object
    CComPtr<IDispatch> pDispatch = VariantToDispatch(var);
    if (!pDispatch)
        return false;

    // invoke the object to retrieve the enumerator containing object
    CComVariant varResult;
    DISPPARAMS dispparamsNoArgs = {0};
    EXCEPINFO excepInfo = {0};
    UINT uiArgErr = (UINT)-1;  // initialize to invalid arg
    HRESULT hr = pDispatch->Invoke( DISPID_NEWENUM,
                                    IID_NULL,
                                    LOCALE_USER_DEFAULT,
                                    DISPATCH_METHOD | DISPATCH_PROPERTYGET,
                                    &dispparamsNoArgs,
                                    &varResult,
                                    &excepInfo,
                                    &uiArgErr);
    if (FAILED(hr))
        return false;

    // query the retrieved interface and get the enumerator object
    CComPtr<IEnumVARIANT> pEnumVariant;
    switch (varResult.vt)
    {
        case VT_UNKNOWN:
        {
            CComPtr<IUnknown> pUnknownResult = varResult.punkVal;
            if (!pUnknownResult)
                return false;
            pEnumVariant = pUnknownResult; // implied query interface
        }
        break;

        case VT_DISPATCH:
        {
            CComPtr<IDispatch> pDispatchResult = varResult.pdispVal;
            if (!pDispatchResult)
                return false;
            pEnumVariant = pDispatchResult; // implied query interface
        }
        break;

        default:
            return false;
    }

    if (!pEnumVariant)
        return false;

    // reset enumerator to beginning of the list
    hr = pEnumVariant->Reset();
    if (FAILED(hr))
        return false;

    // enumerate and fetch items
    CComVariant varItem;
    ULONG uiFetched = 0;
    do 
    {
        // get next item
        hr = pEnumVariant->Next(1, &varItem, &uiFetched);
        if (FAILED(hr))
            return false;

        if (uiFetched == NULL) // last item
            break;

        // insert the item to the vector 
        vecVars.push_back(varItem);
    } while (true);

    return true;
}

hope that helps.

Note:
I saw a post where someone complained this doesn't work on IE9 (but it does on IE6,7,8), I checked it out myself - on IE9 (only) the Invoke method fail and return DISP_E_EXCEPTION.
So i'm still looking for a better solution.

Edited:
Here's a code that will works on all IE browsers:

bool VariantToArray(__in const CComVariant& var, __out vector<CComVariant>& vecVars)
        {
            // convert variant to dispatch object
            CComPtr<IDispatch> pDispatch = VariantToDispatch(var);
            if (!pDispatch)
                return false;

            // get DISPID of length parameter from array object
            LPOLESTR sLengthName = L"length";
            DISPID dispidLength = 0;
            HRESULT hr = pDispatch->GetIDsOfNames(IID_NULL, &sLengthName, 1, LOCALE_USER_DEFAULT, &dispidLength);
            if (FAILED(hr))
                return false;

            // get the number of elements using the DISPID of length parameter
            CComVariant varLength;
            DISPPARAMS dispParams = {0};
            hr = pDispatch->Invoke(dispidLength, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &varLength, NULL, NULL);
            if (FAILED(hr))
                return false;

            int nLength = 0; // length of the array
            bool bGotInt = VariantToInt(varLength, nLength);
            if (!bGotInt)
                return false;

            // get items of array
            for (int i=0 ; i<nLength ; ++i)
            {
                // get DISPID of item[i] from array object
                wstring strIndex = StringUtils::IntToString(i);
                DISPID dispidIndex = 0;
                LPOLESTR pIndex = reinterpret_cast<LPOLESTR>(const_cast<WCHAR *>(strIndex.data()));
                hr = pDispatch->GetIDsOfNames(IID_NULL, &pIndex, 1, LOCALE_USER_DEFAULT, &dispidIndex);
                if (FAILED(hr))
                    continue;

                CComVariant varItem;
                hr = pDispatch->Invoke(dispidIndex, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &varItem, NULL, NULL);
                if (FAILED(hr))
                    continue;

                vecVars.push_back(varItem);
            }

            return true;
        }

Enjoy :)

like image 72
Bagelzone Ha'bonè Avatar answered May 24 '26 13:05

Bagelzone Ha'bonè



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!