Как я могу преобразовать массив JavaScript () в массив ATL / COM? - PullRequest
2 голосов
/ 16 февраля 2011

Как я могу преобразовать массив JavaScript () в массив ATL / COM без использования VBArray?

Я хочу преобразовать новый массив () в SAFEARRAY.

Ответы [ 4 ]

8 голосов
/ 29 июня 2011

Вот код для этого (учитывая, что вы уже получили объект JS Array в качестве варианта C ++), так же, как Sheng Jiang , предложенный ранее:

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;
}

надеюсь, это поможет.

Примечание:
Я видел пост, в котором кто-то жаловался, что это не работает на IE9 (но это работает на IE6,7,8), я проверил это сам - на IE9 (только) метод Invoke не работает и вернуть DISP_E_EXCEPTION.
Поэтому я все еще ищу лучшее решение.

Отредактировано:
Вот код, который будет работать во всех браузерах IE:

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;
        }

Наслаждайтесь:)

1 голос
/ 20 марта 2013

С помощью IActiveScript вы можете создать движок JavaScript в C ++ и использовать его для:

  • Создание IDispatch* указателей для функций JavaScript
  • Создание VARIANT переменных, содержащих JavaScriptобъекты
  • Передача объектов JavaScript в функции JavaScript в C ++

Используя эту технику, мы сделаем следующее:

  1. Объявим функцию JavaScript, например function (arr) { return arr.length; }
  2. Объявление массива JavaScript, например [2, 3, 5, 7, 11]
  3. Вызов функции JavaScript с массивом JavaScript в качестве ввода

Чтобы сделать эту работу, вы должны создать IActiveScriptSite.Ниже приводится консольное приложение C ++, демонстрирующее эту концепцию:

// C++ headers for ATL and Active Script Hosting.
#include <atlbase.h>
#include <atlcom.h>
#include <activscp.h>

// A minimal implementation of IActiveScriptSite.
class ATL_NO_VTABLE CScriptSite :
    public CComObjectRootEx<CComSingleThreadModel>,
    public IActiveScriptSite,
    public IActiveScriptSiteWindow
{
public:
BEGIN_COM_MAP(CScriptSite)
    COM_INTERFACE_ENTRY(IActiveScriptSite)
    COM_INTERFACE_ENTRY(IActiveScriptSiteWindow)
END_COM_MAP()
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    HRESULT FinalConstruct()
    {
        return S_OK;
    }
    void FinalRelease()
    {
    }
public:
    // IActiveScriptSite
    STDMETHOD(GetLCID)(LCID* plcid)
    {
        *plcid = 0;
        return S_OK;
    }
    STDMETHOD(GetItemInfo)(
        LPCOLESTR pstrName,
        DWORD dwReturnMask,
        IUnknown** ppiunkItem,
        ITypeInfo** ppti)
    {
        return TYPE_E_ELEMENTNOTFOUND;
    }
    STDMETHOD(GetDocVersionString)(BSTR* pbstrVersion)
    {
        *pbstrVersion = ::SysAllocString(L"1.0");
        return S_OK;
    }
    STDMETHOD(OnScriptTerminate)(
        const VARIANT* pvarResult,
        const EXCEPINFO* pexcepinfo)
    {
        return S_OK;
    }
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState)
    {
        return S_OK;
    }
    STDMETHOD(OnScriptError)(IActiveScriptError* pIActiveScriptError)
    {
        return S_OK;
    }
    STDMETHOD(OnEnterScript)(void)
    {
        return S_OK;
    }
    STDMETHOD(OnLeaveScript)(void)
    {
        return S_OK;
    }
    // IActiveScriptSiteWindow
    STDMETHOD(GetWindow)(HWND* phWnd)
    {
        *phWnd = NULL;
        return S_OK;
    }
    STDMETHOD(EnableModeless)(BOOL fEnable)
    {
        return S_OK;
    }
};

// ATL in a Console app.
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()

// Main body
int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = S_OK;
    hr = _Module.Init(ObjectMap, NULL, NULL);

    // Instantiate JavaScript engine.
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    CComObject<CScriptSite>* pScriptSite = NULL;
    hr = CComObject<CScriptSite>::CreateInstance(&pScriptSite);
    pScriptSite->AddRef();
    CComPtr<IActiveScript> spIActiveScript;
    hr = spIActiveScript.CoCreateInstance(OLESTR("JScript"));
    hr = spIActiveScript->SetScriptSite(pScriptSite);
    CComPtr<IActiveScriptParse> spIActiveScriptParse;
    hr = spIActiveScript->QueryInterface(IID_IActiveScriptParse, (void **) &spIActiveScriptParse);
    hr = spIActiveScriptParse->InitNew();
    hr = spIActiveScript->SetScriptState(SCRIPTSTATE_CONNECTED);

    // Evaluate an anonymous JavaScript function.
    CComVariant vSomeFunc;
    EXCEPINFO ei = { };
    hr = spIActiveScriptParse->ParseScriptText(
        OLESTR("(function () { return function (arr) { return arr.length; }; } )();"),  // pstrCode
        NULL,                       // pstrItemName
        NULL,                       // punkContent
        NULL,                       // pstrDelimiter
        0,                          // dwSourceContextCookie
        0,                          // ulStartingLineNumber
        SCRIPTTEXT_ISEXPRESSION,    // dwFlags
        &vSomeFunc,                 // pvarResult
        &ei                         // pexcepinfo
        );

    // Make a JavaScript array object.
    CComVariant vObject;
    hr = spIActiveScriptParse->ParseScriptText(
        OLESTR("[2,3,5,7,11]"), // pstrCode
        NULL,                       // pstrItemName
        NULL,                       // punkContent
        NULL,                       // pstrDelimiter
        0,                          // dwSourceContextCookie
        0,                          // ulStartingLineNumber
        SCRIPTTEXT_ISEXPRESSION,    // dwFlags
        &vObject,                   // pvarResult
        &ei                         // pexcepinfo
        );

    // Call the anonymous JavaScript function (gives answer of 5).
    CComVariant vResult;
    DISPPARAMS dispParams = { &vObject, 0, 1, 0 };
    hr = V_DISPATCH(&vSomeFunc)->Invoke(
        DISPID_VALUE,
        IID_NULL,
        0,
        DISPATCH_METHOD,
        &dispParams,
        &vResult,
        &ei,
        NULL);

    // Release variables.
    hr = vSomeFunc.Clear();
    hr = vObject.Clear();
    hr = vResult.Clear();

    // Release JavaScript engine.
    spIActiveScriptParse = NULL;
    spIActiveScript = NULL;
    pScriptSite->Release();
    pScriptSite = NULL;
    ::CoUninitialize();
    return 0;
}

Чтобы ответить на исходный вопрос об афишах, нам нужно создать еще одну функцию JavaScript для извлечения элементов из массива, скажем, function (arr,idx) { return arr[idx]; }.Теперь у нас достаточно функций для обхода массивов JavaScript в C ++.

0 голосов
/ 17 февраля 2011

Массив javascript - это VARIANT, который содержит указатель IDispatch. Реализация IDispatch имеет метод перечислителя с идентификатором отправки DISPID_NEWENUM , если вы используете C ++, вы также можете запросить IEnumVARIANT из объекта для доступа к его элементам.

0 голосов
/ 16 февраля 2011

Я изучал это в прошлом и, насколько я знаю, это невозможно.Ваша единственная возможность в скрипте - использовать VBScript и VBArray.

...