- 综述
现在有很多B/S与C/S相结合的产品,会出现一种需求:从浏览器启动客户端的程序,并且如果客户端未安装相应程序,先提示安装。
主流的几款产品:腾讯QQ、阿里旺旺、迅雷、PPLive等,都有实现了类似的功能。
浏览器启动客户端程序可以通过注册自定义的Url协议来实现,而检测客户端是否安装程序需要利用ActiveX控件。
要额外注意的是,现在只有IE浏览器才支持。
- 客户端程序
先准备一个简单的客户端程序,只用来显示传入的命令行参数。
- Client
1 namespace wuhong.Client 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 if (args != null && args.Length > 0) 8 { 9 Array.ForEach(args, arg => Console.WriteLine(arg)); 10 } 11 12 Console.ReadLine(); 13 } 14 } 15 }
- 从浏览器启动
注册自定义的Url协议,这样当用户点击这个URL协议的链接后,IE就会启动相关URL协议的处理器,使用注册的程序来处理这个协议。
具体来说,比如现在需要注册“wuhong.client”的Url协议,使得形如“wuhong.client:XXXXYYYY”的链接都由上节的控制台程序来处理。
这一切只需要在客户端安装控制台程序的同时向注册表添加下面的项就可以实现:
- 注册表
1 [HKEY_CLASSES_ROOT\wuhong.client] 2 3 @="wuhong.Client" 4 5 "URL Protocol"="" 6 7 [HKEY_CLASSES_ROOT\wuhong.client\DefaultIcon] 8 9 @=" wuhong.Client.exe " 10 11 [HKEY_CLASSES_ROOT\wuhong.client\Shell] 12 13 [HKEY_CLASSES_ROOT\wuhong.client\Shell\open] 14 15 [HKEY_CLASSES_ROOT\wuhong.client\Shell\open\command] 16 17 @="\"[TARGETDIR]wuhong.Client.exe\" \"%1\""
解释一下其中几项:
[HKEY_CLASSES_ROOT\wuhong.client]
默认项和URL Protocol项的值都是设置一个名称。
[HKEY_CLASSES_ROOT\wuhong.client\DefaultIcon]
默认项的值是Url协议的图标文件名的路径。简单处理可以省略这一项。
[HKEY_CLASSES_ROOT\wuhong.client\Shell\open\command]
默认项的值是用来调用(或者启动)处理这个Url协议的程序。整个Url会作为一个参数传递给处理程序。
- 检测客户端程序安装
检测客户端是否安装了上节的控制台程序,需要另外实现一个ActiveX控件,在客户端安装程序的同时一并安装。这样可以利用ActiveX控件的加载情况,来判断控制台程序是否安装。
作为示例,这里实现一个没有任何其他功能的ActiveX控件。
首先定义IobjectSafety接口。IobjectSafety接口用来向IE声明自身是脚本安全的。在IE的中级安全级别上,是允许脚本安全的ActiveX控件的创建而不提示警告。
- IobjectSafety接口
1 namespace wuhong.ActiveX 2 { 3 [ComImport, Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")] 4 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 5 public interface IObjectSafety 6 { 7 [PreserveSig] 8 int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions); 9 10 [PreserveSig()] 11 int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions); 12 } 13 }
接下来是控件的代码,实现一个“wuhong.client”的控件,本身不添加任何额外的功能。
- UserControl类
1 namespace wuhong.ActiveX 2 { 3 [Guid("9C9701D1-D188-495d-8721-9D246211A27C"), ProgId("wuhong.client"), ComVisible(true)] 4 public partial class ActiveXObject : UserControl, IObjectSafety 5 { 6 public ActiveXObject() 7 { 8 InitializeComponent(); 9 } 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 15 public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions) 16 { 17 pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER; 18 pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA; 19 20 return S_OK; 21 } 22 23 public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions) 24 { 25 return S_OK; 26 } 27 } 28 }
另外AssemblyInfo.cs中需要修改并添加以下内容:
- AssemblyInfo.cs
1 using System.Security; 2 [assembly: AllowPartiallyTrustedCallers()] 3 [assembly: ComVisible(true)]
项目生成中也需要选择“为COM互操作注册”。
- 测试
将客户端程序、ActiveX控件以及注册Url协议的注册表项制作成安装包。
使用下面的页面代码测试:
- Test页面
1 <html xmlns="http://www.w3.org/1999/xhtml" > 2 <head> 3 <script type='text/javascript'> 4 function Start() { 5 try {//支持 6 var obj = new ActiveXObject("wuhong.client"); 7 } 8 catch (e) {//不支持 9 } 10 if(null != obj){ 11 delete obj; 12 window.navigate('wuhong.client:start?HelloWorld'); 13 } 14 else{ 15 alert("您未安装程序,请安装!"); 16 } 17 } 18 </script> 19 <title></title> 20 </head> 21 <body> 22 <input type="button" onclick="Start()" value="启动"/> 23 </body> 24 </html>
可以看到安装前后点击“启动”按钮的区别。
安装前:
安装后: