最近在一个项目中需要开发一个ocx插件,在开发过程中发现了一些问题,所以在此记录一下。

我想讨论的主要是函数的参数问题,我分别使用c++,JavaScript,C#对ocx插件做了测试,发现不同的参数类型在这几种语言中表现的差异很大。

(1)首先ocx我是使用C++开发的,所以在同样使用c++语言开发的程序中调用,所有的参数类型都没有问题。

(2)所有指针类型如:long *,float *,BSTR*作为参数时,在JavaScript中调用都会失败,可能在JavaScript中就不能支持这种指针传参方式。

在C#中是能够正常调用的。指针类型在C#中会被转换为ref type类型,指明这个参数的值是能够被函数内部所修改的。像以下的调用方式是成功的。

ActiveX中的函数原型:

LONG CSimpleActiveXCtrl::GetData(LONG v1, LONG* v2)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    *v2 = 10;
    return v1;
}

C#自动生成的函数定义:

public virtual int GetData(int v1, ref int v2);

C#中调用:

   int a=12, b=0;
        int c=axSimpleActiveX1.GetData(a,ref b);

虽然在C#中能够正常调用,但是在JavaScript没办法调用,说明这种类型不是通用的,如果你想你开发的控件是通用的,应该禁止使用指针类型。

(3)数组参数

如果你需要传递一个数组,参数类型可以定义为Variant,但是对于数组的处理,在C#中和JavaScript中又是不一样的。JavaScript传入的数组,其vt类型是VT_DISPATCH,

而C#传入的数组不是VT_DISPATCH类型,所以取值或赋值的方式又不同了。如果你只需要取得数组的值,那么只需要定义为Variant类型就可以了,如果你需要修改数组

的值,用于C#需要定义为Variant*类型,而对于JavaScript只需要定义为Variant类型就可以。所以说在不同语言里面的表现很不一样。很显然Variant类型也没办法做到通用。

ActiveX中的定义:

1)取数组长度:

long CSimpleActiveXCtrl::GetArrayLength(VARIANT &v1)
{
    if (v1.vt == VT_DISPATCH)   //JS数组
    {
        IDispatch* pDisp = v1.pdispVal;
        int ret;
        BSTR varName = L"length";
        VARIANT varValue;
        DISPPARAMS noArgs = { NULL, NULL, 0, 0 };
        DISPID dispId;
        HRESULT hr = 0;

hr = pDisp->GetIDsOfNames(IID_NULL, &varName, 1, LOCALE_USER_DEFAULT, &dispId);
        if (FAILED(hr))
            return hr;
        hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &noArgs, &varValue, NULL, NULL);
        if (SUCCEEDED(hr))
        {
            ret = varValue.intVal;
            return ret;
        }
        else
        {
            return 0;
        }
    }
    else   //C#数组
    {
        long Low(0), High(0);
        SAFEARRAY* pArr1 = v1.parray;
        SafeArrayGetLBound(pArr1, 1, &Low);
        SafeArrayGetUBound(pArr1, 1, &High);
        long len = High - Low + 1;
        return len;
    }
}

2)数组取值

bool CSimpleActiveXCtrl::GetArrayDataI(VARIANT &v1, int index, int &data)

{

 if (v1.vt == VT_DISPATCH)    //JS数组
    {
        IDispatch* pDisp = v1.pdispVal;
        ATL::CComVariant varName(index, VT_I4); // 数组下标
        DISPPARAMS noArgs = { NULL, NULL, 0, 0 };
        DISPID dispId;
        VARIANT varValue;
        HRESULT hr = 0;
        varName.ChangeType(VT_BSTR); // 将数组下标转为数字型,以进行GetIDsOfNames
        //
        // 获取通过下标访问数组的过程,将过程名保存在dispId中
        //
        hr = pDisp->GetIDsOfNames(IID_NULL, &varName.bstrVal, 1, LOCALE_USER_DEFAULT, &dispId);
        if (FAILED(hr))
            return false;
        //
        // 调用COM过程,访问指定下标数组元素,根据dispId 将元素值保存在varValue中
        //
        hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &noArgs, &varValue, NULL, NULL);
        if (SUCCEEDED(hr))
        {
            data = varValue.intVal;    // 将数组元素按int类型取出
            return true;
        }
        else
        {
            return false;
        }
    }
    else   //C#数组
    {
        SAFEARRAY* pArr = v1.parray;
        int *pValue;
        SafeArrayAccessData(pArr, (void**)&pValue);
        data = pValue[index];
        SafeArrayUnaccessData(pArr);
        return true;
    }

}

