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.
With IActiveScript
you can instantiate a JavaScript engine in C++ and use it to:
IDispatch*
pointers for JavaScript functionsVARIANT
variables containing JavaScript objectsUsing this technique, we shall do the following:
function (arr) { return arr.length; }
To make this work, you must create an IActiveScriptSite
. The following is a C++ console application that demonstrates this concept:
// C++ headers for ATL and Active Script Hosting.
#include
#include
#include
// A minimal implementation of IActiveScriptSite.
class ATL_NO_VTABLE CScriptSite :
public CComObjectRootEx,
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* pScriptSite = NULL;
hr = CComObject::CreateInstance(&pScriptSite);
pScriptSite->AddRef();
CComPtr spIActiveScript;
hr = spIActiveScript.CoCreateInstance(OLESTR("JScript"));
hr = spIActiveScript->SetScriptSite(pScriptSite);
CComPtr 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;
}
To answer the original posters question, we then need to create another JavaScript function to extract elements from the array, say, function (arr,idx) { return arr[idx]; }
. Now we have enough functions to walk JavaScript arrays in C++.