Javascript调用ActiveX对象

今天看到淘宝UED团队发布了一个Chrome扩展,用于支持在Chrome浏览器点击"和我联系"按钮会弹出旺旺的聊天窗口。我把这个扩展下载了下来,发现里面使用了插件,不理解这个"npwangwang.dll"对应的代码就不能理解这个扩展,Google官方文档中没有怎么讲插件的开发,也找不到什么好的实例。如果淘宝将这个扩展的源代码公开就好啦,刚好一直想研究下Mozilla NPAPI的使用,毕竟Javascript的接口很有限。Chrome OS的右上角要显示很多系统信息,比如电池的电量,这通过浏览器扩展没法完成,我想要直接调用系统接口就应该通过插件来完成。花了点时间先研究了下ActiveX组件的编写,以及在Javascript如何使用ActiveX组件,非IE浏览器都不支持ActiveX,所以只能在IE类浏览器来运行该实例了。我大致看过潘爱民的《COM原理和应用》,学到一些些皮毛,然后对CORBA也有一些皮毛的认识,虽然很晕,但是这些皮毛知识对于我尝试这个例子还是有一些帮助的。

        在学习的过程中,我发现下面两篇文章很有指导意义,代码基本是从第一篇文章拷贝而来。

1、http://www.diybl.com/course/3_program/c++/cppsl/2008827/138079.html

2、http://www.chenjiliang.com/Article/View.aspx?ArticleID=3387

        第一篇文章先写了一个DLL,这个DLL实现了求两个数之和的函数。接着实现了一个简单的ATL对象,通过该ATL对象调用DLL中实现的函数,最后在Javascript中调用该ATL对象相应的方法以求两个数之和。

       我想着扩展一下这个例子,在DLL中实现了一个函数以获取笔记本电脑电池电量的剩余时间,关于电池的信息,Windows提供了一个很方便的API-GetSystemPowerStatus供开发者使用,该API返回一个结构体如下:
typedef struct _SYSTEM_POWER_STATUS {
  BYTE ACLineStatus;
  BYTE BatteryFlag;
  BYTE BatteryLifePercent;
  BYTE Reserved1;
  DWORD BatteryLifeTime;
  DWORD BatteryFullLifeTime;
} SYSTEM_POWER_STATUS,  *LPSYSTEM_POWER_STATUS;
       通过MSDN可以查到结构体各字段的意义,这个例子中只用到了第五个字段用以获取电池的可持续时间(以秒为单位,当笔记本电脑直接接着交流电时会返回-1),当然也可以一次性返回整个结构体,不过这需要定义比较复杂的IDL类型,我想着返回简单int型比较简单,所以也就返回这一个值了。
       首先新建一个DLL工程,就取名为CalcImpl工程,然后新建一个dll.h和dll.cpp文件,在dll.h写入以下代码:
#ifndef _DLL_H_
#define _DLL_H_

#if defined DLL_EXPORT
#define DECLDIR _declspec(dllexport)
#else
#define DECLDIR _declspec(dllimport)
#endif

extern "C"
{
    DECLDIR int Add(int a, int b);
    DECLDIR int ShowBatteryLifeTime(void);
}

#endif
       在dll.cpp写入以下代码:
#include <windows.h>

#define DLL_EXPORT
#include "dll.h"

extern "C"
{
    DECLDIR int Add(int a, int b)
    {
        return (a + b);
    }

    DECLDIR int ShowBatteryLifeTime(void)
    {
        SYSTEM_POWER_STATUS SystemPowerStatus;
        GetSystemPowerStatus(&SystemPowerStatus);
        int BatteryLifeTime = SystemPowerStatus.BatteryLifeTime;

        return BatteryLifeTime;
    }
}
       Build会生成CalcImpl.lib和CalcImpl.dll文件。接着新建一个ATL Project,取名为CalcCtrl,完成之后会自动生成很多文件。接着为工程添加一个类,在对话框中选择"ATL Simple Object",在接下来的对话框中输入"MyTest",在Options中勾上"Support"多选框中的"Connection points"和"IObjectWithSite(IE object support)",点finish之后工程会生成MyTest对象,自动生成MyTest.h和MyTest.cpp等文件。然后选择类视图,右击"IMyTest"项,然后选择"Add"->"Add Method",在弹出的对话框中的"Method name"处填入"GetBatteryLifeTime",这个方法是最后供Javascript使用的。我发现这儿有一个奇怪的事情,在为方法名选择参数时,out和retval多选框始终都是灰色的,而in确实可以选上的。因为根据用户的选择会生成相关的代码,此处如果不能设置好返回类型,那么就必须手动修改一些文件了。CalcCtrl.idl文件中有接口的定义,接口的实现则放在MyTest.cpp中。编译CalcCtrl.idl文件居然不会对MyTest.h和MyTest.cpp做相应的修改,有点不方便。"GetBatteryLifeTime"只有一个int型的返回值,手动修改CalcCtrl.idl中该接口的声明如下:
GetBatteryLifeTime([out, retval]INT* out);
        对MyTest.h和MyTest.cpp也做相应的处理,因为两个文件中都是C++的代码,所以去掉[out, retval]便可。在MyTest.h头文件中添加以下两句:
#include "../CalcImpl/dll.h"                    //用于包含相关函数声明
#pragma comment(lib, "CalcImpl.lib") //隐式使用CalcImpl.dll所需要的   
        第一篇文章中直接通过LoadLibrary/GetProcAddress/FreeLibrary来使用CalcImpl.dll中的函数,这样太麻烦了,即要声明函数指针类型,又要注意释放句柄,不如通过隐式调用来使用CalcImpl.dll中的函数。在MyTest.cpp文件的CMyTest::GetBatteryLifeTime函数写入以下代码:
STDMETHODIMP CMyTest::GetBatteryLifeTime(INT* out)
{
     //只是简单地调用CalcImpl.dll中的函数
    *out = ShowBatteryLifeTime();

    return S_OK;
}
       build CalcCtrl工程将生成CalcCtrl.dll,不过会有如下的错误提示:
Project : error PRJ0050: Failed to register output.  Please try enabling Per-user Redirection or register the component from a command prompt with elevated permissions.
       VS似乎在尝试注册该组件,权限不够所以失败了,在一个普通的命令窗口使用regsvr32注册组件也会失败,如果使用管理员身份打开命令窗口就能成功注册该组件了。上面这个错误没有什么影响,因为注册是要交给浏览器自动完成的,这儿生成CalcCtrl.dll就好了。
       当网页中嵌入ActiveX对象时,浏览器会判断该对象是否已经存在,如果不存在将提示下载相关的资源,下载完成之后如何处置,这就需要一个inf文件来说明了。因此创建一个setup.inf文件,写入以下内容:
[version]
; version signature (same for both NT and Win95) do not remove
signature="$CHICAGO$"
AdvancedINF=2.0

[Add.Code]
CalcCtrl.dll=CalcCtrl.dll
CalcImpl.dll=CalcImpl.dll
setup.inf=setup.inf

[install.files]
CalcCtrl.dll=CalcCtrl.dll
CalcImpl.dll=CalcImpl.dll
setup.inf=setup.inf

[CalcCtrl.dll]
clsid={A83904E6-3288-431C-8213-8874E228990E}
file-win32-x86=thiscab
FileVersion=1,0,0,1
DestDir=11
RegisterServer=yes

[CalcImpl.dll]
file-win32-x86=thiscab
DestDir=11
FileVersion=1,0,0,1
RegisterServer=yes

[setup.inf]
file=thiscab

[RegisterFiles]
%11%\CalcCtrl.dll

; end of INF file
        "DestDir=11"表示将DLL下载到系统目录下(WINDOWS/SYSTEM32)。CalcImpl.dll、CalcCtrl.dll和setup.inf三件东西都准备好了之后就要开始打包了。打开一个cmd窗口,输入"iexpress",根据向导一步步做,最后生成一个calc.CAB文件。接着写一个测试网页,内容如下:
<HTML>
<HEAD>
<TITLE>Test ActiveX</TITLE>
<OBJECT ID="CalcCtrl" NAME="CalcCtrl"
 CODEBASE="calc.CAB#version=1,0,0,1"
 CLASSID="clsid:A83904E6-3288-431C-8213-8874E228990E">
 </OBJECT>
<script language="javascript">
function doTest()
{
    var leftTime = CalcCtrl.GetBatteryLifeTime();
   
    if(leftTime == -1)
    {
        alert("unknown");
    }
    else
    {
        var hours;

        //Javascript的'/'运算符并不会取整
        hours = leftTime / 3600;
   
        alert(hours + "hours");
    }
}
</script>

</HEAD>
<BODY>
<input type="button" value="Get left time" id="btnOK" onclick="doTest();"></input>
</BODY>
</HTML>
        上面用到的clsid可以在工程中的MyTest.rgs文件找到,这个文件中包含一些要写入注册表的信息,setup.inf中的那个clsid随便填似乎都可以,这个页面中的clsid就必须填正确了。使用IE浏览器打开该页面之后,会提示有不安全的内容,点击"允许阻止的内容"之后,会接连弹出两个对话框(这里是在单机环境进行测试的,在真实的网路环境下,可以还要配置下IE浏览器,让其运行未验证的ActiveX对象或者添加信任域),然后就可以在"WINDOWS/SYSTEM32"目录下发现CalcImpl.dll,CalcCtrl.dll这两个文件了。当笔记本接着交流电时,弹出的对话框将将提示"unknown",只有拔掉电源,不断刷新页面,过一会儿将显示电池的剩余使用时间。
        每次使用时都会弹出如下的对话框,很烦人。


        可以参看第二篇文章,将类MyTest从IObjectSafetyImpl派生可以解决这个问题。我还发现另外一个问题,如果使用下面的代码调用ActiveX对象:

MyTest = new ActiveXObject( "CalcCtrl.MyTest");

 

