I understand that this is a very old post, but I myself came across the same problem of transferring binary data from ActiveX to Javascript and decided to present a solution based on a taxonomic proposal.
Before that, I would like to point out that it is also possible to build SAFEARRAY from binary data and send this object back to JS. The only problem is that VBScript needs to be used to unpack this object, convert it to a data type recognized only by JScript (Microsoft Javascript dialect), which can be used to create a traditional JS array.
Without getting into the business of this solution (for checking the taxil response), here is a method that will build a Javascript array in an ActiveX control and return that array to JS.
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. First, we get a pointer to the clientโs site where our control is located. Then we have a QI (request interface) for IServiceProvider, which is an IE interface that implements many supported services. One of them is IHTMLWindow2 , which is the window type of an object in Javascript. Now that we have a pointer to our window object, we can create an Array object. An array is just an IHTMLWindow2 object method, and to create a new array we must call this function.
To call a method for a COM object (and IHTMLWindow2 is just an interface implemented by some COM object), this object must implement an IDispatch interface that allows the user to call this object method using the Invoke method. The GetIDsOfNames method is used to retrieve the DISPID (send identifier) โโof the Array method, and then we finally create a new array by calling the Array method in our window_obj object. In the ret_val parameter (of type VARIANT) we get the IDispatch * pointer representing our JS array.
Obviously what to do next: use this pointer to get the DISPID of the push method, and then fill the array with the Call method of this method again and again. The sample function also shows how to create the DISPPARAMS and VARIANTARG objects needed for the IDispatch :: Invoke method.
Finally, we return the IDispatch * pointer from the method. JS recognizes this object as its own JS array, because this is actually its internal implementation.