C++ ActiveX开发的问题讨论
最近在一个项目中需要开发一个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开发的问题讨论的更多相关文章
- C# Activex开发、打包、签名、发布 C# Activex开发、打包、签名、发布 [转]
C# Activex开发.打包.签名.发布 2013-06-22 12:01:20 浏览:3823 一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠HTML ...
- .Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇
一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserContr ...
- .Net魔法堂:史上最全的ActiveX开发教程——自动更新、卸载篇
一.前言 B/S模式的特点之一,客户端版本升级相对简单.快捷,适合产品的快速迭代.而ActiveX组件的自动更新同样也继承了这一优点.下面我们一起来了解吧! 二.二话不说更新ActiveX 1. 设置 ...
- .Net魔法堂:史上最全的ActiveX开发教程——部署篇
一.前言 接<.Net魔法堂:史上最全的ActiveX开发教程——发布篇>,后我们继续来部署吧! 二. 挽起衣袖来部署 ActiveX的部署其实就是客户端安装ActiveX组件,对未签 ...
- .Net魔法堂:史上最全的ActiveX开发教程——发布篇
一. 前言 接着上一篇<.Net魔法堂:史上最全的ActiveX开发教程——开发篇>,本篇讲述如何发布我们的ActiveX. 二.废话少讲,马上看步骤! 1. 打包 C#开发的Activ ...
- .Net魔法堂:史上最全的ActiveX开发教程——开发篇
一.前言 在设计某移动内部自动化运维平台时,经综合考虑终端机性能和功能需求等因素后,决定采用B/S模式,并且浏览器通过ActiveX组件实现与服务器Agent作P2P的通讯.好处,整个平台以网页形式存 ...
- 基于MFC的ActiveX控件开发教程------------浏览器插件之ActiveX开发
浏览器插件之ActiveX开发(一) 一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...
- ActiveX开发
转自(http://blog.csdn.net/mingojiang/article/details/8159263) 一.ActiveX基础 1.1什么是ActiveX ActiveX是COM规范的 ...
- 【VS开发】ActiveX开发注意事项
[VS开发]ActiveX开发注意事项 标签:[VS开发] 注意:必须在工程的app文件的InitInstance()中加入如下代码,否则动态创建控件不会成功: AfxEnableControlCon ...
随机推荐
- python 批量打印PDF
有一批PDF文件,好几百个,每个只打印第2,3页,双面打印. 网上搜索一波,方案如下: 安装Ghostscript,GhostView,使用gsprint命令打印pdf文件. gsprint命令参数说 ...
- GitHub与Markdown(学习笔记)
一.学前提问: 1.GitHub用翻墙吗? 访问 GitHub 不用翻墙,只是可能访问速度稍慢些. 2.英语差学得会吗? GitHub 虽然都是英文,但是,对英语水平的要求不是那么高,都是些简单的单词 ...
- ThinkCMF框架任意内容包含
更多内容,欢迎关注微信公众号:信Yang安全,期待与您相遇. ThinkCMF是一款基于PHP+MYSQL开发的中文内容管理框架,底层采用ThinkPHP3.2.3构建.ThinkCMF提出灵活的应用 ...
- Windbg妙用
计算器 当你在调试,需要做一些从十六进制到十进制的简单转换,一些整数计算你不需要切换到calc.exe,你可以只使用windbg的表达式计算器.假设你得到了一个十六进制的大小,比如说2e903000, ...
- Windbg命令脚本流程控制语句详解
在Windbg命令脚本一文里,我们介绍了命令脚本语言的的组成要素,在本文里将对语句进行展开的讲解.这些语句主要是流程控制的语句,比如我们常见的条件分子和循环语句等. ; (命令分隔符) 分号(:)字符 ...
- Phalcon框架的编译安装 内存不足的解决办法
对症解决 有两种解决方法,一种是提升ECS系统内存.但是却要真金白银跟阿里云去购买的.另一种,则是手动创建swap交换文件.下面来介绍第二种方法. 第一步:首先确定系统是否已经开启swap交换分区: ...
- 守护进程daemon.c
它的特点是:•不占用控制终端(后台运行)•独立于控制终端•周期性运行 #include<stdio.h>#include<unistd.h>#include<fcntl. ...
- go的接口内部实现
1 前言 1.1 Go汇编 Go语言被定义为一门系统编程语言,与C语言一样通过编译器生成可直接运行的二进制文件.这一点与Java,PHP,Python等编程语言存在很大的不同,这些语言都是运行在基于C ...
- koa post提交数据,koa-bodyparser中间件来获取post提交数据
原生 Nodejs 获取 post 提交数据 首先创建并初始化一个node应用,根路由使用index.ejs模板 var Koa=require('koa') var router = require ...
- PHP系列 | ThinkPHP5数据库迁移工具 migration
了解更多,请关注微信公众号 ThinkPHP5数据库迁移工具 migration 什么是Migration? migration用谷歌翻译是移民的意思,在PHP中我们将它理解为迁移,将Migratio ...