OCX 打包 CAB 与 JS 调用详细教程

最近在做一个 WEB 项目,需要调用 OCX 进行连接读卡器读卡。本来并不想用 OCX 技术,因为 ActiveX 技术是微软出品,这样就导致整个系统只能使用 IE 浏览器(其他浏览器可以通过插件的形式支持 OCX 的调用)。但尝试了很多方法调用客户端 DLL 发现都走不通。最后无奈之下,才打算研究 OCX 技术。

网上都说 ActiveX 技术很麻烦,会出现各种错误,不做的时候不知道,真正需要研究时候,才发现确实如此。主要问题是,报错和问题的真正原因没有关系或者说关系不大,甚至很多时候没有报错,只有不断尝试。

言归正传,下面来讲一下如何搞定 OCX。

首先先声明,本教程只关于 OCX 打包 CAB 和 JS 调用 OCX,这里不探讨如何通过 C++ 写 OCX。因为本人只做 Java 开发,对于C++ 并没有研究。

开发系统:win8.1 64位

测试通过系统:win7 64 位、win8.1 64位、xp 32位

OCX 和 CAB 的关系。

大家知道,得到 OCX 之后,如果想要调用,首先要注册。在不注册的情况下,没办法调用 OCX 。

最简单的注册办法是手动注册。手动注册需要在命令行操作。对于开发人员来说,多多少少要和命令行打交道,可能没有问题,不过对于使用我们系统的客户来说,让他进行命令行操作的确不合适。不管我们文档写得多么详细,对客户来说这都是不友好的,而且手动注册也设计到安全性问题。所以就需要在客户不知情的情况下自动对 OCX 注册。这也就是 CAB 的作用。

手动注册 OCX

我们假设,OCX 所在的目录是:D:/ocx/xpbutton/xpbutton.ocx

通过 regsvr32 xpbutton.ocx ,这样我们就手动注册了 OCX。

如果想卸载 OCX,我们可以反注册:regsvr32 /u xpbutton.ocx 。

注册和卸载,我们都需要以管理员身份运行 cmd 控制台。-- 这里特别需要注意!

如果在注册时候出现下面的错误:

1.首先查看是否以管理员身份运行 CMD。

2.如果还是不行,可以考虑将相应的 OCX 放到系统目录下

a). 32 位系统放到 C:\Windows\System32

b). 64 位系统放到 C:\Windows\SysWOW64

3.如果还是报错,那可以确定是缺少 DLL 导致的。各位可以下载一个工具:Dependency Walker。

这里的教程,看少了哪些 DLL,去网上下载这些 DLL,这些 DLL 就是你必须要和 OCX 一起打包到 CAB 压缩包里面去的。因为你自己电脑少了,说明客户电脑也同样可能会少这些 DLL。

这里需要注意的是:首选的是和你系统相同位数的 DLL,肯定不会错,实在找不到 64 位的,才考虑 32 位的版本。

下载了 DLL 后,将 DLL 存放到上面第二步提到的系统文件夹下面,然后再注册。

OCX 打包 CAB

首先下载 OCX 打包签名工具:ocx 打包签名工具,  访问密码 ddb4。

将需要签名和打包的 OCX 和命令放到相同文件夹。

制作签名证书:

在命令行运行下面命令:

1. 执行命令:

[plain]view plaincopy

makecert.exe -ss xpbutton -n "CN=这里随便" -sv .\xpbutton.pvk -r .\xpbutton.cer

输入三次,密码,查看控制台出现 Succeeded 表示成功。

此时生成文件:xpbutton.cert 和 xpbutton.pvk

2. 运行命令:

[plain]view plaincopy

Cert2Spc.exe .\xpbutton.cer .\xpbutton.spc

查看控制台,出现 Succeeded 表示成功。

此时会生成文件:xpbutton.spc

两条命令结束,我们一共得到三个文件:xpbutton.cer、xpbutton.pvk、xpbutton.spc

对 OCX 进行签名

打包 CAB 之前,首先需要对 OCX 本身进行签名操作,这一步很重要,如果没做,你可能就犯错了。

3. 运行命令:signtool signwizard

如果想要填写时间戳,可以填写以下地址:http://timestamp.verisign.com/scripts/timstamp.dll

等待片刻,出现下面提示,则表示对 ocx 签名完成。

