将字节数组从ActiveX传递到javascript,反之亦然

时间:2009-12-18 06:47:06

标签: javascript c++ activex binary

我需要将ActiveX对象(使用带有ATL的Visual C ++)的数据(字节数组,即char *)传递给我的javascript代码(反之亦然)。我已经在网上挖掘了这样的问题并尝试了很多解决方案,但没有成功。我尝试了以下几点:

  • 将char *转换为BSTR并将其传递给javascript(JS),但我在JS中的结果是“”,因为我的数据的性质不是字符串。
//in C++:
STDMETHODIMP CActiveXObj::f(BSTR* msg) // msg is the return value in ATL automation function
{
    char *buffer; // byte data is stored in buffer
    *msg = SysAllocStringByteLen((LPCSTR)buffer, bufferLen+1);
}
//////////////////////////////////////////////////////////////////////////
//in JavaScript:
var myobj= new ActiveXObject("IGCE.ActiveXObj");
var result = myobj.f(); // result = ""
  • 从C ++传递安全的字节数据数组

有人能以最简单的形式向我提供工作代码吗?

非常感谢!

克里斯汀

4 个答案:

答案 0 :(得分:3)

// In *.idl file 
[propget, id(0)] HRESULT ArrayProperty([out, retval] SAFEARRAY(VARIANT) *pArray);
[propput, id(0)] HRESULT ArrayProperty([in] SAFEARRAY(VARIANT) Array);


// Somewhere in javascript

function ax2js(axArray) {    
    return new VBArray(array).toArray();
}

function js2ax(jsArray) {
    var dict = new ActiveXObject("Scripting.Dictionary");

    for (var i = 0; i < jsArray.length; i++) {
        dict.add(i, jsArray[i]);
    }

    return dict.Items();
}

function fooHandler() {
    var ax = new ActiveXObject("My.My");

    var ar = ax2js(ax.ArrayProperty);

    ax.ArrayProperty = js2ax(ar);
}

答案 1 :(得分:0)

您可能需要使用SAFEARRAY传递数据。这个名为CComSafeArray的ATL包装器。希望这对你来说已经足够了,如果没有的话 我会挖出一些代码。

答案 2 :(得分:0)

据我所知(根据我的经验),在与javascript交谈时,您只能使用以下基本数据类型:

  • 字符串
  • 内部
  • 布尔
  • 的IDispatch *

其他任何东西似乎都不起作用。我从来没有尝试过使用SAFEARRAY,但我可以提出一个可能的选择。

如果您获得对DOM窗口的引用(我不会在此处介绍;如果您不知道如何,搜索和/或提交新问题,我可以在那里回答),您可以使用IDispatch在窗口上调用方法“Array”,它将返回一个空的javascript数组的IDispatch *。然后,您可以在Array IDispatch *上为要作为int发送到javascript的每个字节调用“push”,然后返回Array的IDispatch *作为相关方法或属性的返回值。您将获取javascript中的数据作为整数数组,但每个元素都是一个字节,您可以这样使用它。

如果您可以建议在javascript中使用二进制数据的另一种方法(暂时忘记activex控件),我可能会告诉您如何从控件中返回数据。

这实际上是FireBreath(IE和Firefox的开源插件框架; http://www.firebreath.org)在从JSAPI(javascript脚本对象)方法返回向量以将数据作为数组返回到javascript时使用的方法。您可以在兼容NPAPI的浏览器上使用类似(几乎相同)的方法。

答案 3 :(得分:0)

我知道这是一篇非常古老的帖子,但我自己偶然发现了将二进制数据从ActiveX传递到Javascript的同样问题,并决定提出一个基于taxilian提案的解决方案。

在此之前,我想指出也可以构建SAFEARRAY的二进制数据并将此对象发送回JS。唯一的问题是必须使用VBScript解压缩此对象,将其转换为仅由JScript(Microsoft的Javascript方言)识别的数据类型,而不是用于构建传统JS数组。

在没有理解这个解决方案的背后(对于那个检查taxilian的答案),这里有一个方法将在ActiveX控件中构建Javascript数组并将此数组返回给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编程。首先,我们检索指向托管控件的客户端站点的指针。然后,我们为IServiceProvider提供QI(查询接口),这是一个实现许多支持服务的IE接口。其中一个是 IHTMLWindow2 ,它是Javascript中 window 对象的类型。现在我们有一个指向窗口对象的指针,我们可以创建一个Array对象。 Array只是IHTMLWindow2对象的一个​​方法,为了创建一个新的数组,我们必须调用这个函数。

为了在COM对象上调用方法(而IHTMLWindow2只是某个COM对象实现的接口),该对象必须实现IDispatch接口,允许用户使用调用方法。 GetIDsOfNames 方法用于检索Array方法的DISPID(dispatch id),然后我们最终通过在 window_obj 对象上调用Array方法创建一个新数组。在 ret_val 参数(VARIANT类型)中,我们将获得一个表示我们的JS数组的IDispatch *指针。

显然下一步该做什么:使用此指针获取 push 方法的DISPID,然后通过&#34; Invoking&#34;填充数组。这种方法一遍又一遍。示例函数还显示了如何构建IDispatch :: Invoke方法所需的DISPPARAMS和VARIANTARG对象。

最后,我们从方法返回IDispatch *指针。 JS会将此对象识别为本机JS数组,因为这实际上是它的内部实现。