3)数组赋值

bool CSimpleActiveXCtrl::SetArrayDataI(VARIANT &v1, int index, int data)

{

 if (v1.vt == VT_DISPATCH)   //JS数组
    {
        IDispatch* pDisp = v1.pdispVal;
        ATL::CComVariant varName(index, VT_I4);
        DISPID dispId;
        ATL::CComVariant varValue;
        HRESULT hr = 0;
        varName.ChangeType(VT_BSTR); // 将数组下标转为数字型,以进行GetIDsOfNames
        hr = pDisp->GetIDsOfNames(IID_NULL, &varName.bstrVal, 1, LOCALE_USER_DEFAULT, &dispId);
        if (FAILED(hr))
            return false;

DISPID dispidPut = DISPID_PROPERTYPUT; // put操作
        DISPPARAMS dispparams;
        dispparams.rgvarg = new VARIANTARG[1]; // 初始化rgvarg
        dispparams.rgvarg[0].vt = VT_I4; // 数据类型
        dispparams.rgvarg[0].intVal = data; // 更新值
        dispparams.cArgs = 1; // 参数数量
        dispparams.cNamedArgs = 1; // 参数名称
        dispparams.rgdispidNamedArgs = &dispidPut; // 操作DispId,表明本参数适用于put操作

hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
        return true;
    }
    else    //C#数组
    {
        long Low(0), High(0);
        SAFEARRAY* pArr = v1.parray;
        int *pValue;
        SafeArrayAccessData(pArr, (void**)&pValue);
        pValue[index]=data;
        SafeArrayUnaccessData(pArr);
        return true;
    }

}

4)JS数组拷贝

LONG CSimpleActiveXCtrl::CopyArrayData(VARIANT &v1, VARIANT &v2)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

long len = GetArrayLength(v1);
    for (int i = 0; i < len; i++)
    {
        int value;
        GetArrayDataI(v1, i, value);
        SetArrayDataI(v2, i, value);
    }
    return 0;
}

5)C#数组拷贝

LONG CSimpleActiveXCtrl::CopyArray_CS(VARIANT &v1, VARIANT* v2)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

// TODO:  在此添加调度处理程序代码
    long len = GetArrayLength(v1);
    for (int i = 0; i < len; i++)
    {
        int value;
        GetArrayDataI(v1, i, value);
        SetArrayDataI(*v2, i, value);
    }
    return 0;
}

JS中的调用例子:

function CopyArrayClick()
{
      var arr1 = new Array();
      var arr2 = new Array();
      for (var i = 0; i < 10; i++)
      {
           arr1[i] = i;
           arr2[i] = 0;
      }
      var c = SimpleActiveX.CopyArrayData(arr1, arr2);
      var tmp = "";
      for (var j = 0; j < 10; j++)
           tmp += arr2[j];
      document.getElementById("ResultDisplay").value = "arr2:" + tmp;

}

C#中的调用例子:

private void button1_Click(object sender, EventArgs e)
{
            int[] arr1 = new int[10];
            int[] arr2 = new int[10];
            for (int i = 0; i < 10; i++)
                arr1[i] = i;
            object var = new System.Runtime.InteropServices.VariantWrapper(arr2);
            axSimpleActiveX1.CopyArray_CS(arr1, ref var);
            int[] arr3 = var as int[];
            string te="";
            for (int i = 0; i < 10; i++)
                te += string.Format("{0} ", arr3[i]);
                textBox1.AppendText(te);
}

从上面的测试结果来看,对于ocx控件开发中,参数的选择我有以下的建议:

1.避免使用指针类型;

2.如果需要输出参数应该以函数返回值的方式返回,如果有多个参数要返回可以分成多个函数来实现或者通过属性来返回;

3.个人认为可以使用BSTR来替代数组,不管作为参数或者返回值都更容易实现,在不同语言中对字符串的操作都是类似的。

