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

2020-12-13 14:13

阅读:401

YPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">

标签:winform   style   blog   http   io   color   os   ar   使用   

一、前言

      最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信、调用,单单依靠HTML是无法实现了,因此必须借用Activex来实现。由于本人主要擅长C#,自然本文给出了用C#实现的范例,本文的预期效果是有一定Winform基础的人可都轻松读懂本文。

文章主要介绍了以下几个部分:

1、用C#制作Activex控件,并发布为msi安装文件

2、将msi打包为cab,达到浏览器自动安装的效果

3、给cab数字签名(可选)

4、将Activex应用到网页上

二、用C#制作Activex控件,并发布为msi安装文件

1)新建window用户控件项目EasyActivex。其实VS2010并没有提供专门的Activex项目模板,所谓的Activex,只要符合com标准即可。

soscw.com,搜素材

2)在EasyActivex项目添加IObjectSafety接口

soscw.com,搜素材

在IObjectSafety接口代码如下,值得注意的是Guid不能随便改,必须为一下代码给出的Guid:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace EasyActivex
{

    [ComImport, GuidAttribute("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
    [InterfaceTypeAttribute(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);
    }
}

3)在EasyActivex项目添加EUserControl控件,在控件中实现IObjectSafety接口。

soscw.com,搜素材

在控件上面添加按钮,命名为btnOpenNote

soscw.com,搜素材

控件的后台代码必须实现IObjectSafety接口

soscw.com,搜素材 View Code

小提示:EUserControl代码的Guid可以用VS附带的Guid生成工具生成:

soscw.com,搜素材

4)在EasyActivex项目AssemblyInfo.cs文件中添加代码

soscw.com,搜素材

//用户添加
[assembly: AllowPartiallyTrustedCallers()]

5)设置EasyActivex项目项目属性为com互操作

soscw.com,搜素材

6)新建windows程序安装项目EasySetup

soscw.com,搜素材

7)将EasyActivex项目生产的dll添加到EasySetup项目中。下图的EasyActivex.dll为已经添加进去了的文件。

soscw.com,搜素材

8)在EasySetup项目中,设置EasyActivex.dll文件属性为vsdraCOM。

soscw.com,搜素材

完成以上步骤,生成下即可得到msi安装文件

三、将msi安装文件打包为cab,达到在浏览器中自动安装的效果

如果只是生成了msi文件,用户安装的时候比较麻烦,像安装一般软件一样,需要用户慢慢点击下一步,慢慢安装,在本项目中采用打包成cab文件的方式,做到用户点击运行后,即可自动安装。

在这里需要准备文件有:

    cabarc.exe:微软提供的cab打包工具

    EasySetup.msi:  本案例中EasySetup项目生成的windows部署安装文件

    install.inf : 需要跟EasySetup.msi打包在一起的文件,制作方法请见下文

    build.bat:  打包的批处理命令,制作方法请见下文

1)  install.inf制作。新建txt文件,加入以下内容,将文件名重新命名为install.inf即可。其中EasyZSetup.msi即是要打包的安装程序的名称。

[version]  
signature="$CHICAGO$"  
AdvancedINF=2.0  

[Setup Hooks]  
hook1=hook1  

[hook1]  
run=msiexec.exe /i "%EXTRACT_DIR%\EasySetup.msi" /qn
2)   build.bat制作。新建txt文件,加入以下内容,将文件名重新命名build.bat即可。其中EasyActivex.cab是生成目标cab的名称;install.inf是第一步生成的文件名,而EasySetup.msi是需要打包的安装程序名;第二条ping命令仅仅是让批处理不要那么快退出,起到更利于观察生成结果的作用。
"cabarc.exe" -s 6144 n EasyActivex.cab install.inf EasySetup.msi
ping -n 20 127.0.0.1 >nul

把以上四个文件复制到同一个文件夹中,双击build.bat批处理命令即可生成cab文件

soscw.com,搜素材

双击bat后的运行结果如下,其中EasyActivex.cab即是生成的目标cab文件。

soscw.com,搜素材

