【问题标题】:How can I convert a JavaScript array() to an ATL/COM array?如何将 JavaScript array() 转换为 ATL/COM 数组?
【发布时间】:2011-06-28 05:37:14
【问题描述】:

如何在不使用 VBArray 的情况下将 JavaScript array() 转换为 ATL/COM 数组?

我想将一个新的 Array() 转换为 SAFEARRAY。

【问题讨论】:

    标签: javascript c++ arrays atl


    【解决方案1】:

    这里有一个代码可以做到这一点(考虑到您已经将 JS Array 对象作为 C++ 变体),与之前 Shengjiang 建议的方式相同:

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

    希望对您有所帮助。

    注意:
    我看到了someone complained this doesn't work on IE9 的帖子(但它在 IE6、7、8 上确实如此),我自己检查了它 - 在 IE9(仅)上调用方法 fail 并返回 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;
            }
    

    享受:)

    【讨论】:

    • 非常感谢,我打算放弃并使用逗号分隔的字符串,但这段代码确实有效(至少在 IE11 上)!只是想添加 - 代码使用 VariantToDispatch 和 VariantToInt 函数,结果非常简单。在前者中,您只需检查是否 vt == VT_DISPATCH,如果是,则返回 pdispVal。后一个有点棘手——你只需使用类似VariantChangeType(&amp;newvar,&amp;inputvar,0,VT_I4) 的东西,如果成功就得到newvar.intVal。我希望我可以插入一个代码 sn-p 但我认为 SO 不允许在 cmets 中执行此操作。
    • 谢谢@AndrewSimontsev,你让我开心:)
    【解决方案2】:

    使用IActiveScript,您可以在 C++ 中实例化 JavaScript 引擎并将其用于:

    • 为 JavaScript 函数创建 IDispatch* 指针
    • 制作包含 JavaScript 对象的 VARIANT 变量
    • 将 JavaScript 对象传递给 C++ 中的 JavaScript 函数

    使用这种技术,我们将执行以下操作:

    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]; }。现在我们有足够的函数在 C++ 中遍历 JavaScript 数组。

    【讨论】:

      【解决方案3】:

      我过去曾对此进行过调查,据我所知这是不可能的。脚本中您唯一的选择是使用 VBScript 和 VBArray。

      【讨论】:

        【解决方案4】:

        javascript 数组是一个包含 IDispatch 指针的 VARIANT。 IDispatch 实现在调度 id DISPID_NEWENUM 处有一个枚举器方法 . 如果您使用 C++,您还可以从对象中查询 IEnumVARIANT 以访问其元素。

        【讨论】:

          猜你喜欢
          • 2012-02-22
          • 1970-01-01
          • 1970-01-01
          • 2014-10-24
          • 2021-09-11
          • 2015-11-03
          • 1970-01-01
          • 2016-06-26
          • 2011-09-07
          相关资源
          最近更新 更多