C++ ActiveX开发的问题讨论的更多相关文章

  1. C# Activex开发、打包、签名、发布 C# Activex开发、打包、签名、发布 [转]

    C# Activex开发.打包.签名.发布 2013-06-22 12:01:20 浏览:3823 一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠HTML ...

  2. .Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇

    一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserContr ...

  3. .Net魔法堂:史上最全的ActiveX开发教程——自动更新、卸载篇

    一.前言 B/S模式的特点之一,客户端版本升级相对简单.快捷,适合产品的快速迭代.而ActiveX组件的自动更新同样也继承了这一优点.下面我们一起来了解吧! 二.二话不说更新ActiveX 1. 设置 ...

  4. .Net魔法堂:史上最全的ActiveX开发教程——部署篇

    一.前言 接<.Net魔法堂:史上最全的ActiveX开发教程——发布篇>,后我们继续来部署吧! 二. 挽起衣袖来部署   ActiveX的部署其实就是客户端安装ActiveX组件,对未签 ...

  5. .Net魔法堂:史上最全的ActiveX开发教程——发布篇

    一. 前言 接着上一篇<.Net魔法堂:史上最全的ActiveX开发教程——开发篇>,本篇讲述如何发布我们的ActiveX. 二.废话少讲,马上看步骤! 1. 打包  C#开发的Activ ...

  6. .Net魔法堂:史上最全的ActiveX开发教程——开发篇

    一.前言 在设计某移动内部自动化运维平台时,经综合考虑终端机性能和功能需求等因素后,决定采用B/S模式,并且浏览器通过ActiveX组件实现与服务器Agent作P2P的通讯.好处,整个平台以网页形式存 ...

  7. 基于MFC的ActiveX控件开发教程------------浏览器插件之ActiveX开发

    浏览器插件之ActiveX开发(一) 一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...

  8. ActiveX开发

    转自(http://blog.csdn.net/mingojiang/article/details/8159263) 一.ActiveX基础 1.1什么是ActiveX ActiveX是COM规范的 ...

  9. 【VS开发】ActiveX开发注意事项

    [VS开发]ActiveX开发注意事项 标签:[VS开发] 注意:必须在工程的app文件的InitInstance()中加入如下代码,否则动态创建控件不会成功: AfxEnableControlCon ...

随机推荐

  1. ThreadLocal(在一个线程中共享数据)

    ThreadLocal 在"事务传递Connection"参数案例中,我们必须传递Connection对象,才可以完成整个事务操作.如果不传递参数,是否可以完成?在JDK中给我们提 ...

  2. 项目Alpha冲刺--9/10

    项目Alpha冲刺--9/10 作业要求 这个作业属于哪个课程 软件工程1916-W(福州大学) 这个作业要求在哪里 项目Alpha冲刺 团队名称 基于云的胜利冲锋队 项目名称 云评:高校学生成绩综合 ...

  3. element ui 中的 resetFields() 报错'resetFields' of undefined

    每次做各种form表单时,首先要注意的是初始化,但是刚开始若没有仔细看文档,则会自己写个方法将数据设置为空,但是这样就会出现一个问题,表单内存在各种验证,假如是一个弹框内有form表单,弹框出现就执行 ...

  4. MP4文件批量转码成MP3

    需求背景:最近为了学python爬虫,在论坛里找了不少视频教程,非常棒.但有时看视频不方便,就想着能否把视频批量转码成音频,这样在乘坐地铁公交的时候也能学习了. 解决路径:有了需求,我首先在论坛里搜了 ...

  5. Hive优化(整理版)

    1. 概述 1.1 hive的特征: 可以通过SQL轻松访问数据的工具,从而实现数据仓库任务,如提取/转换/加载(ETL),报告和数据分析: 它可以使已经存储的数据结构化: 可以直接访问存储在Apac ...

  6. 9、Python复杂数据类型(list、tuple、set、dict)

    一.列表(list):[a1,a2],可变数据类型 1.序列: 序列是基类类型,序列扩展类型包括:字符串.元组和列表 序列都可以进行的操作包括索引,切片,加,乘,检查成员. 此外,Python已经内置 ...

  7. [Codeforces Educational Round 71]Div. 2

    总结 手速场...像我这种没手速的就直接炸了... 辣鸡 E 题交互,少打了个 ? 调了半个小时... 到最后没时间 G 题题都没看就结束了...结果早上起来被告知是阿狸的打字机...看了看题一毛一样 ...

  8. 通过USB 2.0电缆手动设置内核模式调试

    Windows的调试工具支持通过USB 2.0电缆进行内核调试.本文介绍如何手动设置USB 2.0调试.通过USB 2.0电缆进行调试需要以下硬件: USB 2.0调试电缆.此电缆不是标准USB 2. ...

  9. C++对象内存布局,this指针,对象作为参数,作为返回值

    class TestClass { public: void setNum(int num) { m_num1 = num; } int getNum() { return m_num1; } pri ...

  10. P1071 潜伏者

    //Pro:NOIP2009 T1 P1071 潜伏者 #include<iostream> #include<cstdio> #include<cstring> ...