控制台出现:Successfully completed signing wizard:<> 表示成功。

编写 INF 文件

INF 文件也是一个重点,如果编写错误,则不能正确打包。(各位可以下载本人编写的 INF 文件,在此基础上进行修改,保证 INF 文件的正确性)

INF 参考文件下载地址:OCX inf 文件,访问密码 49de。

如果打包 CAB 没有问题 ocx 、dll 都会下载到 c:/windows/ocx/ 目录下,方便各位卸载 ocx ,删除 dll 文件。

[version]

signature="$CHICAGO$"

AdvancedINF=2.0

[DefaultInstall]

CopyFiles=files

RegisterOCXs=RegisterFiles

[DefaultUninstall]

cleanup=1

Delfiles=files

UnRegisterOCXs=RegisterFiles

[SourceDisksNames]

1 = %DiskName%, "xpbutton.cab", 1

[SourceDisksFiles]

xpbutton.ocx=1

msvcrtd.dll=1

mfc42d.dll=1

mfco42d.dll=1

[RegisterFiles]

%30%\Windows\ocx\xpbutton.ocx

[DestinationDirs]

files=30, Windows\ocx

[files]

xpbutton.ocx=xpbutton.ocx

msvcrtd.dll=msvcrtd.dll

mfc42d.dll=mfc42d.dll

mfco42d.dll=mfco42d.dll

[xpbutton.ocx]

file=thiscab

clsid={134EE1CC-4B8A-4E74-8C41-F4990065E2E1}

FileVersion=1,0,0,1

RegisterServer=yes

[msvcrtd.dll]

file=thiscab

FileVersion=6.0.8337.0

[mfc42d.dll]

file=thiscab

FileVersion=6.0.8168.0

[mcfo42d.dll]

file=thiscab

FileVersion=6.0.8267.0

[Strings]

DiskName="Windows\ocx"

以上是本人的 inf 文件。解释几个部分。

1.这里面添加了 3 个dll,如果各位不需要将 dll 打包到 cab ,则可以参照上面蓝色的部分。如果没有 mcfo42d.dll 则将蓝色的部分全部删除,其余的不动。以此类推。

2.

[plain]view plaincopy

[xpbutton.ocx]

file=thiscab

clsid={134EE1CC-4B8A-4E74-8C41-F4990065E2E1}

FileVersion=1,0,0,1

RegisterServer=yes

进行简单解释:

file=thiscab 照搬照抄,不解释。由于是 64 位系统,本人测试这么写没问题。

32 位系统也可以这样写: file-win32-x86=thiscab

clsid 这里,最简单的办法是找到 ocx 的来源,询问制作 ocx 作者,他们知道这里应该填写什么。

如果找不到制作人,也有办法,参照前文手动注册 ocx ,然后查看搜索注册表:xpbutton

找到左边类似的注册表结构,然后 134EE1CC-4B8A-4E74-8C41-F4990065E2E1 将是我们需要的 clsid 了。

注意:本人在 C:\Windows\ocx\ 目录下注册的 xpbutton.ocx 文件。所以上面右图地址才会是C:\Windows\ocx\xpbutton.ocx

FileVersion 也是一样,最好的办法,找到 ocx 的来源(开发者),确定版本号,编写 ocx 时,代码中会有 ocx 对应的版本号。当初和 C 沟通时,看过 ocx 的 C++ 代码,里面有对应的版本信息。这里的版本信息必须和 OCX 的版本信息一致。

RegisterServer=yes 表示下载下来后自动注册此 ocx 。

大家可以看到我下面的 dll 文件的代码中都没有这一句,意思是 dll 下载下来后不需要注册,如果各位的 dll 也需要注册,对应 dll 区域也需要添加此语句。

3.对 INF 文件中绿色的部分进行解释

绿色的部分,表示 dll 的版本号,如何确定 dll 版本号。其实很简单。

找到对应的 dll 右击,查看属性,这里的文件版本,就是我们需要的版本号,请注意,不是产品版本。

OCX 打包 CAB 文件

4.运行命令:

[plain]view plaincopy

CABARC.EXE -s 6144 n xpbutton.cab xpbutton.ocx xpbutton.inf

