摘自:http://blog.csdn.net/liyun919/archive/2009/11/13/4807958.aspx
1、 需求描述:项目由网页部分及客户端大厅程序部分组成,网页上有一个进入大厅按钮,如果客户端安装了,点击按钮直接打开客户端程序,如果未安装则提示消息提醒安装客户端程序。
类似于QQ游戏大厅、联众游戏大厅的打开,QQ的强制聊天功能。
2、首先要实现如果客户端安装了,就打开客户端,网上大多给出的是通过js调用activex控件的方式实现,因为这样IE会弹出安全提示,感觉不好。
<SCRIPT language=javascript>
function Run() {
var o = new ActiveXObject("WScript.Shell");
o.exec("D:\\Program Files\\Ulead Systems\\Ulead VideoStudio 9.0\\vstudio.exe");
}
</SCRIPT>
其实我们只需要通过注册协议方式就可以成功实现。
在客户端安装时我们只需要写如下注册表信息:
[HKEY_CLASSES_ROOT\test]
@="testProtocol"
"URL Protocol"=""C:\\Program Files\\test.Setup\\test.exe""
[HKEY_CLASSES_ROOT\Ustcori\DefaultIcon]
@=""C:\\Program Files\\test.Setup\\test.exe,1""
[HKEY_CLASSES_ROOT\test\shell]
[HKEY_CLASSES_ROOT\test\shell\open]
[HKEY_CLASSES_ROOT\test\shell\open\command]
@=""C:\\Program Files\\test.Setup\\Driver.exe""
这样就注册了一个新协议test,之后直接通过在浏览器中输入test:\\\ ,就会自动从注册表中查找该协议程序的地址,打开客户端程序。
3、然后就要实现客户端未安装时,提示消息了。
这个颇费周折,网上没有找到相关的方式,大多的实现方式也是通过js调用WScript.Shell控件。
后来找到了网上有很多QQ的实现方式:
function IsInstallQQ(){
try{//支持
var xmlhttp=new ActiveXObject("TimwpDll.TimwpCheck");
return true;
}catch(e){//不支持
location.href='http://is.qq.com/webpresence/up_alarm.shtml';
//window.open('http://is.qq.com/webpresence/up_alarm.htm','','toolbar=no,location=no,directories=no,status=no,menubar=no, scrollbars=yes,resizable=no,copyhistory=no,width=410, height=410,top=0,left=0')
return false;
}
return false;
}
对 于红色那段activex的调用是怎么回事却没有说明,搜索一下发现msn,迅雷等其实是通过类似的方式实现的,制作一个自定义控件后,并对它进行安全授 权,连同客户端程序一起安装,该自定义控件会往注册表中写值,然后通过js实例化该控件,如果异常说明客户端程序未安装。
看一下QQ如何写值的:
[HKEY_CLASSES_ROOT\TimwpDll.TimwpCheck]
@="TimwpDll.TimwpCheck"
[HKEY_CLASSES_ROOT\TimwpDll.TimwpCheck\CLSID]
@="ED4CA2E5-0EEA-44C1-AD7E-74A07A7507A4"
认准的实现方向,那么下面就展开对于自定义activex控件的调查。
4、利用VS2008制作自定义activex控件
a、首先在解决方案下建一个Windows控件库项目(windows from control library),然后增加一个控件UstcOriWebLab.cs。
b、打开AssemblyInfo.cs修改程序集信息。引用System.Security命名空间,并添加 [assembly : AllowPartiallyTrustedCallers()]安全声明,修改[assembly: ComVisible(false)]为[assembly: ComVisible(true)]使程序集Com可见。
c、为Com Interop注册。右键demoActiveX项目属性,在“生成”选项卡里将“为Com Interop注册”打上勾即可。
d、选择菜单工具->创建 Guid工具生成一个新的Guid{E5FD041B-8250-4cbc-B662-A73FC7988FB5},copy下来,加在类头上
[Guid("F325140B-90E3-42d7-8F27-F1E68E1BD92E"), ProgId("UstcOriWebLabActivex.UstcOriWebLab"), ComVisible(true)]
F325140B-90E3-42d7-8F27-F1E68E1BD92E就是写入注册表的CLSID,UstcOriWebLabActivex.UstcOriWebLab为键名。
e、实现IObjectSafety接口,把ActiveX控件标记为安全的。
ActiveX危险,那么为什么QQ以及MediaPlayer等都是用ActiveX的方式创建的,却没有问题?原来,这是因为这些ActiveX组件都声明自己是脚本安全的,而IE的中级安全设置上,是允许脚本安全的ActiveX创建,并且不予警告的。
IE怎么知道一个插件是脚本安全的?它是通过以下两个办法。一是查询ActiveX组件是否实现了IObjectSafety接口,并且返回脚本 安全;二是查询ActiveX组件是否在注册表的Component Category Manager里表明自己实现了CATID_SafeForInitializing和CATID_SafeForScripting。(参考http://blog.csdn.net/optman/archive/2007/07/18/1698070.aspx)
那么我们就通过实现IObjectSafety接口
建一个接口文件IObjectSafety.cs,内容如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace UstcOriWebLabActivex
{
[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);
}
}
在UstcOriWebLab.cs中实现接口
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;
namespace UstcOriWebLabActivex
{
[Guid("F325140B-90E3-42d7-8F27-F1E68E1BD92E"), ProgId("UstcOriWebLabActivex.UstcOriWebLab"), ComVisible(true)]
public partial class UstcOriWebLab : UserControl, IObjectSafety
{
public UstcOriWebLab()
{
InitializeComponent();
}
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;
}
}
}
f、开始制作安装包
新建一个安装项目,在项目上点右键,【添加】->【项目输出】,添加刚才的项目,生成项目,这样我们的activex控件就会和客户端程序一起安装到用户的机器上了。会生成两个文件,一个exe文件和一个msi文件。
安装后,就是自动写入注册表键UstcOriWebLabActivex.UstcOriWebLab。
客户端通过js检查:
function IsInstall(){
try{//支持
var xmlhttp=new ActiveXObject("UstcOriWebLabActivex.UstcOriWebLab");
window.navigate('Ustcori:///'); //打开客户端
}catch(e){//不支持
alert("您未安装大厅程序!");
return false;
}
return false;
}
OK搞定!!!!!!
总结一下思路:
通过写注册表自定义协议,然后通过协议地址打开客户端;
自定义activex控件,并对控件进行安全授权,然后将该控件和客户端程序一起安装,写入注册表;
最后通过js实例化自定义activex控件来检查控件是否安装,从而判断客户端程序是否安装。
通过自定义URL协议在Web中启动本地应用程序
1、注册应用程序来处理自定义协议
你必须添加一个新的key以及相关的value到HKEY_CLASSES_ROOT中,来使应用程序可以处理特殊的URL协议。
新注册的key必须与协议scheme相匹配才可以被添加。例如,增加一个“alert:”协议,被增加到HKEY_CLASSES_ROOT的key必须是alert。在这个新的key之下,默认的字符串value将显示新协议的名字,并且URL协议字符串value将包含协议特有的信息或者空字符串。Keys将同样被添加到DefaultIcon和shell中。
默认的DefaultIcon key的字符串value必须是新URL协议图标文件名的路径。
在shell key之下,一个key使用一个动词(就像open)将被添加。一个command(命令) key和一个DDEEXEC(动态数据交换执行) key都是使用动词来添加的。这command和DDEEXEC keys之后的values都是用来调用(或者启动)处理新协议的应用程序。
2、启动处理程序
当一个用户点击一个注册了你的自定义URL协议的链接后,Windows Internet Explorer(IE)启动注册的URL协议的处理器。如果指定shellopen命令在注册表中包含一个%1参数的话,Internet Explorer传递这个URI给注册协议的处理器。这最后的统一资源标识符(URI)被编码(%1);即16进制换码符被转换为等价的UTF-16字符。例如,用%20字符串取代空格。
安全警示:应用程序处理URL协议必须全力面对恶意数据。因为处理程序接收来自不信任源的数据,URL和其它参数值传递给应用程序可能包含的恶意数据企图使用处理程序。因此,处理程序可以首先启动基于外部数据的空闲行为确认这些行为以及它们的用户。
3、示例
接下来的例子演示如何注册alert.exe应用程序,来处理alert协议。
首先我们可以新建一个txt文件,写入内容如下:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\alert]
@="URL:alert Protocol"
"URL Protocol"="C:\\Program Files\\Alert\\alert.exe"
[HKEY_CLASSES_ROOT\\alert\\DefaultIcon]
@="C:\Program Files\Alert\alert.exe"
[HKEY_CLASSES_ROOT\\alert\\shell]
@="open"
[HKEY_CLASSES_ROOT\\alert\\shell\\open]
@="open"
[HKEY_CLASSES_ROOT\\alert\shell\\open\\command]
@="\"C:\\Program Files\\Alert\\alert.exe\" \"%1\""
注意:a.路径使用双杠"\\";b.如果字符串中有双引号("),那么需要加转义字符"\"
然后将文件名改为Alert.reg,双击本文件执行,将这些项写入到注册表。
增加这些设置信息到注册表,尝试导航到像“alert:Hello%20World”这样的URLs中,将会尝试启动alert.exe程序并且在命令行中传递“Hello World”。
4、协议处理实例
下面的代码包含了一个简单的C#控制台应用程序演示了一种实现alert协议处理程序的方式:
复制内容到剪贴板 程序代码
using System;
using System.Collections.Generic;
using System.Text;
namespace Alert1
{
class Program
{
static string ProcessInput(string s)
{
// TODO Verify and validate the input
// string as appropriate for your application.
return s;
}
static void Main(string[] args)
{
Console.WriteLine("Alert.exe invoked with the following parameters.\r\n");
Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine);
Console.WriteLine("\n\nArguments:\n");
foreach (string s in args)
{
Console.WriteLine("\t" + ProcessInput(s));
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}
5、测试
a、打开记事本,输入以下代码,并保存为test.html
<a href="alert://Hello!">点击这里启动程序</a>
b、将test.html拖入浏览器中打开,本人在Google Chrome中测试的;
c、点击页面中的按钮"点击这里启动程序";
d、你发现刚才写的程序运行了,并且TextBox中显示"alert://Hello!"。
详细还可参考:http://msdn.microsoft.com/en-us/library/aa767914.aspx