win7下VS2010 C# ActiveX控件制作(附原码)
原码以及makeCAB包下载地址:
https://files.cnblogs.com/zouyang/HelloDaniel.rar
一、环境:
操作系统:Win7
开发工具:Visual Studio 2010
.NET版本:4.0
开发语言:C#
下载makeCAB压缩包,这里面包含了制作数字签名和制作cab的几个exe程序
保存路径:D:\makeCAB
需要编辑的系统环境变量设置:(下面的系统变量)
这样做是为了方便后面在cmd里调用。
二、编写ActiveX控件
1、创建一个新的Windows窗体控件库
2、重命名UserControl1.cs 为HelloDanielActiveX.cs
3、拖1个GroupBox和2个Label控件到界面上
4、为控件添加一个方法,用来设置Label中显示当前时间
#region Javascript调用ActiveX的方法
// 设置当前时间
public void SetTime(string timeStr)
{
this.label2.Text = timeStr;
}
#endregion
5、新建一个接口
下面是这个接口的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace HelloDaniel
{
[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);
}
}
6、在控件中实现这个接口
关于实现这个接口,篇幅较长,可以在这篇文章找到:http://www.pinvoke.net/default.aspx/Interfaces/IObjectSafety.html
7、为控件添加Guid、ProgId和ComVisible属性
Guid的获得应该是这样的:
点击复制,然后将你得到的Guid号Copy下,粘贴到下面代码里面。
然后,右键单击项目HelloDaniel——>属性,在应用程序中选择程序集信息
把使程序集COM可见的对勾打上。
同时,在属性——>【生成】项中选择为COM互操作注册
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using mshtml; using System.Reflection; namespace HelloDaniel {// {6DCD51C2-33FD-4556-842F-419B6AA3C252} [Guid("6DCD51C2-33FD-4556-842F-419B6AA3C252"), ProgId("HelloDaniel.HelloDanielActiveX"), ComVisible(true)] public partial class HelloDanielActiveX : UserControl, 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 HelloDanielActiveX() { InitializeComponent(); } 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; } #region Javascript调用ActiveX的方法 /// <summary> /// 设置当前时间 /// </summary> /// <param name="timeStr"></param> public void SetTime(string timeStr) { this.label2.Text = timeStr; } #endregion #region ActiveX调用Javascript的函数 方法一 /// <summary> /// HTMLWindow2Class对象 /// </summary> private mshtml.IHTMLWindow2 html = null; /// <summary> /// 调用Javascript /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { html.execScript("CallByActiveX('" + this.label2.Text + "');", "javascript"); //CallJavaScript(this.label2.Text); } /// <summary> /// 将window对象传递进来 /// </summary> /// <param name="obj">The obj.</param> public void SetHtml(object obj) { html = obj as mshtml.IHTMLWindow2; } #endregion #region ActiveX调用Javascript的函数 方法二 /// <summary> /// 调用Javascript /// </summary> /// <param name="Filenames">The filenames.</param> private void CallJavaScript(string param) { //反射获取当前的控件的ClientSite Type typeIOleObject = this.GetType().GetInterface("IOleObject", true); object oleClientSite = typeIOleObject.InvokeMember("GetClientSite", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, this, null); //获取Container IOleClientSite oleClientSite2 = oleClientSite as IOleClientSite; IOleContainer pObj; oleClientSite2.GetContainer(out pObj); //参数数组 object[] args = new object[1]; args[0] = param; //获取页面的Script集合 IHTMLDocument pDoc2 = (IHTMLDocument)pObj; object script = pDoc2.Script; try { //调用JavaScript方法OnScaned并传递参数,因为此方法可能并没有在页面中实现,所以要进行异常处理 script.GetType().InvokeMember("ShowNowTime", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, script, args); } catch { } } #endregion } }
代码里添加了两个在控件中实现javascript的方法。
8、为了实现在控件中javascript的调用,需要添加引入
右键单击项目HelloDaniel——>【添加引用】
9、生成项目,进入到你的项目文件夹bin\debug目录下可以看到
二、制作ActiveX Setup安装文件
1、右键解决方案——>添加——>新建项目
在其他项目类型——>安装和部署——>安装项目
2、在新建的项目上点右键,【添加】——>【项目输出】,如图操作。
3、生成项目
会生成两个安装程序。
剩下的工作就是打包签名部署到网页上测试了
三、打包生成cab文件
1、准备打包文件,建议新建个文件夹用来做cab包
其中install.inf配置文件如下: [version] signature="$CHICAGO$" AdvancedINF=2.0 [Setup Hooks] HOOKRUN1=HOOKRUN1 [Deployment] InstallScope=machine [HOOKRUN1] run=%EXTRACT_DIR%\run.bat run.bat内容如下: set CURDIR=%cd% msiexec.exe /i "%CURDIR%\HelloDanielSetup.msi" /qn
2、打包cab
【开始】——【运行】——输入CMD
进入你新建的文件夹目录下(ActiveX),输入命令:
CABARC -s 6144 n HelloDaniel.cab HelloDanielSetup.msi install.inf run.bat
3、给cab数字签名
这里我需要求助一下,我在网上发现大家都是自己制作测试证书,但是我自己制作的证书签名后验证确实签名失败的。后来找到一个免费的数字签名工具(DSignTool)v1.7专业版,当然是做测试证书的。实验成功,大家可以下来试试,确实要方便的多,毕竟coder需要不断的测试嘛,下面是下载网址:
http://www.arpun.com/soft/9388.html
4、编辑网页测试
网页代码如下:
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "<A href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="<A href="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <script type="text/javascript"> function SetTime() { var d = new Date(); var vYear = d.getFullYear(); var vMon = d.getMonth() + 1; var vDay = d.getDate(); var vHour = d.getHours(); var vMin = d.getMinutes(); var vSec = d.getSeconds(); document.HelloDaniel.SetTime(vYear + "-" + vMon + "-" + vDay + " " + vHour + ":" + vMin + ":" + vSec) } </script> </head> <body style="font-size: 12px; color: black"> <form id="form1" runat="server"> <div> <object classid="clsid: 6DCD51C2-33FD-4556-842F-419B6AA3C252" codebase="HelloDaniel.cab" width="442" height="87" id="HelloDaniel" name="HelloDaniel"> </object> <br /> <br /> <input type="button" value="显示当前时间" onclick="SetTime()" /> </div> </form> </body> </html>
5、然后就是发布测试了
将签名后的cab和编辑好的测试网页放到服务器上,这里我没有使用VS2010提供的WEB制作方法,个人认为比较麻烦。我想大家基本上都有装着XAMPP Control Panel这个东西吧,就是一个一个整合性的Apache套件,做网页测试非常方便。
把制作好的HelloDaniel.cab和网页拷贝到工程目录下:
本地上测试是没太大价值的,这时候可以在内网中测试:
看看如何,如意需要把IE选项当中的安全级别调低一些,特别是那些禁止ActiveX的选项需要启用或者提示,看看效果吧。
测试后发现在Win7下没问题,能自动下载安装,控件有效。
XP系统下需要下载安装.Net,之后一样可以安装工作。
OK,到这里就结束吧。
最后非常感谢网友:波斯马(BOSIMA 或 BOSSMA)
参考了他的博客:
http://blog.bossma.cn/dotnet/csharp-activex-cab/
有什么问题大家也可以提出来互相交流学习,互联网的精神就是分享开放的精神。
共勉!