三、给cab数字签名(可选)

     由于处于安全问题考虑,IE浏览器设置默认是禁用未签名的Activex控件的,不过想想也知道,假如打开个未知网页,“网页”就能随便调用计算机本地的东西是多么恐怖的事情,因此,浏览器运行的Activex必须是签名了的,也符合常理。

     如果不怕用户麻烦,不采用cab签名的方式的话,也可以通过设置浏览器安全性来运行Activex。设置方法:打开浏览器--浏览器Internet选项--安全选项卡--自定义级别按钮-下载未签名的Activex控件设置为提示,保存即可。等安装完毕后,可以将“下载未签名的Activex控件”设置回禁用。

   以下为给cab签名的方法,具体方法,数字认证网上面已经介绍得很详细:

   1)申请、安装证书。上中国数字认证网(http://www.ca365.com/)申请一个免费数字证书(试用期为1年,如果企业用的话需要购买)。

        操作方法:http://www.ca365.com/forward.do?pageurl=/ca/yhsc/4.jsp ,值得注意的是证书用途必须选择代码签名证书。

        申请成功后的证书,由于是不带密钥的,因此下载完毕后只能够在申请证书的机器上安装、使用(签名文件),如果需要在其他机器上使用的话需要将密钥导出,操作方法为: http://www.ca365.com/forward.do?pageurl=/ca/yhsc/5.jsp 。

  2)用证书给cab包签名:http://www.ca365.com/forward.do?pageurl=/ca/thsc/7.jsp

四、在解决方案中添加EasyWeb项目

    终于到了最后一步,发布鸟。在解决方案中添加EasyWeb项目

soscw.com,搜素材

在网页目录中新建Activex文件夹,并将EasyActivex.cab文件拷贝进去

soscw.com,搜素材

在网页中添加以下代码,即可调用Activex控件了。值得注意的是codebase是cab包的相对路径;clsid是EUserControl控件的Guid。

object id="csharpActiveX" codebase="Activex/EasyActivex.cab" classid="clsid:685F0A47-944D-4145-BF4E-76A02A422B02">object>
五、附件下载
附件:http://files.cnblogs.com/still-windows7/EasyActivex%E4%BE%8B%E5%AD%90%E4%BB%A3%E7%A0%81%26cab%E6%89%93%E5%8C%85%E5%B7%A5%E5%85%B7%26%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E5%B7%A5%E5%85%B7.zip

六、参考资料:

1、使用C#开发ActiveX控件  http://www.cnblogs.com/yilin/archive/2009/09/15/1567332.html

2、Activex签名方法和工具技巧  http://www.360doc.com/content/10/0901/15/203871_50402416.shtml

3、中国数字认证网用户手册 http://www.ca365.com/forward.do?pageurl=/ca/yhsc.jsp

前言

ActiveX控件以前也叫做OLE控件,它是微软IE支持的一种软件组件或对象,可以将其插入到Web页面中,实现在浏览器端执行动态程序功能,以增强浏览器端的动态处理能力。通常ActiveX控件都是用C++或VB语言开发,本文介绍另一种方式,在.NET Framework平台上,使用C#语言开发ActiveX控件。

虽然本文通篇都在讲如何使用C#语言开发ActiveX控件,但我并不极力推荐使用这种技术,因为该技术存在明显的局限,即需要浏览器端安装.NET Framework(版本取决于开发ActiveX控件使用的.NET Framework版本),该局限对于挑剔的互联网用户,几乎是不可接受的。所以,我建议以下几条均满足时,方可考虑使用该技术:

  • 开发团队中没有人掌握使用C++/VB开发ActiveX控件技术;
  • 该ActiveX控件不用于互联网;
  • 用户对仅能使用IE浏览器访问表示可以接受;
  • 用户对在浏览器端安装.NET Framework组件表示可以接受。

另外,我建议如果不是因为控件的依赖库基于更高版本的.NET Framework,或需要更高版本的.NET Framework提供的扩展功能(如需要WCF等),尽量在.NET Framework 2.0上开发ActiveX控件,因为.NET Framework 2.0只有20M,相比300M的.NET Framework 3.5和40M的.NET Framework 4.0都要小很多,对客户端操作系统的要求也要低很多,并且随着Windows版本的不断升级换代,Windows Vista以后的版本已经内置了.NET Framework 2.0。等到Windows XP系统寿终正寝之时,也将迎来该技术的春天。所以,别被我上面的建议夯退了,掌握该技术其实还是蛮有实用价值的,毕竟,C#高效的开发效率很有吸引力。

