activex开发部署更新

Posted 吉春虎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了activex开发部署更新相关的知识,希望对你有一定的参考价值。

开发篇 

一、前言                                

  在设计某移动内部自动化运维平台时,经综合考虑终端机性能和功能需求等因素后,决定采用B/S模式,并且浏览器通过ActiveX组件实现与服务器Agent作P2P的通讯。好处,整个平台以网页形式存在,界面渲染性能高于桌面应用(终端机性能其低);通过ActiveX组件与各服务器Agent进行P2P通讯,不对Web服务器造成压力。风险,当用ActiveX传输上百兆的文件时,会对浏览器造成哪些影响;团队中没有类似解决方案的经验供借鉴。解决方法:前期对主要功能进行快速原型设计、开发、验证和总结。

  本系列将记录从开发、部署、更新、卸载到ActiveX与JS间的交互的.Net开发ActiveX全过程。由于之前学习如何使用.Net开发ActiveX时,查找了不少文档,经过两天的东拼西凑后才掌握了整个开发过程,现在整理成系列以供日后查阅。

  下面我们一起按部就班写ActiveX吧!

 

二、写代码咯!                              

  开发环境:Win7、VS2010

  1. 创建类

  

  2. 设置工程属性

  2.1.  在应用程序页中,打开 程序集信息 ,勾选 使程序集COM可见

      

  2.2.  在 生成 页中, 勾选 为COM互操作注册

      

  2.3. 在 Properties.AssemblyInfo.cs文件中 添加 `[assembly:AllowPartiallyTrustedCallers()]`(注意引入:`System.Security`命名空间)

      

 3. 添加用户控件

     ActiveX以用户控件为载体,加载到网页中

 4. 添加控件的GUID

   ActiveX的用户控件均有一个独立的GUID标识,该GUID必须与工程的GUID不同。

     4.1. 通过VS2010->工具->创建GUID

     

    4.2. 引入`System.Runtime.InteropServices`
  4.3. 将生成的GUID粘贴到用户控件类声明前

 [Guid("4D39585B-7947-4197-8BDB-B0A6918B1098")]
 public partial class ATC : UserControl
 {
    InitializeComponent();
 }

 5. 开发IObjectSafety接口

     为了让ActiveX控件获得客户端的信任,用户控件必须实现`IObjectSafety`接口,并且下面的代码是固定的(GUID也不能变)

复制代码
[ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
  [PreserveSig]
    int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);
  
    [PreserveSig()]
    int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
}
复制代码

6. 用户控件实现IObjectSafety接口

复制代码
[Guid("4D39585B-7947-4197-8BDB-B0A6918B1098")]
 public partial class ATC : UserControl, IObjectSafety
 {
    InitializeComponent();
 }

 #region IObjectSafety 成员
 
 private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
 private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
 private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
 private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
 private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";
 
 private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
 private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
 private const int S_OK = 0;
 private const int E_FAIL = unchecked((int)0x80004005);
 private const int E_NOINTERFACE = unchecked((int)0x80004002);
 
 private bool _fSafeForScripting = true;
 private bool _fSafeForInitializing = true;
 
 
 public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
 {
     int Rslt = E_FAIL;
 
    string strGUID = riid.ToString("B");
    pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
    switch (strGUID)
    {
        case _IID_IDispatch:
        case _IID_IDispatchEx:
            Rslt = S_OK;
            pdwEnabledOptions = 0;
            if (_fSafeForScripting == true)
                pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
            break;
        case _IID_IPersistStorage:
        case _IID_IPersistStream:
        case _IID_IPersistPropertyBag:
            Rslt = S_OK;
            pdwEnabledOptions = 0;
            if (_fSafeForInitializing == true)
               pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
            break;
        default:
             Rslt = E_NOINTERFACE;
            break;
    }

    return Rslt;
 }
 
 public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
 {
    int Rslt = E_FAIL;

    string strGUID = riid.ToString("B");
    switch (strGUID)
    {
        case _IID_IDispatch:
        case _IID_IDispatchEx:
            if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
                   (_fSafeForScripting == true))
                Rslt = S_OK;
            break;
        case _IID_IPersistStorage:
        case _IID_IPersistStream:
        case _IID_IPersistPropertyBag:
            if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
                    (_fSafeForInitializing == true))
                Rslt = S_OK;
            break;
        default:
            Rslt = E_NOINTERFACE;
            break;
    }
 
    return Rslt;
 }
 
 #endregion
复制代码

7. 获取ActiveX的Classid

  7.1. 打开VS2010->工具->OleView(若没有就自行添加,程序路径:C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\bin\\OleView.exe)。
  7.2. 在`Object Classes`->`Grouped by Component Category`->`.NET Category`找到刚才新建的ActiveX控件
  7.3. 右键复制html标签

  


8. 页面引用ActiveX控件

    在html页面上

<object classid="clsid:ActiveX控件的clsid" codebase="控件打包后的exe文件名或cab文件名" width="200px" height="200px">
</object>

  8.1. classid:用于指定要加载的ActiveX的clsid,clasid就是用户控件的GUID值;
  8.2. codebase:用于指定clasid的基本URL,可为绝对或相对路径,因ActiveX控件被打包到安装包中,所以codebase必须为安装包的路径。
  8.3. 实例:页面URL为www.test.com/index.html,codebase为test.cab(或test.exe),classid为clasid:xxxxxxxxxxxxxxxxx。那么ActiveX控件的绝对路径就是      www.text.com/test.cab(或test.exe)/xxxxxxxxxxxxxxx。

 

 

发布:

 1. 打包

   C#开发的Activex不像`OCX`那样直接通过`regsvr32.exe`注册,而采用两种方式发布使其运行在浏览器上。
   1. 离线安装:将控件类库打包成MSI安装包,然后在客户端安装。
    1.1. 添加安装项目

    

    1.2. 右键“添加”->“项目输出”,选择ActiveX控件为“主输出”

    

     1.3. 双击“解决方案资源管理器”的“检测到的依赖项”下的 Microsoft.NET Framework 并修改“启动条件”下 .NET Framework 的Version为.NET             Framework2.0 

    

     1.4. 修改“主输出来自XXX”的 Register 属性为 vsdrpCOM

     

 

  2. 在线安装:将MSI再封装成CAB包,并将CAB包随应用一同发布,当浏览器访问含该ActiveX控件时就会自动提示安装。
    
2.1. 定义cab.ddf     

复制代码
.OPTION   EXPLICIT
.Set Cabinet=on
.Set Compress=on
.Set MaxDiskSize=CDROM
.Set ReservePerCabinetSize=6144
.Set DiskDirectoryTemplate="."
.Set CompressionType=MSZIP
.Set CompressionLevel=7
.Set CompressionMemory=21
.Set CabinetNameTemplate="自定义CAB文件名.CAB"
"installer.inf"
"打包的msi文件名称.msi"
复制代码

     2.2. 定义installer.inf

复制代码
[Setup Hooks]
hook1=hook1

[hook1]
run=msiexec /i %EXTRACT_DIR%\\打包的msi文件名称.msi /qn

[Version]
Signature= "$CHICAGO$"
AdvancedInf=2.0
复制代码

    2.3. 定义makecab.bat

makecab.exe   /f   "cab.ddf"

    2.4. 执行makecab.bat就会生成CAB包

 

  2. 签名

  1. 制作cer证书
    使用makecert.exe工具生成cer证书(工具在`C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\bin`)

cmd命令

makecert -r -n "CN=发行者名称" -b 01/01/2012 -e 01/01/2018 -sv 证书文件名称.pvk 证书文件名称.cer

    注意:执行命令后将弹出私钥密码对话框,可以设置或不设置私密。

  2. (可选项)将cer证书转换为spc证书
    使用cert2spc.exe工具(工具在`C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\bin`)

cmd命令

cert2spc cer证书文件名.cer spc证书文件名.spc

  3. 将cer证书转换为pfx证书
    使用pvk2pfx.exe工具将cer证书转换为pfx证书(PKCS#12证书及私钥)(工具在`C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\bin`)
    cmd命令格式:

pvk2pfx /pvk pvk证书文件名.pvk [/pi pvk证书密码] /spc cer或spc证书文件名.cer或spc [/pfx pfx证书名称.pfx] [/po pfx证书的私密] [/f]

说明:
  若第一步中,没有设置私钥,那么就`/pi pvk证书密码`就不用填写;
  若`/po pfx证书的私密`没有填写,那么pfx证书就使用与pvk证书一样的私钥;
  如果存在于-pfx指定的名称相同名称的文件,则需要使用`/f`来覆盖已有的.pfx文件。

示例,cmd:

pvk2pfx /pvk fsgmcc.pvk /pi gmcc123 /spc fsgmcc.cer /pfx fsgmcc.pfx  /f

  4. 通过signtool导入证书到cab的ActiveX文件
    signtool是vs2010的AuthentiCode签名工具(使用 PKCS#7标准定义的数据结构生成待签名文件的数字签名,并加入到待签名文件的PE结构中即可)。

cmd命令

signtool sign -f pfx证书文件名.pfx [-p pfx密码] ActiveX控件文件.CAB

示例:

signtool sign -f fsgmcc.pfx DirBrowser.CAB




部署:

  ActiveX的部署其实就是客户端安装ActiveX组件,对未签名和已签名的ActiveX,分别有对应的部署方式。

  1. 部署未签名的ActiveX

  未签名的ActiveX控件不受浏览器端信任,默认是不被允许安装的
  1. 将网站加入 **可信站点**
  2. 在“可信站点”和“Internet”下的 **自定义级别** 中确认“对未标记为可安全执行脚本的ActiveX控件初始化并执行脚本”项设置为“启用”,“下载未签名的ActiveX控件”项设      置为“提示”。

 

  2. 部署已签名的ActiveX

  1. 客户端获取pfx证书
      2. 客户端安装证书

   

  点击“安装证书”按钮,将该证书安装到“受信任的根证书颁发机构”

  

  开IE的“工具->Internet选项”对话框,选择“内容”选项卡,点击“证书”按钮,打开IE证书对话框,确认在“受信任的根证书颁发机构”选项卡中包含刚才导入的代码签名证书

  

自动更新、卸载篇

  1. 设置ActiveX控件项目的程序集版本号、文件版本号

  打开项目属性->程序集信息

  

  2. 设置安装程序的版本号(msi文件的版本号)

  打开项目属性

   

  3. 设置注册表中的插件版本号

  右键点击安装项目->视图->注册表;

  

   在 HKEY_CLASSES_ROOT下创建 CLSID/{ActiveX控件的GUID}/InstalledVersion;
   在 InstalledVersion 下新建字符串值,设置 Value 为如“1,3,1,0“的版本号,删除 属性Name 中的值;

  

  4. 设置cab包的版本号
  xxx.html页面

<object classid="clsid:xxxxxxxxxx" id="ax" codebase="xxx.CAB#version=1,3,1,0"></object>


  5. 版本检测流程
  1. cab包版本号通过object标签codebase属性的version指定;每次加载ActiveX控件时,会用cab包的版本号与注册表中的版本号做对比,若cab包版本号较新则会下载并更   新新的cab包;
  2. 在安装cab包时,会对比cab包中的msi文件的版本号,若msi版本号较旧,则安装失败;较新则继续安装;
  3. 在安装msi文件时,会对比ActiveX控件的版本号,若版本号较旧,则安装失败;较新则继续安装。

  5. 更新注意

  1.如果客户端配置文件手动更改,则升级时不会更新该文件

  2.文件记得设置版本号

三、卸载                                

  在“控制面板” -> “程序和功能” 中卸载ActiveX控件



以上是关于activex开发部署更新的主要内容,如果未能解决你的问题,请参考以下文章

Studio向导更新:重新支持Typelib和ActiveX

ActiveX 属性持久性

如何在部署机器上兼容Flash ActiveX控件?

如何在 MFC 对话框中替换/更新 ActiveX 控件

在 vs2008 中更新 ActiveX 包装器

如何根据浏览器在同一页面上嵌入 32 位或 64 位 ActiveX 控件?