JavaScript Interactive with ActiveX Control

1.在COM组件中调用JavaScript函数
// 连接点方式页面javascript脚本
<object class VIEWASTEXT></object>
<script language="JavaScript" for="testCom" event="staTe(s)">
alert("State(" + s + ")");
return 123;
</script>
<script language="JavaScript">
testCom.FireStateEvent("Hello");
</script>
 
// 事件属性方式页面javascript脚本
function onState(s){
alert("onState(" + s + ")");
return 456;
}
var o = new ActiveXObject("TestATL.TestCom");
o.onstaTe=onState;
o.FireStateEvent("Hello");
 
// Com组件VC7.1 ATL代码
__interface _ITestComEvents{
[id(1), helpstring("State事件")] HRESULT State([in] BSTR str);
};
__event __interface _ITestComEvents;
IDispatchPtr m_onState;        // 事件属性
STDMETHOD(get_onState)(IDispatch** pVal) {
*pVal = m_onState;
return S_OK;
};
STDMETHOD(put_onState)(IDispatch* newVal) {
m_onState = newVal;
return S_OK;
};
STDMETHOD(FireStateEvent)(BSTR str) {
__raise State(str);        // 激发连接点事件
CComVariant result;
CComVariant avarParams[1] = {str};
DISPPARAMS dispParams = {avarParams, NULL, 1, 0};
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
UINT nArgErr = (UINT)-1;      // initialize to invalid arg
if (m_onState)        // 激发属性事件
HRESULT hr = m_onState->Invoke(0, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &dispParams, &result, &excepInfo, &nArgErr);
return S_OK;
}
参见:
How To Call a Script Function from a VC WebBrowser Application
如何在COM object中使用 Javascript function object?
在COM组件中调用JavaScript函数
 
2.从页面javascript向Com组件传递结构数组
// 页面脚本
var o = new ActiveXObject("TestATL.TestCom");
o.onstaTe=onState;
o.Put("array", {0: 123, 1: "abc"});
o.Put("array", [456, "def"]);
o.Put("array", [{name: "tom", age: 8}, {name: "jack", age: 10}]);
var a = new Array(789, "ghi"); // has "length" property
o.Put("array", a);
 
// Com组件VC7.1 ATL代码
STDMETHODIMP CTestCom::Put(BSTR key, VARIANT value)
{
WCHAR output[4096] = L"";
if(0 == wcsicmp(key, L"array") && VT_DISPATCH == value.vt)
{
IDispatchPtr spDisp = value.pdispVal;
DISPID dispID = 0;
DISPPARAMS dispParams = {NULL, NULL, 0, 0};
CComVariant result;
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
UINT nArgErr = (UINT)-1; // initialize to invalid arg
unsigned int length = 0; // 数组长度 或 属性 个数
 
     LPOLESTR func = L"length";
HRESULT hr = spDisp->GetIDsOfNames(GUID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispID);
if(S_OK == hr){       // 如果有"length"属性
hr = spDisp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &result, &excepInfo, &nArgErr);
if(S_OK == hr && VT_I4 == result.vt)
length = result.intVal;       // 直接读取数组长度
}else{
unsigned int nTypeInfo = 0;
hr = spDisp->GetTypeInfoCount(&nTypeInfo);
ATLASSERT(1 == nTypeInfo);
ITypeInfoPtr spTypeInfo;
hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
TYPEATTR *pTypeAttr = NULL;
hr = spTypeInfo->GetTypeAttr(&pTypeAttr);
//ATLASSERT("{C59C6B12-F6C1-11CF-8835-00A0C911E8B2}" == pTypeAttr->guid);     // JScript: 
length = pTypeAttr->cVars;       // 从类型信息读取数组长度
spTypeInfo->ReleaseTypeAttr(pTypeAttr);
}
for(unsigned int i=0; i<length; i++)
{
WCHAR buf[32];
_itow(i, buf, 10);
func = buf;
hr = spDisp->GetIDsOfNames(GUID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispID);
hr = spDisp->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &result, &excepInfo, &nArgErr);
if(S_OK != hr)
continue;
if(VT_DISPATCH == result.vt){
IDispatchPtr spItem = result.pdispVal;
func = L"name";
hr = spItem->GetIDsOfNames(GUID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispID);
hr = spItem->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &result, &excepInfo, &nArgErr);
if(S_OK == hr && VT_BSTR == result.vt)
swprintf(output + wcslen(output), L"name=%s", result.bstrVal);
func = L"age";
hr = spItem->GetIDsOfNames(GUID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispID);
hr = spItem->Invoke(dispID, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParams, &result, &excepInfo, &nArgErr);
if(S_OK == hr && VT_I4 == result.vt)
swprintf(output + wcslen(output), L" age=%d\n", result.intVal);
}else if(VT_BSTR == result.vt)
swprintf(output + wcslen(output), L"BSTR:%s\n", result.bstrVal);
else if(VT_I4 == result.vt)
swprintf(output + wcslen(output), L"I4:%d\n", result.intVal);
else
swprintf(output + wcslen(output), L"item.vt=%d\n", result.vt);
}
}
FireStateEvent(output);
return S_OK;
}
 
3.枚举IE窗口的内容,并调用其中的脚本
#import <mshtml.tlb>       // Internet Explorer 5
#import <shdocvw.dll> 
SHDocVw::IShellWindowsPtr spSHWinds; 
spSHWinds.CreateInstance(__uuidof(SHDocVw::ShellWindows));
long nCount = spSHWinds->GetCount();
IDispatchPtr spDisp;
for (long i = 0; i < nCount; i++)
{
_variant_t va(i, VT_I4);
spDisp = spSHWinds->Item(va);
SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
if (spBrowser != NULL)
{
_bstr_t location = spBrowser->GetLocationName();
if(_bstr_t(L"Test DapCtrl") == location)       // 找指定IE窗口
{
IHTMLDocument2Ptr spDoc(spBrowser->GetDocument());
if (spDoc != NULL)
{
_bstr_t exp = m_onState;
IDispatch *pdis = NULL;
hr = spDoc->get_Script(&pdis);
if(pdis){
DISPID tmpDispID = 0;
LPOLESTR func = L"Test"; // javascript 函数名
hr = pdis->GetIDsOfNames(GUID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &tmpDispID);
if(S_OK == hr)
hr = pdis->Invoke(tmpDispID, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &dispParams, &result, &excepInfo, &nArgErr);
}
}
}
}
}
参见:
HOWTO: Connect to a Running Instance of Internet Explorer
ActiveX组件与JavaScript交互
ActiveX组件控制其所在的IE窗口
 
4.在VC中执行脚本
#import <msscript.ocx>       // msscript.ocx 
using namespace MSScriptControl;
IScriptControlPtr pScriptControl(__uuidof(ScriptControl));
LPSAFEARRAY psa;
SAFEARRAYBOUND rgsabound[]       = { 1, 0 }; // 1 elements, 0-based
int i;
psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
if (!psa)
{
return E_OUTOFMEMORY;
}
VARIANT vFlavors[1];
for (i = 0; i < 1; i++)
{
VariantInit(&vFlavors[i]);
V_VT(&vFlavors[i]) = VT_BSTR;
}
V_BSTR(&vFlavors[0]) = SysAllocString(bstr);
long lZero = 0;
hr = SafeArrayPutElement(psa, &lZero,&vFlavors[0]);
for(i=0;i<1;i++)
{
SysFreeString(vFlavors[i].bstrVal);
}
pScriptControl->Language = "JScript";
pScriptControl->AllowUI = TRUE;
_bstr_t exp = L"1+2+3";
_variant_t outpar = pScriptControl->Eval(exp);
//_variant_t outpar = pScriptControl->ExecuteStatement(exp);
//_variant_t outpar = pScriptControl->Run("MyStringFunction", &psa);
_bstr_t bstrReturn = (_bstr_t)outpar;
char *pResult = (char *)bstrReturn;
SafeArrayDestroy(psa);
参见:
How To Call Run() Method of the Microsoft Script Control in C++

本文研究如何在JS等脚本语言与ActiveX控件之间通信,如何传递各种类型的参数,以及COM的IDispatch接口。使用类似的方法,可以推广到其他所有脚本型语言,如LUA,AutoCad等。

本文将研究以下几个方面:

1. 整形数组传参

2. 字符串参数,字符串返回值

3. 修改传入字符串内容

4. 数组参数

5. IDispatch接口介绍

6. 修改输入数组内容

7. 增加数组内容

8. 以数组传参方式,JS调用S4Execute( )

(一)整形参数

1. 整形参数,可直接传递。整形返回值需要以 [retVal] Long *方式声明

2. COM中c++接口定义

STDMETHODIMP CJsAtl::IntSum(LONG a, LONG b, LONG* retVal)

