开发demo:
利用visual Studio .NET开发自定义的WinForm控件,恐怕大家都已经很熟悉了。正常情况下,这种控件只能用在基于。NET的Windows application程序开发上,而无法直接嵌入到IE,这不免让人感觉遗憾。
毕竟。NET控件已经不同于在Win32下用Delphi或C++Builder开发的自定义控件。基于。NET开发的任何受控装配件(Assembly)都是IL代码,可以在运行期被。NET FrameWork进行反射(Reflection)、安全检查。应该有办法让这种特殊控件被IE使用。
经过几日的研究,终于找到一个方法可以将。NET WinForm自定义控件即用于普通的Windows application开发,又可以将其当做ActiveX控件直接嵌入到IE中,同js脚本语言进行互动(客户端需要安装。NET FrameWork)。这无疑可以大大的节省客户端代码在Windows和Web应用的移植工作,并保持控件代码的最大复用。
原理为:。NET自定义控件没有普通ActiveX的GUID唯一标示,同时为了解决以往版本冲突引起的“DLL地狱”问题,。NET控件不再向注册表进行类型的注册,而是不同的版本部署在应用程序当前目录或GAC(Globle Assembly Cache)中……NET可以为。NET控件产生强名称的代理,从而提供给其他语言比如VB、VC、Delphi等调用。我们只需要在装配件文件(c#下为AssemblyInfo.cs)中就可以设置这些开关,也可以在VS.NET的项目属性中设置。这样。NET就可以为我们自动产生ClassLibrary对应的OLE对象特征。然后我们再创建ActiveX用到的事件接口、安全接口等。
第一步――创建。NET自定义控件。
下面用visual Studio.NET 2005 Professional为开发工具进行介绍。
创建新项目,选择Windows Control Library.名称为默认的:WindowsControlLibrary1.
在界面中放置如下控件:TextBox、Button。这就是界面上所有的内容。双击界面中的Button控件,书写如下代码:
private void button1_Click(object sender, EventArgs e)
{
StreamWriter bplStreamWriter = new StreamWriter(
System.Environment.GetEnvironmentVariable("SystemDrive"+
http://www.cnblogs.com/bobzhangfw/admin/file://a.txt,true/);
bplStreamWriter.WriteLine(DateTime.Now.ToUniversalTime() +
"\tInserted Text by .Net WinFormControl.");
bplStreamWriter.Close();
}
该部分代码表示在系统盘根目录下创建一个文件,并添加一行文本。为了能调用StreamWriter类,我们要添加System.IO名称空间。
同时添加自定义控件的属性:ButtonCaption。
[Category("自定义控件的属性")]
[Description("设置按钮的文本内容")]
public string ButtonCaption
{
get
{
return button1.Text;
}
set
{
button1.Text = value;
}
}
添加委托(delegate)并创建按键点击事件:
// 声明按键点击事件类型
public delegate void ButtonClickHandler(string SendMsg);
public partial class UserControl1 : UserControl
{
// 声明按键点击事件
public event ButtonClickHandler OnButtonClick;
在刚才的按钮点击函数中增加点燃事件的代码:
private void button1_Click(object sender, EventArgs e)
{
……
if (OnButtonClick != null)
OnButtonClick(textBox1.Text);
}
增加公共方法SetText:
public void SetText(string text)
{
textBox1.Text = text;
}
此时点击运行,可以利用VS.NET 2005自带的控件容器运行我们的控件。通过属性编辑器可以验证ButtonCaption属性。点击Button可以发现在系统盘创建文件,并添加了文本内容。您也可以新创建一个Windows application项目,将这个控件加入后,测试其属性、事件和方法,这里就不介绍了。
上一篇我介绍了如何创建普通的.NET控件。为了能让.NET控件嵌入到IE中,这次我们来为控件添加COM特性。
打开项目属性,在Application页面中点击“Assembly Information…”按钮,打开Make assembly COM-Visible选项。
进入项目属性的Build页面,打开Register for COM interop选项。
保存配置后进行Build,此时的.NET控件已经具备普通OLE对象的特性。打开VS.NET自带的OLE/COM对象查看器(默认目录为C:\Program Files\Microsoft visual Studio8\Common7\Tools\Bin\OleView.Exe)。我们可以在.NET Category下看到我们创建的控件WindowsControlLibrary1.UserControl1。所有的信息已经被自动注册,包括控件的强路径。
其实,此时的.NET控件已经可以放入IE了,只是还不能算是合格的ActiveX控件,但仍然是合格的.NET控件。
我们编写一个htm文件来测试该控件。htm文件内容如下:
文本框:
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</HEAD>
<body bgcolor="#D8EEFB">
<OBJECT id="UserControl1"
classid="WindowsControlLibrary1.dll#WindowsControlLibrary1.UserControl1"
VIEWASTEXT></OBJECT>
<br><br><br><br>
<input type='button' value=' 调用方法' onclick='UserControl1.SetText("Hello
World!");'>
<br><br><br><br>
<input type='button' value=' 设置属性' onclick='UserControl1.ButtonCaption =
"Set";'>
</body>
</HTML>
将htm文件和WindowsControlLibrary1.dll部署在IIS的虚拟目录中,用浏览器浏览,就可以看到如下效果。
虽然.NET控件可以被显示在IE中,但是.NET FrameWork能动态检查代码的安全性,因为没有安全授权,不安全的代码将无法执行。比如点击按钮,我们可以得到如下提示。
有意思的是,只要该控件在装载时没有不安全代码,IE就不会有任何的提示,不同于IE对加载ActiveX时的安全警告。这样说明IE对.NET的代码安全性是交给FrameWork处理的。因此此类控件也有一种用处,就是不必数字签名,就可以加载显示在用户的IE浏览器中,并顺利执行安全的受控代码。当然前提是用户需要安装.NET FrameWork。
此时我们还是没有得到一个真正的ActiveX控件,虽然它可以通过IIS发布在Web页面中,也可以执行安全的代码。但存在如下局限性:无法发布到其他Web服务器上、无法访问本地资源、js脚本无法响应控件的事件等。
下一篇我们将讨论.NET自定义控件如何实现所有的COM对象接口,包括属性、方法、事件。并利用js脚本在Web页面中形成互动。
这次我们将讨论.NET自定义控件如何实现所有的COM对象接口,包括属性、方法、事件。并利用js脚本在Web页面中形成互动。
按照上一篇我们介绍的将.NET控件添加COM特性后,VS.NET已经自动注册了该控件的属性、方法和事件,只是还需要我们实现事件接口。
为了实现事件接口,我们需要添加如下代码:
[ComVisible(true)]
[Guid("43BCDB16-0281-487b-BB53-997546122442")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface UserControl1Events
{
[DispId(0)]
void OnButtonClick(string SendMsg);
}
为了保证Guid的有效性,我们可以使用VS.NET的Create GUID工具创建。接口中的方法就是我们要触发的事件函数。该函数的声明要参照我们控件中的事件,名称必须保持一致。
public event ButtonClickHandler OnButtonClick;
下一步我们就是要为我们的控件类创建Guid,并实现上面声明的事件接口。这里我们不要试图去实现事件接口,而是要用属性的方式去引用该COM接口。
[Guid("FBA0FCC2-D75F-40a5-9C8C-1C9D03813731")]
[ComSourceInterfaces(typeof(UserControl1Events))]
public partial class UserControl1 : UserControl
{
Build后我们已经得到一个具有公共属性、方法和事件的COM控件了。
我们创建html文件来测试新的ActiveX控件,代码如下:
<html>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</HEAD>
<script language='javascript' for='usercontrol1' event='OnButtonClick(Message)'>
alert("响应ActiveX触发的事件成功。"+Message);
window.defaultStatus = "OnButtonClick()";
</script>
<body>
<object id="usercontrol1" classid="clsid:FBA0FCC2-D75F-40a5-9C8C-1C9D03813731">
</object>
<br><br><br><br>
<input type='button' value=' 调用ActiveX方法' onclick='usercontrol1.SetText("Hello World!");'>
<br><br><br><br>
<input type='button' value=' 设置ActiveX属性' onclick='usercontrol1.ButtonCaption = "Set";'>
</html>
这次我们不再需要IIS,可以直接运行该html文件。可以得到下面的结果:
我们的.NET控件已经可以象普通的ActiveX控件使用。同时,该.NET控件也可以继续被.NET 的Windows application使用。(全篇完)
安全性设置:
除了配置虚拟目录外,将虚拟目录的执行权限设置为Scripts也是非常重要的。如果将其执行权限设置为Scripts & Executables,控件就不会被正确地激活.如果控件在企业内部网上,它公正确地执行,但如果要运行来自互联网网站上的控件,就需要对IE进行配置或修改安全策略,使它能够运行。通过将托管网页看成是受信任区段的一部分,就能够做到这一点。为了将你的站点设置为受信任区段的一部分,我们可以在IE中通过选择“工具”->“选项”->“安全”->“受信任的站点”,然后将你的站点添加到列表中,再点击“OK”按钮。这样下次再浏览到该网页时,控件就会得到正确的执行,因为已经设置了互联网许可。
一、如果在web页面中嵌入复杂winform控件客户端在通过IE访问的时候,必须把该站点添加到受信任站点中而且还需要更改受信任站点区域安全性。这样既降低了系统的安全性,又对客户访问带来不便(第一访问之前都需要做如此复杂的操作),但是这样在做系统(CS/BS)时会节省大量的代码和工作量。为了解决这个弊端本人经过潜心研究,终于发现只要为站点创建代码组就可以了。具体操作如下:
1、打开Microsoft .NET Framework 2.0 Configuration控制台程序(如果有多个fx版本只更改最新的即可)
2、运行库安全策略
3、计算机
4、代码组
5、右键All_code-新建-数据新建代码组名称-下一步--代码组权限类型选择“URL”--下面的URL中输入URL地址,例如:http://192.168.0.1/*
6、使用现有权限集选择”FullTrust"---确定。
7、重新启动计算机。
二、这样操作也挺麻烦的,所以阿又经过对“%Systemroot%\Microsoft.NET\Framework”目录中文件的研究发现有一个命令可以来做这些活:CasPol.exe
例如:
三、如果写一个程序来做不是更简单了。
下面是代码:
public override void Install(IDictionary stateSaver) { //MessageBox.Show( "OK" ); base.Install(stateSaver); StringDictionary parameters = Context.Parameters; string hostnamestr = parameters["hostname"]; if (hostnamestr == null || hostnamestr == "") { this.Rollback(null); } else { //MessageBox.Show( str.ToString() ); string[] fxs = this.UpToTheMinuteFX(); if (fxs != null && fxs.Length != 0) { foreach (string fxpath in fxs) { string fxpathtemp = fxpath + "\\caspol.exe"; if (File.Exists(fxpathtemp)) { //MessageBox.Show( fxpathtemp.ToString() ); this.startexe(fxpathtemp.Trim(), hostnamestr.Trim() + "/*"); } } } } } /// <summary> /// 添加代码组权限。 /// </summary> /// <param name="exepath">Caspol.exe程序的路径</param> /// <param name="url">代码组的url地址</param> private void startexe(string exepath, string url) { //声明一个程序信息类 System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo(); //设置外部程序名 Info.FileName = exepath; //设置外部程序的启动参数(命令行参数)为test.txt Info.Arguments = "-quiet -machine -addgroup All_Code -url " + url + " FullTrust -n OGTLogDBMS -d 测井数据库系统程序访问授权控制"; //设置外部程序工作目录为 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 //Info.WorkingDirectory = @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727"; //声明一个程序类 System.Diagnostics.Process Proc; try { // //启动外部程序 // Proc = System.Diagnostics.Process.Start(Info); } catch (System.ComponentModel.Win32Exception ee) { MessageBox.Show("系统找不到指定的程序文件。", ee.ToString()); return; } //如果这个外部程序没有结束运行则对其强行终止 if (Proc.HasExited == false) { //Console.WriteLine("由主程序强行终止外部程序的运行!"); //Proc.Kill(); } else { //Console.WriteLine("由外部程序正常退出!"); } } /// <summary> /// 获取最各个版本的Framework目录。 /// </summary> /// <returns></returns> private string[] UpToTheMinuteFX() { //调用GetWindowsDirectory取得Windows路径 const int nchars = 128; StringBuilder Buff = new StringBuilder(nchars); GetWindowsDirectory(Buff, nchars); string fxstr = Buff.ToString(); fxstr = fxstr + @"\Microsoft.NET\Framework"; string[] fxstrs = Directory.GetDirectories(fxstr); if (fxstrs != null && fxstrs.Length != 0) return fxstrs; return null; }
缓存设置:
如果上述工作做完后,还未正常工作,可先清除缓存或重新启动。