需要解释一下这条命令:是将 xpbutton.ocx xpbutton.inf 文件打包成 xpbutton.cab 文件,如果我们需要将额外的 dll 也打包到 cab 里面,那这样写:CABARC.EXE -s 6144 n xpbutton.cab xpbutton.ocx msvcrtd.dll xpbutton.inf 以此类推。

对 CAB 文件签名

对 CAB 文件签名的过程,可以查看上文对 OCX 文件签名的过程,除了第一步此处选择的是 CAB 文件之外。其余步骤完全相同。

到目前为止,我们已经将 OCX 打包成 CAB ,但是到现在还不行。很多教程都到此为止,其实 OCX 的繁琐远还没有结束。

JS 调用 OCX(CAB)

引入 OCX 控件

新建一个 HTML 文件,我们通过如下方式引入 OCX:

一项一项解释:

id="xpButton" 表示此 object 对象的 id 为 xpButton,命名随便都可以,后续会用到。

classid="clsid:134EE1CC-4B8A-4E74-8C41-F4990065E2E1" 看到这个,应该很熟悉。classid="clsid:这部分照抄,不要改动。只改动冒号 : 后面部分就行了。(本人不小心把 clsid: 这一部分漏掉了,写成了 classid="134EE1CC-4B8A-4E74-8C41-F4990065E2E1",结果 CAB 文件下载不下来)。注意:千万别漏了 clsid。

codebase="./xpbutton.cab#version=1,0,0,1" 这一部分代码是告诉浏览器,如果找不到 clsid 为 134EE1CC-4B8A-4E74-8C41-F4990065E2E1 注册表,也就是系统中没有注册过此 xpbutton.ocx ,则去找对应的 xpbutton.cab 文件。这里 "./xpbutton.cab" 意思是和当前 html 存放在同一目录下的 xpbutton.cab 文件。“./”表示当前路径,也就是 html 所在的路径。后面的 "#version=1,0,0,1" 表示当前 OCX 的版本号,也就是 CAB 压缩包中 INF 文件里面写的 OCX 的版本号(FileVersion)。注意版本号:1,0,0,1 是用逗号 "," 分隔,不是点号 "." ,如果你用了点号 ".",那么恭喜你,你又错了。

补充说明:我们遇到过三个版本号

1. OCX 编写时,C++ 代码中规定了 OCX 的版本号。

2. OCX 打包 CAB 文件时,INF 文件中规定了当前 OCX 的版本号。

3. HTML 调用 OCX 时,CODEBASE 表明了需要调用的 OCX 版本号。

这三个版本号需要一致。

很有意思的是,如果想要升级 OCX,其实很简单,让 OCX 编写人员升级 OCX,然后我们升级 INF 文件中的版本号,再把相应 HTML 中的版本号也升级。浏览器调用此 HTML 时候,如果发现 CODEBASE 中的版本号升级了,则会自动重新下载 CAB 文件,并重新注册。

之前,为了测试 OCX 升级后是否本身有问题,手动注册此 OCX ,没有重新打包 CAB,结果每次访问都发现注册的是 CAB 中上一个版本的 OCX。说明只要发现注册过的 OCX 版本和 CAB 版本不一致的情况下,浏览器都会重新下载 CAB,并重新注册。

这里还要说明一点就是,OCX 被编写出来后。clsid 就固定了。此 OCX 不管注册到哪台电脑上,查看注册表,clsid 都是一样的,不会改变。这也就是为什么我们在 HTML 里面,直接可以写上 clsid 的原因。因为客户下载 CAB ,自动注册后,OCX 的 clsid 就是我们编写 C++ 时候规定的 clsid。

JS 调用 OCX 方法

解释以上代码

1.xpButton.AboutBox() ;

xpButton 其实不是凭空出现的,这里的 xpButton 是 标签的 id,大家看上面的截图可以看到,……。

2. xpButton.AboutBox();

AboutBox() 其实是 OCX 中的一个方法。各位如果想要知道此 OCX 中有哪些方法,首选的是找 OCX 开发者,在找不到的情况下,通过 tstcon32 软件,各位可以在这里下载:tstcon32 ActiveX 容器访问密码 904d。如果不能使用,根据报错提示下载相应 DLL 即可。(研究 OCX 留下的后遗症,总觉得某些软件会缺少 DLL )

3. 为什么要 try catch

OCX 的繁琐和摸不着头脑,很重要的原因是,即使调用失败它也不会报错。所以,我们必须要在这里 try catch 手动弹出错误信息。但问题其实也没有那么简单,即使是有报错信息,提示也让人摸不到头脑。

注意,各位一定要记得 try catch,不然任何错误都不会有提示。

下面总结一下本人遇到的报错信息与真实原因的对应关系,以防止各位各种百度、Google 最后找到的是错误的解决方案。

OCX 报错,一般情况下,都是本机测试通过后,部署到服务器或者使用其他人的电脑,发现调用失败。

1.[object Error]、Error:找不到成员

如果是遇到上面的报错:[object Error] 、Error:找不到成员

1.首先确定C:\Windows\ocx 目录下是否有下载的 OCX 文件(如果各位下载了本教程中的 INF 文件,则到C:\Windows\ocx 目录下去找),如果该目录下没有任何文件或者没有该目录,则就对照上文,查看是否是 INF 文件编写有问题,或者是 HTML 引入 OCX 对象有错误,导致下载失败。

2.如果 OCX 文件已下载,则表示虽然 CAB 文件没问题,但 OCX 未注册或者说是注册失败。注册失败的原因,我们首先需要确认是否是缺少 DLL 。如何确认,可以使用上文提到的Dependency Walker软件。如果发现确实是少了 DLL,那我们应该重新打包 CAB,将所缺少的 DLL 文件一起打包到 CAB 中。很多时候,我们通过 CAB 自动注册 OCX ,大多数情况下是不会报任何的错误的。即使是因为缺少 DLL 没有注册成功,也没有任何提示。本人在很多电脑上测试的结果是,只有一台 Win7,出现了缺少 DLL 的报错示,其他所有电脑都没有任何的错误提示。这里特别需要注意:打包 CAB 文件后,请用多台电脑进行测试,最好测试不同的系统,也尽量可以挑选公司非开发人员的电脑。千万别在自己电脑上测试通过,或者某些 OCX 开发者电脑上测试通过后便认为其没有问题。

2. 对象不支持此属性或方法

这个错误,在开发的过程中,是必然会遇到的。刚开始,一直以为是调用方式有问题,在尝试了网上能找到的所有其他不同调用方式后发现,并非如此。如果能保证 OCX 的正确性的前提下,此问题的根本原因是因为浏览器对于 OCX 不信任,拦截了 OCX 里面的方法调用。解决此问题的根本办法是修改浏览器设置。

3.修改浏览器安全设置

网上可以找到很多浏览器的修改点,其实大部分不需要修改,修改了反而降低了安全性,增加风险。

反选“对该区域中的所有站点要求服务器验证(https)”,填上服务器地址,例如:http://192.168.0.61 然后添加,添加完成后,再次勾选"对该区域中的所有站点要求服务器验证(https)"。这一步的操作是保证浏览器信任此站点。

此处修改信任站点的安全性级别,将图上的两个,由原来的禁止,修改为提示或者启用。

其实在首次访问 CAB 网页前,我们就应该先修改浏览器安全性策略,修改完成后再访问,一般都不会出现什么问题。

至此,OCX 的相关知识已经全部介绍完毕,此教程应该可以帮助大家少走一些弯路。这也是最近两个星期的研究成果。

2015.08.26 补充

1.注意:如果使用了 Java 开发,后台使用了 Spring。则应该在 Web.xml 中添加下面的代码。

[html]view plaincopy

default

*.cab

上面代码告诉 Spring 不要拦截 *.cab 静态文件。

2.JSP 引入 CAB 文件

[html]view plaincopy

classid="clsid:F6F2B22E-FC89-489F-967B-9676EB269F55"

CODEBASE="${pageContext.request.contextPath}/cab/readcard.cab#version=1,0,0,1"

>

在工程中引入 CAB 文件,我们需要写

[html]view plaincopy

CODEBASE="${pageContext.request.contextPath}/cab/readcard.cab#version=1,0,0,1"

${pageContext.request.contextPath} 表示工程名

cab 文件存放在 webapp/cab 下面

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,669评论 0 27
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • 加入合金元素使相图的E点左移,出现了含碳量低于2.11%的合金钢中出现莱氏体,这种合金就成为莱氏体钢。
    孔李聃丘阅读 1,097评论 0 1