{

*retVal = a + b;

return S_OK;

}

3. Js中调用

<object > </object>

function test_int()

{

var a = 1;

var b = 2;

try {

var obj = document.getElementByIdx_xx_x("obj");

var retVal = obj.IntSum(a, b);

Alert("RetVal: " + retVal);

} catch (e) {

Alert( "Js error: " + e.message);

}

}

(二)字符串参数,字符串返回值

1. COM中,字符串使用BSTR表示,BSTR实际是UNICODE 字符数组(WCHAR[])

2. COM字符串传参规范中规定:

a) 生成字符串变量时,需要SysAllocString/SysAllocStringByteLen分配空间。

b) 函数结束前,分配的空间需要释放,SysFreeString。

c) 若函数中分配的空间作为返回值,则不释放。而由外部调用者负责释放。

3. COM中c++函数定义

STDMETHODIMP CJsAtl::StringAdd(BSTR str1, BSTR str2, BSTR* retVal)

{

int len = SysStringLen(str1);

len += SysStringLen(str2);

len += 4; // 保证有'\0'结尾

BSTR result = SysAllocStringLen(NULL, len);

memset(result, 0, len * 2); // 双字节字符

StrCat(result, str1);

StrCat(result, str2);

*retVal = result; // 设置返回值指针。注:不释放内存

return S_OK;

}

4. JS中调用

function test_str_cat()

{

var a = "123";

var b = "abc";

try {

var obj = document.getElementByIdx_xx_x("obj");

var retVal = obj.StringAdd(a, b);

alert("RetVal: " + retVal);

} catch (e) {

alert("JS ERROR: " + e.message);

}

}

(三)修改传入字符串内容

1. 原则上,不应修改传入字符串的内存数据,否则可能破坏数据,造成js端异常。

2. 使用中,可通过修改传入字符串缓冲区内容的方法,实现参数传递。

3. 不能使用SysFreeString破坏传入的BSTR参数,否则会破坏js内存结构

4. COM中C++定义

STDMETHODIMP CJsAtl::StrModify(BSTR str)

{

int len = SysStringLen(str); // 注:此方法修改BSTR,不能破坏原占用内存,不能越界访问

for (int i = 0; i < len; i++)

str[i] = '0' + i;

return S_OK;

}

5. JS调用

function test_str_modify()

{

var str = "abcdefghijklmn";

try {

var obj = document.getElementByIdx_xx_x("obj");

obj.StrModify(str);

alert("After modify: " + str);

} catch (e) {

alert("JS ERROR: " + e.message);

}

}

6. 测试执行

原字符串: abcdefghijklmn

调用后: 0123456789:;<=

(四)数组参数

1. 在使用时,有时需要使用数组传参,如S4Execute( )的inBuff/ outBuff。

2. JS中整形数据不分Byte/ Short/ Int等,因此数组元素类型都为int (COM中的VT_I4,其中I表示整形、4表示4字节)

3. JS中的Array在COM中是一个实现了IDispatch的对象,可通过IDispatch接口api进行操作。关于IDispatch请看下一节介绍。

4. COM中C++定义

下面代码中定义了两个函数 GetArrayNumberOfIndex、GetArrayLength两个函数,功能分别获取数组长度和获取指定序号元素

以下代码含义,请参考下一节 “IDispatch接口介绍”

JS数组在COM中是一个IDispatch对象,获取长度,实际是获取其中名为“length”的属性值。

而获取最后一个数组,实际是获取名为“4”的属性值(假设5个元素)

STDMETHODIMP CJsAtl::GetLastElement(VARIANT vArray, LONG* retVal)

{

int len = 0;

HRESULT hr = GetArrayLength(vArray.pdispVal, &len);

if (FAILED(hr))

return hr;

hr = GetArrayNumberOfIndex(vArray.pdispVal, len - 1, retVal);

return S_OK;

}

// ***

// 获取Javascript数组长度

// Javascript中的数组length只计算列表中下标为数字的部分

// ***

HRESULT GetArrayLength(IDispatch* pDisp, int* pLength)

{

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))

{

*pLength = varValue.intVal;

return hr;

}

else

{

return hr;

}

}

// ***

// 获取Javascript数组中指定位置的整数元素值

// ***

HRESULT GetArrayNumberOfIndex(IDispatch* pDisp, int index, int * pValue)