本文接下来将使用C#语言开发一个ActiveX控件,实现对浏览器端的MAC地址遍历功能;另外,提供一个在Web静态页面中调用该控件的测试实例。本实例的开发环境为Visual Studio 2010旗舰版(SP1),目标框架为.NET Framework 2.0;浏览器端测试环境为Windows 7旗舰版,IE8。

控件开发

使用C#进行ActiveX控件开发过程其实很简单。首先,在解决方案中添加一个类库项目,目标框架使用.NET Framework 2.0,如图1所示:

soscw.com,搜素材

1创建ActiveX控件类库

此处有一个关键操作,需要设置类库项目属性->程序集信息->使程序集COM可见,如图2所示:

soscw.com,搜素材

2设置ActiveX控件类库程序集COM可见

ActiveX类库的内容大致包括两部分,IObjectSafety接口和实现该接口的控件类。考虑所有控件类都要实现IObjectSafety接口,可以将该接口的实现抽象为一个控件基类。

一、IObjectSafety接口

为了让ActiveX控件获得客户端的信任,控件类还需要实现一个名为“IObjectSafety”的接口。先创建该接口(注意,不能修改该接口的GUID值),接口内容如下:

soscw.com,搜素材

 1 [ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
 2 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 3 public interface IObjectSafety
 4 {
 5     [PreserveSig]
 6     int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);
 7 
 8     [PreserveSig()]
 9     int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
10 }

soscw.com,搜素材

二、ActiveXControl控件基类

soscw.com,搜素材

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

soscw.com,搜素材

三、MacActiveX控件类

soscw.com,搜素材

 1 [Guid("65D8E97F-D3E2-462A-B389-241D7C38C518")]
 2 public class MacActiveX : ActiveXControl
 3 {
 4     public string GetMacAddress()
 5     {
 6         var mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
 7         var mos = mc.GetInstances();
 8         var sb = new StringBuilder();
 9 
10         foreach (ManagementObject mo in mos)
11         {
12             var macAddress = mo["MacAddress"];
13 
14             if (macAddress != null)
15                 sb.AppendLine(macAddress.ToString());
16         }
17 
18         return sb.ToString();
19     }
20 }

soscw.com,搜素材

注意,第一行指定的Guid值即为该ActiveX控件的唯一标识,请保证其唯一性。Guid的生成有多种方法,你可以在系统目录的Program Files目录搜索一个名为guidgen.exe的工具,用该工具产生;也可以写一段测试代码,调用Guid.NewGuid()方法产生;有的Visual Studio版本也提供了快捷方式,在“工具->生成GUID”菜单下。另外,访问MAC需要添加对System.Management系统组件的引用。

到此,控件类库的开发工作就做完了,整个实现过程确实很简单。

发布

C#开发的ActiveX控件类库不像OCX那样可以直接通过regsvr32.exe注册(实际上,微软提供了替工具regasm.exe,但由于这种方式要不能实现自动升级,所以本文就不介绍了),要使控件类库运行于浏览器端,可以采取两种方式,一种是将控件类库打包为MSI安装包,然后直接在浏览器端安装;另一种是将MSI再封装为一个CAB包,这个CAB包就是一个ActiveX控件了,可以将它随应用程序一并发布,浏览器端访问包含有该控件的页面时,就会自动提示安装了。接下来就后一种发布方式进行详细讲解。

一、安装项目

在解决方案中添加一个安装项目,如图3所示:

soscw.com,搜素材

3添加安装项目

右键点击新添加的安装项目,依次选择“添加->项目输出”菜单,打开添加项目输出组对话框,并选择ActiveX控件类库“CSharpActiveX”作为主输出,如图4所示:

soscw.com,搜素材

4添加项目输出

双击安装项目检测到的依赖项“Microsoft .NET Framework”,打开安装项目的启动条件界面,选中“.NET Framework”项,如图5所示:

soscw.com,搜素材

5安装项目启动条件

按F4快捷键,打开属性窗口,设置.NET Framework项的Version为“.NET Framework 2.0”,如图6所示:

soscw.com,搜素材

6设置安装项目的依赖框架