var sum = MyTest.GetBatteryLifeTime();

        浏览器并不会自动下载calc.CAB文件,所以页面运行将出错。但是如果组件已经被安装了,使用这种方法却又能正常工作。

时间: 2024-09-23 00:44:12

Javascript调用ActiveX对象的相关文章

javascript调用ActiveX接口失败的解决方案及使用心得

前段时间公司做了个比较大的项目,需要用到ocx控件,我厂大部分项目都采用C#.net,而winform程序条用ocx控件接口是相对简单的,但是javascript调用ocx接口,却和winform的用法有些不同,其实真捉摸下,也就能发现:差别不大. 笔者此次主要阐述在项目中用javascript调用ocx控件接口,也就是activeX控件时所遇到的问题及其解决方案.winform用法不在此篇中阐述. 调用activeX插件前,我们要做一些准备工作,我们介绍两种方案: 1.使用regsvr32 命

如何用Javascript捕获ActiveX对象的事件

最近参与了一个项目,我的同事在开发一个ActiveX对象,我帮他编写JS脚本来调用这个对象,其中碰到蛮多问题,最难的就是如何响应由ActiveX对象返回的对象事件.正好,现在一起总结一下.     首先,我来介绍一下COM组件,以便说明js如何响应      COM组件         方法:                     void init()                    //初始化对象                   Project createProject()  

JavaScript调用Activex控件的事件的实现方法_javascript技巧

书写成这样: <SCRIPT type="text/javascript" FOR="activexID" EVENT="onXXXevent()" > // js 处理具体内容. </SCRIPT> 当acitveX控件 onXXXevent()事件被触发时,调用此方法. 如果一个半个的话还好说,几十个这样的函数,把我的页面占据的满满的.况且在VS2008中,"设置选定内容格式化"总是提示:"

Android中在WebView里实现Javascript调用Java类的方法_Android

为了方便网页和Android应用的交互,Android系统提供了WebView中JavaScript网页脚本调用Java类方法的机制.只要调用addJavascriptInterface方法即可映射一个Java对象到JavaScript对象上. 1.映射Java对象到JavaScript对象上 复制代码 代码如下: mWebView = (WebView) findViewById(R.id.wv_content); mWebView.setVerticalScrollbarOverlay(tr

XMLHTTP ActiveX对象的访问ASP区段变量的其他方法

几个月以前,一个同事到我这里来,说他有一个客户端JavaScript的问题.我不得不向他解释说,活动服务器页面(ASP)的区段变量不能从客户端访问,除非它们通过cookie或者隐藏的输入框被传递.但是我没有停止继续寻找从客户端访问ASP区段变量更好方法的工作.   最近在研究如何不需要上传和下载页面就能够从服务器获取信息时,我读到了关于XMLHTTP ActiveX对象的内容.从本质上讲,这个对象为Internet Explorer和Web服务器通讯并接收回应提供了一种方法.它是MSXML动态加

js创建调用ocx对象的几种方法,检测ocx是否可用

1:创建ActiveX对象   在web中使用ActiveX组件有两种方法,一是如下<HTML><HEAD><TITLE>rep_print_medcan.CAB</TITLE></HEAD><BODY><OBJECT ID="Rep_Print_MedCan21"CLASSID="CLSID:C0558D51-6AFD-11D5-BF5C-005070024001"CODEBASE=&q

javascript使用activex控件的代码_javascript技巧

首先在页面加入avtivex 复制代码 代码如下: <object classid="clsid:B094CE95-40EA-4798-99DC-4B8CE548121F" height="100%" width="100%" id="UiMonitor"> </object> javascript 代码 复制代码 代码如下: <%--使用activex事件--%> <script t

JavaScript调用客户端Java程序的方法_javascript技巧

本文实例讲述了JavaScript调用客户端Java程序的方法.分享给大家供大家参考.具体如下: 一般说来,JavaScript是没有权限调用客户端可执行程序的.但有时有些特殊需要,又不得不调用.例如有时涉及客户端文件的操作此时,当然我们可以用 FileSystemObject对象来完成.又例如我们前些时做的项目中,需求是点击页面一个链接不弹出IE的那个文件下载的对话框 (有三个选项:打开.保存.取消的那个),直接把文件下载到指定目录 .此时就不得不绕过浏览器的这个对话框,自己写一个客户端的ja

activex-JSP页面用js调用ActiveX使用摄像头

问题描述 JSP页面用js调用ActiveX使用摄像头 这是一个VB程序,调用了一个TesoSeeu.ocx打开摄像头拍照的程序http://pan.baidu.com/s/1sjyyo0H 现在我想在JSP页面上使用这个ocx,可以吗? 求各位大神给一个例子 这个ocx好像是拍照后把数据保存在内存,js没法调用了吧? 解决方案 可以,不过需要预先安装这个控件到客户端计算机,并且在浏览器的安全设置中允许.而且不是所有浏览器都支持ocx,只能用IE或者兼容的浏览器. 解决方案二: 控件有暴露接口给