I understand this is a very old post, but I myself stumbled upon the same issue of passing binary data from ActiveX to Javascript and decided to present a solution based on taxilian's proposal.
Before that, I'd like to point out that it's also possible to build SAFEARRAY of binary data and send this object back to JS. The only problem is that VBScript has to be used to unpack this object, convert it to data type recognised only by JScript (Microsoft's Javascript dialect) that can than be used to build traditional JS array.
Without going into reason behind this solution (for that check taxilian's answer), here is a method that will build Javascript array within ActiveX control and return this array to JS.
/** NOTE: you have to include MsHTML.h header in order to access IServiceProvider,
IHTMLWindow2 and related constants. **/
IDispatch* CActiveX_TutorialCtrl::GetJSArrayObject(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
LPOLECLIENTSITE site = this->GetClientSite();
IServiceProvider* serviceProvider = nullptr;
site->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&serviceProvider));
IHTMLWindow2* window_obj = nullptr;
serviceProvider->QueryService(SID_SHTMLWindow, IID_IHTMLWindow2, reinterpret_cast<void**>(&window_obj));
DISPPARAMS disparam = { nullptr, nullptr, 0, 0 };
VARIANT ret_val;
DISPID dispid;
LPOLESTR method_name = L"Array";
HRESULT hr = window_obj->GetIDsOfNames(IID_NULL, &method_name, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
hr = window_obj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &disparam, &ret_val, nullptr, nullptr);
if (ret_val.vt != VT_DISPATCH)
return nullptr;
VARIANTARG push_arg;
method_name = L"push";
hr = ret_val.pdispVal->GetIDsOfNames(IID_NULL, &method_name, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (hr != S_OK)
return nullptr;
::VariantInit(&push_arg);
::VariantChangeType(&push_arg, &push_arg, 0, VT_I4);
for (int i = -10; i <= 10; ++i)
{
push_arg.intVal = i;
disparam.rgvarg = &push_arg;
disparam.rgdispidNamedArgs = nullptr;
disparam.cArgs = 1;
disparam.cNamedArgs = 0;
hr = ret_val.pdispVal->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &disparam, nullptr, nullptr, nullptr);
if (hr != S_OK)
return nullptr;
}
::VariantClear(&push_arg);
serviceProvider->Release();
window_obj->Release();
serviceProvider = nullptr;
window_obj = nullptr;
return ret_val.pdispVal;
}
Most of the code you see here is typical COM programming. Firstly, we retrieve a pointer to the client site where our control is hosted. Then, we QI (query interface) for the IServiceProvider which is an IE interface that implements many supported services. One of them is IHTMLWindow2 which is the type of window object in Javascript. Now that we have a pointer to our window object we can create an Array object. Array is just a method of IHTMLWindow2 object and in order to create a new array we have to invoke this function.
In order to invoke a method on COM object (and IHTMLWindow2 is just an interface implemented by some COM object), this object has to implement IDispatch interface that allows the user to call this object's method by using Invoke method. GetIDsOfNames method is used to retrieve a DISPID (dispatch id) of the Array method and then we finally create a new array by invoking Array method on our window_obj object. In the ret_val parameter (of type VARIANT), we will get a IDispatch* pointer representing our JS array.
It is obvious what to do next: use this pointer to get the DISPID of the push method and then fill the array by "Invoking" this method over and over. The sample function also shows how to build the DISPPARAMS and VARIANTARG object needed for the IDispatch::Invoke method.
Finally, we return the IDispatch* pointer from the method. JS will recognise this object as a native JS array because this is in fact its internal implementation.