下面这步很关键,选中“主输出来自CSharpActiveX(活动)”项,如图7所示:

soscw.com,搜素材

7主输出内容项

设置主输出项内容的Register属性值为vsdrpCOM,如图8所示:

soscw.com,搜素材

8设置主输出项属性

二、制作CAB包

Visual Studio 2010提供了CAB项目模板,但非常遗憾,无论我怎么设置,其生成的CAB安装包都不能在终端成功安装,最终只能放弃,转而选择了makecab.exe工具。源码提供了该打包工具,位于CAB目录下,共包含makecab.exe、cab.ddf、installer.inf和makecab.bat四个文件,其中cab.ddf和installer.inf文件需要简单说明下。

cab.ddf文件定义了CAB文件的打包行为,内容包括打包参数,打包内容项以及输出文件等。需要指出的是,使用C#开发的ActiveX控件CAB包中需要包含MSI文件和installer.inf安装文件两部分。cab.ddf文件内容如下:

soscw.com,搜素材

.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="CSharpActiveX.CAB"
"installer.inf"
"CSharpActiveX.msi"

soscw.com,搜素材

installer.inf文件定义了CAB文件的安装行为,作为控件的一部分打入CAB包中,其内容如下:

soscw.com,搜素材

[Setup Hooks]
hook1=hook1

[hook1]
run=msiexec /i %EXTRACT_DIR%\CSharpActiveX.msi /qn

[Version]
Signature= "$CHICAGO$"
AdvancedInf=2.0

soscw.com,搜素材

makecab.bat文件是调用makecab.exe进行打包的批处理文件,内容如下:

makecab.exe   /f   "cab.ddf"

当生成安装项目后,将CSharpActiveX.msi文件拷贝到CAB目录下,就可以双击makecab.exe文件进行打包了,执行完成后会输出CSharpActiveX.CAB文件,这就是所谓的ActiveX控件了。

三、签名

IE采用了AuthentiCode代码签名技术,对浏览器端安装ActiveX控件行为进行了控制。上面生成的ActiveX控件如果想在浏览器端成功安装,需要对浏览器进行设置,具体操作参见部署章节。

让所有用户都对IE进行设置,显得不太友好,为此,我们可以考虑使用AuthentiCode技术对ActiveX控件进行签名。Visual Studio 2010附带的signtool.exe(以前版本的VS提供的是另一个工具signcode.exe)代码签名工具可以完成该工作(注意,并非一定要用微软提供的工具进行签名,只要按照AuthentiCode技术标准,使用 PKCS#7标准定义的数据结构生成待签名文件的数字签名,并加入到待签名文件的PE结构中即可)。但需要先准备一个PKCS#12(证书及私钥)文件(.pfx),注意,该证书的增强型密钥用法须包含代码签名这项,如图9所示:

soscw.com,搜素材

9代码签名证书

本文源码提供了一份测试PKCS#12文件Apollo.pfx,PIN码为11111111。在Visual Studio命令提示(2010)中,进入源码的CAB目录,输入如下命令即可对ActiveX控件进行签名操作了:

signtool sign –f Apollo.pfx –p 11111111 CSharpActiveX.CAB

图10对比了签名前后的ActiveX控件文件属性,可以看出,签名后的ActiveX控件属性中已经多了一项数字签名,表示该文件已经过签名。

soscw.com,搜素材

10签名前后的ActiveX控件属性对比

出于方便考虑,本文源码的CAB目录下提供了一份signtool.exe工具的拷贝,这样就可以将签名命令加入makecab.bat文件中,修改后的makecab.bat我将其命名为makecabsigned.bat,内容如下:

makecab.exe   /f   "cab.ddf"
signtool sign -f Apollo.pfx -p 11111111 CSharpActiveX.CAB

应用

ActiveX控件用于HTML静态页面,执行于IE浏览器端。需要以标签的形式引入页面文件,然后使用Javascript语言调用它。测试代码如下:

soscw.com,搜素材

 1 
 2
 3     CSharpActiveX测试
 4 
 5 
 6 
 7     
11 
12 

soscw.com,搜素材

注意,标签的classid属性值即为MacActiveX类的Guid特性值。

部署

ActiveX控件在IE浏览&


评论


亲,登录后才可以留言!