{

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 hr;

//

// 调用COM过程,访问指定下标数组元素,根据dispId 将元素值保存在varValue中

//

hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTY GET , &noArgs, &varValue, NULL, NULL);

if (SUCCEEDED(hr))

{

*pValue = varValue.intVal; // 将数组元素按int类型取出

return hr;

}

else

{

return hr;

}

}

5. JS中调用

function test_get_last()

{

var array = new Array(0, 1, 2, 3); // 定义数组

try {

var obj = document.getElementByIdx_xx_x("obj");

var lastElement = obj.GetLastElement(array); // 获取数组最后一个元素

alert("lastElement: " + lastElement);

} catch (e) {

alert("JS ERROR: " + e.message);

}

}

6. 测试执行

数组定义:{0,1,2,3}

最后元素:3

(五)IDispatch接口介绍

1. C程序调用时,调用者必须预先知道接口规范(如,参数类型、参数字节长度、参数顺序等)。由于不同语言这些规范有所不同,COM未解决不同语言之间调用,提供了IDispatch接口。

2. IDispatch要求其实例必须自我描述,即拿到一个对象后,可从对象中直接获取调用方式,而无须预先明确。

3. IDispatch中通过VT_TYPE来指定相关类型,如 VT_I4为4字节整形、VT_BSTR为unicode字符串,VT_DISPATCH表示是一个IDispatch对象

4. 给对象中每一属性或函数(Method)分配一个整形Id和一个字符串name,调用者可以通过name字符串确定如何调用。如,若name为"length"的属性,调用者就理解为长度。由于这里通常是根据name来理解相应属性,因此name描述应足够准确。如,以"length()"为名称的函数实现整数相加功能就是不恰当的。

5. 使用IDispatch对象时,首相调用 IDispatch::GetIDsOfNames()将属性、函数名称作为参数,获取对应的属性、函数id。

6. 再调用IDispatch::Invoke() 将id作为参数,实际调用功能。

7. 若为获取属性值,则 Invoke()调用时,传入 Dispatch_PropertyGet标志。

8. 若为设置属性值,则Invoke()调用时,传入 Dispatch_PropertyPut标志。并在 DispParams参数中指定修该属性改为何值。DispParams结构说明见后。

9. 若为调用函数,则 Invoke()调用时,传入 Dispatch_Method标志。若该Method需要参数,则通过IDispatch::Invoke()的DispParams参数指定。

10. DispParams结构使用举例:

DISPPARAMS dispparams;

dispparams.rgdispidNamedArgs = &dispidOfNamedArgs;

dispparams.cArgs = 1;

dispparams.cNamedArgs = 1;

dispparams.rgvarg = new VARIANTARG[1];

dispparams.rgvarg[0].vt = VT_I4;

dispparams.rgvarg[0].intVal = 123;

a. 上面代码是一个用于给Method传参的简单例子,先创建一个DispParams对象

b. cArgs指定Method中的参数个数。

c. cNamedArgs指定Method中已经命名的参数个数。(命名参数是对应无名参数的概念。有些语言可定义不定参数,此时IDispatch的描述中不会给参数分配名称,而是调用时以无名参数存在。如,JS中Array对象的push()方法,可支持不定个数的参数)

d. rgvarg 为实际参数数组,每一元素表示一个参数,其中.vt表明此元素的数据类型,intVal项是一个C++联合结构,如vt == VT_I4时,应以intVal = xxx方式赋值;若 vt == VT_BSTR,则应以 bstrVal = xxx方式赋值

11. 举例:两个参数,都是无名称参数,第一个为整形,第二个为BSTR型

DISPPARAMS dispparams;

dispparams.rgdispidNamedArgs = NULL;

dispparams.cArgs = 2;

dispparams.cNamedArgs = 0;

dispparams.rgvarg = new VARIANTARG[2]; // 2个参数,分配2个空间

dispparams.rgvarg[0].vt = VT_I4; // 整形

dispparams.rgvarg[0].intVal = 123;

dispparams.rgvarg[1].vt = VT_BSTR; // 字符串型

dispparams.rgvarg[1].bstrVal = L"abcd";

(六)修改输入数组内容

1. 第五节介绍了如何从JS向COM传递数组参数,以及如何在COM中获取参数。本节介绍如何在COM中修改JS传入的数组。

2. 修改JS数组值时,首先通过GetIDsOfNames获取指定序号元素的dispid;然后调用Invoke(),传入Dispatch_PropertyPut标志表明写操作,并在DispParams结构中指明此元素类型和元素值。

