对于不懂C++和VB的我, 在工作上却遇到需要重写旧ActiveX控件的任务.

好在客户机都是Windows PC, 基本上都有.net framework 2.0, 勉强用C#实现可以满足需求

所以我参考了不少前辈在网上分享的示例,

在.net framework 2.0的基础上用C#实现ActiveX小控件.

一波三折, 好不容易测试成功, 所以在此做一次简单的操作整理.

1. 准备一个的ActiveX控件, 这里的示例仅实现最简单的功能, 只包含IE与ActiveX之间的传值功能.

新建一个窗体控件库:

修改工程的属性

注意一下的"生成"中的选择框, 此处根据是Debug和Release分开的, 切换模式时, 请检查此项是否已经勾上:

修改AssemblyInfo, 添加以下代码:

删除默认的UserControl1, 自己新建一个UserControl:

新建一个接口:

代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Runtime.InteropServices;
  5.  
  6. namespace ActiveXDemo
  7. {
  8. [ComImport, GuidAttribute("B5030596-63D6-4B21-9D01-90698B1FC277")]
  9. [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
  10. public interface IObjectSafety
  11. {
  12. [PreserveSig]
  13. int GetInterfaceSafetyOptions(
  14. ref Guid riid,
  15. [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions,
  16. [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions
  17. );
  18. [PreserveSig()]
  19. int SetInterfaceSafetyOptions(
  20. ref Guid riid,
  21. [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask,
  22. [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions
  23. );
  24. }
  25. }

让我们刚刚新建的UserControl实现上面的接口, GUID处请使用VS自带的Tools->Create GUID新建GUID:

实现接口代码如下:

  1. #region IObjectSafety 成员
  2. private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
  3. private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
  4. private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
  5. private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
  6. private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
  7. private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
  8. private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
  9. private const int S_OK = ;
  10. private const int E_FAIL = unchecked((int)0x80004005);
  11. private const int E_NOINTERFACE = unchecked((int)0x80004002);
  12. private bool _fSafeForScripting = true;
  13. private bool _fSafeForInitializing = true;
  14. public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
  15. {
  16. int Rslt = E_FAIL;
  17. string strGUID = riid.ToString("B");
  18. pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
  19. switch (strGUID)
  20. {
  21. case _IID_IDispatch:
  22. case _IID_IDispatchEx:
  23. Rslt = S_OK;
  24. pdwEnabledOptions = ;
  25. if (_fSafeForScripting == true)
  26. pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  27. break;
  28. case _IID_IPersistStorage:
  29. case _IID_IPersistStream:
  30. case _IID_IPersistPropertyBag:
  31. Rslt = S_OK; pdwEnabledOptions = ;
  32. if (_fSafeForInitializing == true)
  33. pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
  34. break;
  35. default:
  36. Rslt = E_NOINTERFACE;
  37. break;
  38. }
  39. return Rslt;
  40. }
  41. public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
  42. {
  43. int Rslt = E_FAIL; string strGUID = riid.ToString("B"); switch (strGUID)
  44. {
  45. case _IID_IDispatch:
  46. case _IID_IDispatchEx: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && (_fSafeForScripting == true)) Rslt = S_OK; break;
  47. case _IID_IPersistStorage:
  48. case _IID_IPersistStream:
  49. case _IID_IPersistPropertyBag: if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && (_fSafeForInitializing == true))
  50. Rslt = S_OK; break;
  51. default: Rslt = E_NOINTERFACE; break;
  52. }
  53. return Rslt;
  54. }
  55. #endregion

为了可以与前台的js进行交换, 添加以下dll引用和命名空间:

添加一个方法, 用于调用前台js, 参数win为前台传入的window对象, func为前台js方法的名字, para为参数:

代码如下:

  1. public void SetFunc(object win, string func, string para)
  2. {
  3. IHTMLWindow2 htmlWin = win as IHTMLWindow2;
  4. if (htmlWin == null || string.IsNullOrEmpty(func))
  5. {
  6. MessageBox.Show("Assign error.");
  7. }
  8. else
  9. {
  10. string jsCode = string.Format("{0}('{1}')", func, para);
  11. htmlWin.execScript(jsCode, "jscript");
  12. }
  13. }

再添加一个方法, 用于接收前台传入的信息, 然后后台调用前台的方法显示该信息(没错, 这个功能是多此一举的, 仅为了展示前后台之间的传值)

  1. public void ShowMessage(object win, string msg)
  2. {
  3. SetFunc(win, "show", msg);
  4. }

2. 打包步骤1生成的dll, 使之成为msi安装文件

ActiveX控件部分已经完成了, 现在要把控件Build出来的dll打包成msi

新建setup工程:

添加步骤1的工程为输出:

检查属性:

Rebuild步骤1工程, 然后Rebuild步骤2工程, 在工程目录下取出安装包(例子是Release中的msi):

把这个msi放在新建一个空的文件夹, 方便进一步打包:

3. 进一步打包客户端下载后可以自动安装的Cab包

在上面的文件夹中放入以下文件:

cabarc.exe是打包cab的工具, signcode.exe为数字签名工具

build.bat文件控制cabarc打包操作, install.inf是IE安装cab的配置

build.bat文件内容如下:

第一个文件为需要生成的cab, 后面的为cab包含的文件:

  1. @echo off
  2. "cabarc.exe" -s 6144 n ActiveXDemo.cab install.inf ActiveXSetup.msi
  3. pause

install.inf文件内容如下:

在此不具体解释, 可以参考文后的参考文章

  1. [version]
  2. signature="$CHICAGO$"
  3. AdvancedINF=2.0
  4.  
  5. [Strings]
  6. Version="1.0.0.0"
  7.  
  8. [Setup Hooks]
  9. InstallerHook=InstallerHook
  10.  
  11. [InstallerHook]
  12. run=msiexec.exe /i "%EXTRACT_DIR%\ActiveXSetup.msi" /qn

双击build.bat文件, 打包cab完成:

4. 部署在一个简单网页上

准备一个简单网站(静态html也行, 不过要配搭配IIS上运行):

如图放入cab文件:

写入如下html和js脚本:

注意object标签中的clsid, 它对应前面步骤1生成的GUID

codebase, 意为: ActiveX目录下的ActiveXDemo.cab文件, 版本是1,0,0,0, 与install.inf和程序集中版本对应

IE会根据这个版本号, 决定是否下载或更新cab包

sendMsg获得ActiveX对象, 发送window对象和参数给ActiveX控件,

ActiveX控件接收到后, 根据我们定义的逻辑, ActiveX将调用前台js方法show, 回显msg参数的值

  1. <html xmlns="http://www.w3.org/1999/xhtml">
  2. <head runat="server">
  3. <title></title>
  4. </head>
  5. <body>
  6. <form id="form1" runat="server">
  7. <div>
  8. <object id="objActiveX" classid="clsid:1D1999CC-0221-4A81-A0C5-F5ABA2059249" codebase="ActiveX/ActiveXDemo.cab#version=1,0,0,0">
  9. </object>
  10. <input id="msg" type="text" />
  11. <input type="button" onclick="sendMsg()" value="提交" />
  12. </div>
  13. </form>
  14. <script type="text/javascript">
  15. function sendMsg() {
  16. var objActiveX = document.getElementById("objActiveX");
  17. var msg = document.getElementById('msg').value;
  18. objActiveX.ShowMessage(window, msg);
  19. }
  20. function show(msg) {
  21. alert(msg);
  22. }
  23. </script>
  24. </body>
  25. </html>

5. 使用测试

测试成功

要注意的是, 要下载并运行未签名的控件, 请在Interest选项中

添加信任域名, 启用与ActiveX相关设置

6. 数字签名(这里我们使用自己建立的数字证书)

使用VS自带的命令行, 创建Demo证书:

输入如下命令, 工具会让你输入3次相同的密码:

  1. makecert -ss name -n "CN=DemoName" -sv d:\certDemo.pvk d:\certDemo.cer

接着输入以下命令:

  1. cert2spc d:\certDemo.cer d:\certDemo.spc

这样就一共生成了3个文件:

放到有signcode.exe的文件夹, 双击工具, 下一步, 选择需要签名的cab包:

下一步, 选自定义:

选择刚刚生成的certDemo.spc

选择私钥文件certDemo.pvk

然后一路到底, 弹出需要你输入密码, 就输入证书的密码, 最后就完成签名了.

检查是否签名, 右键cab, 查看属性, 会多出一项:

有了证书, 客户机就可以安装证书, 双击certDemo.cer:

证书安装完成, 接着可以像步骤5一样测试.

注意: 由于证书不是官方购买的, IE认为还是不安全的, 可能会遇到IE拒绝安装的情况.

本人认为上线使用最好还是官方证书, 安全, 不需要手动安装.cer文件.

示例代码和用到的工具: http://yunpan.cn/Q7XhfsMINV5RL (提取码:2bd7)

参考文献:

http://www.cnblogs.com/still-windows7/p/3148623.html

http://blog.csdn.net/tomatozq/article/details/7656729

http://blog.csdn.net/cds27/article/details/7533479

http://www.cnblogs.com/yilin/archive/2009/09/15/1567332.html

http://www.cnblogs.com/catvi/archive/2007/08/15/1952976.html

整理:C#写ActiveX, 从代码到打包到签名到发布的示例的更多相关文章

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

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

  2. C# Activex开发、打包、签名、发布

    一.前言      最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠HTML是无法实现了,因此必须借用Activex来实现.由于本人主要擅长C#,自然本文给出了用C# ...

  3. C#制作、打包、签名、发布Activex全过程

    一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠HTML是无法实现了,因此必须借用Activex来实现.由于本人主要擅长C#,自然本文给出了用C#实现的范例 ...

  4. C# winFrom 制作、打包、签名、发布Activex全过程

    注:转自园中 http://www.cnblogs.com/still-windows7/p/3148623.html 一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调 ...

  5. C#制作、打包、签名、发布Activex全过程【转】

    http://www.cnblogs.com/still-windows7/p/3148623.html 一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠H ...

  6. Java工具创建密钥库,用于Unity 3D打包、签名、发布

    Java工具创建密钥库 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心创新! ...

  7. weex 项目开发 weexpack 项目 打包、签名、发布

    一. weexpack build android  和  weexpack run android 的 区别. (1)单纯打包 weexpack build android (2)打包并运行 wee ...

  8. weex 项目开发(六)weexpack 项目 打包、签名、发布

    一. weexpack build android  和  weexpack run android 的 区别. (1)单纯打包 weexpack build android (2)打包并运行 wee ...

  9. 将自己写的Python代码打包放到PyPI上

    如果是开源的Python代码,为了能够让大家更方便的使用,放到PyPI上也许是个非常不错的主意(PyPI:Python Package Index).刚开始我以为要将代码打包放到PyPI上是一件非常复 ...

随机推荐

  1. 如何通过Request.ServerVariables["HTTP_USER_AGENT"]获取客户端操作系统信息

    http://www.useragentstring.com/pages/api.php

  2. php获取mac用于网站绑定服务器

    php获取mac用于网站绑定服务器 <?php class GetMacAddr{ var $return_array = array(); // 返回带有MAC地址的字串数组 var $mac ...

  3. [转]SGI STL 红黑树(Red-Black Tree)源代码分析

    STL提供了许多好用的数据结构与算法,使我们不必为做许许多多的重复劳动.STL里实现了一个树结构-Red-Black Tree,它也是STL里唯一实现的一个树状数据结构,并且它是map, multim ...

  4. ajax提交表单序列化(serialize())数据

    知识点: $("#form").serialize();将表单数据序列化为标准URL编码文本字符串(key1=value1&key2=value2…). 以下用一个例子来演 ...

  5. PHPCMS 插件开发教程及经验谈

    虽说 PHPCMS 开源,但其它开发文档及参考资料实在少得可怜.进行二次开发时,自己还得慢慢去研究它的代码,实在让人郁闷. PHPCMS 的“Baibu/Google地图”实在有待改进,对于数据量比较 ...

  6. JavaScript获取Select下拉框Option的Value和Text值的方法

    Js获取select下拉列表框各个Option的Value值相对比较容易,不过获取Text值却有点麻烦,对于一个初学JavaScript的 新手来说,可能一时还无从下手,那么就请看下本文的方法,以一个 ...

  7. 大整数算法[11] Karatsuba乘法

    ★ 引子         前面两篇介绍了 Comba 乘法,最后提到当输入的规模很大时,所需的计算时间会急剧增长,因为 Comba 乘法的时间复杂度仍然是 O(n^2).想要打破乘法中 O(n^2) ...

  8. DotNET知识点总结五(笔记整合)

    1.委托:通常指的是 多播委托 通常的说,委托就是一个存放方法指针的容器,是一个安全的函数指针,供程序员安全调用.委托的本质就是一个类,继承于MulticastDelegate——>Delega ...

  9. JavsScript中的Document对象

    Document对象的属性 alinkColor,linkColor,vlinkColor:这些属性描述了超链接的颜色.linkColor指未访问过的链接的正常颜色,vlinkColor指访问过的链接 ...

  10. I2C的读写操作实验

    [实验任务]   利用24C08断电以后存储的数据不消失的特点,可以做一个断电保护装置.首先利用单片机做一个0-99秒的自动计时器.然后随机关断电源,在 通电以后计时器接着断电前的状态继续计时. [实 ...