如何将 JavaScript array() 转换为 ATL/COM 数组?

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案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 函数

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

    声明一个 JavaScript 函数,例如function (arr) return arr.length; 声明一个 JavaScript 数组,例如[2、3、5、7、11] 以 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 以访问其元素。

【讨论】:

以上是关于如何将 JavaScript array() 转换为 ATL/COM 数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何将javascript中uint8array转成普通数组或字符串?

如何将javascript中uint8array转成普通数组或字符串

如何在 JavaScript 中将 Object 转换为 Array [] 键值对

在javascript中如何将字符串转换为数组和数组转换为字符串

javascript怎样将object类型转换成array数组

create-react-app javascript 将文件转换为 Uint8Array