3. COM中C++定义

STDMETHODIMP CJsAtl::ArrayModiy(VARIANT vArray)

{

SetArrayNumberOfIndex(vArray.pdispVal, 0, 123); // 修改数组第[0]个元素,值为

return S_OK;

}

// ***

// 设置Javascript数组中指定位置的整数元素值

// ***

HRESULT SetArrayNumberOfIndex(IDispatch* pDisp, int index, int value)

{

CComVariant varName(index, VT_I4);

DISPID dispId;

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 hr;

DISPID dispidPut = DISPID_PROPERTYPUT; // put操作

DISPPARAMS dispparams;

dispparams.rgvarg = new VARIANTARG[1]; // 初始化rgvarg

dispparams.rgvarg[0].vt = VT_I4; // 数据类型

dispparams.rgvarg[0].intVal = value; // 更新值

dispparams.cArgs = 1; // 参数数量

dispparams.cNamedArgs = 1; // 参数名称

dispparams.rgdispidNamedArgs = &dispidPut; // 操作DispId,表明本参数适用于put操作

hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTY PUT, &dispparams, NULL, NULL, NULL);

return hr;

}

4. JS调用

function test_set_first()

{

var array = new Array(0, 1, 2, 3);

try {

var obj = document.getElementByIdx_xx_x("obj");

obj.ArrayModiy(array);

alert("first element: " + array[0]);

} catch (e) {

alert("JS ERROR: " + e.message);

}

}

5. 测试执行

原数组:{0, 1,2,3}

修改后:{123,1,2,3}

(七)增加数组内容

1. 在COM中无法向JS中一样,直接增加数组元素。只能使用属性、方法的方式访问数组对象,并以此产生增加数组元素的效果。

2. JS的Array中包含push( )、 pop( )两个方法,用于在数组尾部增减元素。在COM中需要增减元素时,可通过IDispatch:: Invoke( )接口调用 "push"、"pop"方法来实现。

3. COM中C++定义

STDMETHODIMP CJsAtl::AddNewElement(VARIANT vArray)

{

AddArrayElement(vArray.pdispVal, 123); // 增加元素,值为 123

return S_OK;

}

// ****************************************************

// 向js数组中增加元素

// ****************************************************

HRESULT AddArrayElement(IDispatch* pDisp, int value)

