Я понимаю, что это очень старый пост, но я сам наткнулся на ту же проблему передачи двоичных данных из ActiveX в Javascript и решил представить решение, основанное на предложении таксиста.
Перед этим я хотел бы отметить, что также возможно создать SAFEARRAY из двоичных данных и отправить этот объект обратно в JS. Единственная проблема заключается в том, что для распаковки этого объекта необходимо использовать VBScript, преобразовать его в тип данных, распознаваемый только JScript (диалект Javascript от Microsoft), который затем можно использовать для создания традиционного массива JS.
Не вдаваясь в причину этого решения (для проверки ответа таксиста), вот метод, который создаст массив Javascript в элементе управления ActiveX и вернет этот массив 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;
}
Большая часть кода, который вы видите здесь, является типичным программированием COM. Во-первых, мы получаем указатель на клиентский сайт, где находится наш элемент управления. Затем мы используем QI (интерфейс запросов) для IServiceProvider, который является интерфейсом IE, который реализует множество поддерживаемых сервисов. Одним из них является IHTMLWindow2 , который является типом window объекта в Javascript. Теперь, когда у нас есть указатель на наш оконный объект, мы можем создать объект Array. Массив - это просто метод объекта IHTMLWindow2, и для создания нового массива мы должны вызвать эту функцию.
Чтобы вызвать метод для COM-объекта (а IHTMLWindow2 - это просто интерфейс, реализованный некоторым COM-объектом), этот объект должен реализовать интерфейс IDispatch, который позволяет пользователю вызывать метод этого объекта с помощью Invoke * 1015. * метод. GetIDsOfNames метод используется для извлечения DISPID (идентификатор отправки) метода Array, а затем мы наконец создаем новый массив, вызывая метод Array для нашего window_obj объекта. В параметре ret_val (типа VARIANT) мы получим указатель IDispatch *, представляющий наш массив JS.
Очевидно, что делать дальше: используйте этот указатель, чтобы получить DISPID метода push , а затем заполните массив, вызывая этот метод снова и снова. В примере функции также показано, как построить объекты DISPPARAMS и VARIANTARG, необходимые для метода IDispatch :: Invoke.
Наконец, мы возвращаем указатель IDispatch * из метода. JS распознает этот объект как собственный массив JS, потому что на самом деле это его внутренняя реализация.