{

HRESULT hr = 0;

DISPID dispid[2] = {0};

CComBSTR funcName(L"push");

hr = pDisp->GetIDsOfNames(IID_NULL, &funcName, 1, LOCALE_USER_DEFAULT, dispid);

if (FAILED(hr))

return hr;

DISPID dispidNamed = DISPID_UNKNOWN;

DISPPARAMS params;

params.rgdispidNamedArgs = NULL;

params.cArgs = 1;

params.cNamedArgs = 0;

params.rgvarg = new VARIANTARG[1];

params.rgvarg[0].vt = VT_I4;

params.rgvarg[0].intVal = value;

hr = pDisp->Invoke(dispid[0], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

return hr;

}

4. JS调用

function test_add_element()

{

var array = new Array(0, 1, 2, 3);

try {

var obj = document.getElementByIdx_xx_x("obj");

obj.AddNewElement(array);

alert("length: [" + array.length + "] " + array[array.length - 1]);

} catch (e) {

alert("JS ERROR: " + e.message);

}

}

5. 测试执行

原数组:{0,1,2,3}

增加后:{0,1,2,3,123}

(八)以数组传参方式,JS调用S4Execute( )

1. 本例展示如何在JS中执行精锐4锁内程序,且以数组方式处理参数。

2. 本例在Execute传参时,直接以整形数组表示字节数组,而不再需要Hex字符串形式,使得JS端接口更加直观。

3. JS代码

var obj = document.getElementByIdx_xx_x("obj");

var deviceID = "123";

var userPin = "12345678";

var fileID = "0001";

var inBuff = new Array(1, 2, 3, 4);

var outBuff = new Array(0, 0, 0, 0);

var ret = 0;

try {

ret = obj.OpenLock(deviceID);

ret = obj.ChangeDir("\\");

ret = obj.VerifyPin(userPin);

ret = obj.Execute(fileID, inBuff, outBuff);

ret = obj.Close();

} catch (e) {

alert("JS Exception: " + e.message);

}

// JS数组操作,打印结果

var str = "";

for (var i = 0; i < outBuff.length; i++)

str += " " + outBuff[i];

alert(str);

4. ActiveX代码

SENSE4_CONTEXT g_ctx = {0}; //全局变量保存当前打开的ctx

// 打开设备,以设备ID作为筛选条件,若设备ID指定为空串,则打开第一把锁

STDMETHODIMP CS4ActiveX::OpenLock(BSTR deviceID, LONG* retVal)

{

SENSE4_CONTEXT * pctx = NULL;

unsigned long ret = 0;

unsigned long size = 0;

unsigned long devCount= 0;

unsigned long i = 0;

char bDeviceID[9] = {0};

char bUserPin[9] = {0};

S4Enum(NULL, &size);

if (size == 0)

{

*retVal = S4_NO_LIST;

goto cleanup;

}

pctx = (SENSE4_CONTEXT*) malloc(size);

ret = S4Enum(pctx, &size);

if (ret != S4_SUCCESS)

{

*retVal = ret;

goto cleanup;

}

// 获取ascii格式的设备ID

WideCharToMultiByte(CP_ACP, 0, deviceID, SysStringLen(deviceID), bDeviceID, 9, NULL, NULL);

// 遍历,寻找deviceID为指定值的设备

devCount = size / sizeof(SENSE4_CONTEXT);

for (i = 0; i < devCount; i++)

{

if (strlen(bDeviceID) == 0) // 未指定设备ID,返回第一把锁

{

break;

}

if (0 == memcmp(bDeviceID, pctx[i].bID, 8))

{

break;

}

}

// 没有找到

if (i == devCount)

{

*retVal = S4_NO_LIST;

goto cleanup;

}

memcpy(&g_ctx, &pctx[i], sizeof(SENSE4_CONTEXT));

ret = S4Open(&g_ctx);

if (ret != S4_SUCCESS)

{

*retVal = ret;

goto cleanup;

}

*retVal = S4_SUCCESS;

cleanup:

if (pctx)

{

free(pctx);

pctx = NULL;

}

return S_OK;

}

STDMETHODIMP CS4ActiveX::ChangeDir(BSTR dir, LONG* retVal)

{

char bDir[20] = {0};

WideCharToMultiByte(CP_ACP, 0, dir, SysStringLen(dir), bDir, 20, NULL, NULL);

*retVal = S4ChangeDir(&g_ctx, bDir);

return S_OK;

}

STDMETHODIMP CS4ActiveX::Execute(BSTR fileID, VARIANT inBuff, VARIANT outBuf, LONG* retVal)

{

char bFileID[5] = {0};

BYTE * bInBuff = NULL;

BYTE * bOutBuff = NULL;

int inBuffSize = 0;

int outBuffSize = 0;

unsigned long size = 0;

unsigned long ret = 0;

int i = 0;

int tmp = 0;

GetArrayLength(inBuff.pdispVal, &inBuffSize);

GetArrayLength(outBuf.pdispVal, &outBuffSize);

if (inBuffSize > 0)

bInBuff = (BYTE*) malloc(inBuffSize);

if (outBuffSize > 0)

bOutBuff = (BYTE*) malloc(outBuffSize);

for (i = 0; i < inBuffSize; i++)

{

GetArrayNumberOfIndex(inBuff.pdispVal, i, &tmp);

bInBuff[i] = (BYTE)tmp;

}

WideCharToMultiByte(CP_ACP, 0, fileID, SysStringLen(fileID), bFileID, 5, NULL, NULL);

ret = S4Execute(&g_ctx, bFileID, bInBuff, inBuffSize, bOutBuff, outBuffSize, &size);

if (ret != S4_SUCCESS)

{

*retVal = ret;

return S_FALSE;

}

for (i = 0; i < size; i++)

{

SetArrayNumberOfIndex(outBuf.pdispVal, i, bOutBuff[i]);

}

return S_OK;

}

STDMETHODIMP CS4ActiveX::VerifyPin(BSTR userPin, LONG* retVal)

{

unsigned char bUserPin[9] = {0};

WideCharToMultiByte(CP_ACP, 0, userPin, SysStringLen(userPin), (char*)bUserPin, 9, NULL, NULL);

*retVal = S4VerifyPin(&g_ctx, bUserPin, 8, S4_USER_PIN);

return S_OK;

}

STDMETHODIMP CS4ActiveX::Close(LONG* retVal)

{

*retVal = S4Close(&g_ctx);

return S_OK;

}