Silverlight 运行原理 解析综合版(二)

几种RIA技术

Silverlight是一种RIA(Rich Internet Applicaation,富互联网应用)之一,在RIA的历史上比较流行的曾经有Java Applet和Flash及现在的Silverlight,HTML5将来会直接支持多媒体,如果它取得了成功,或许RIA会成为一个历史名词,但是“将来”到底有多远,谁也不知道。就像喊了多年的“C/C++必死”、“IE6已亡”的口号一样,实际上C/C++仍然占据着非常重要的地位,并且IE6在浏览器市场份额中也是最大(尽管存在着很多缺点),对于HTML5什么时候真正能像现在它所憧憬的那样在现实中被应用起来,我们只能拭目以待。

Java Applet:Java Applet是一种利用Sun公司(现已被Oracle公司收购)的Java语言开发的客户端组件(插件),在早期它是一种比较普遍的客户端技术,客户端需要安装Java运行环境JRE(Java Runtime Envirment)才能运行Java Applet。潜入了Java Applet的网页被打开时,其中的Applet代码被下载到本地客户端,由本地客户端的JRE运行。不过到今天已经很少有人使用Java Applet开发客户端应用了。

Flash:Flash是Adobe公司提供的一种RIA技术,在没有Java Applet的日子里Flash独领风骚了很多年,据说Flash Player最高时具有90%以上的装机率,最近有向移动领域发展的趋势,不过苹果公司拒绝了在其流行的iPhone上使用,而据目前在Android上的表现来看,它似乎也表现得不是太好。

Silverlight:Silverlight是Microsoft公司提供的一种RIA技术,使用Silverlight的好处是.NET开发人员可以利用已经学习过的.NET开发语言(比如C#或者VB.NET)来开发,上手和掌握起来比较快,而且可以利用微软公司的VS这个开发工具,微软公司的软件在易用性商确实做得没有话说。

说句题外话,在桌面客户端开发比较炫的游戏可以使用基于Java平台的JavaFX和基于.NET平台的XNA,当然如果仅仅是开发软件,也可以使用WPF,我有一个朋友就在中烟集团湖北分公司开发基于WPF技术的应用。

如果仔细分析Java Applet、Flash和Silverlight的话,我们会发现它们具有一些共同点:

(1)都是作为浏览器的插件运行(当然有些可以作为浏览器外插件运行);

(2)都会当被嵌入的页面打开时下载到客户端运行;

(3)都需要客户端运行环境的支持;

(4)都会尽可能支持多种操作系统(跨平台)和支持多种浏览器(跨浏览器)。

因为是作为浏览器的插件运行,所以它要求以一种安全的方式运行,仅能访问客户端的一些有限资源而不能执行一些需要较高权限的操作(比如获取系统硬件信息或者格式化磁盘分区等),它需要客户端安装运行环境,Flash和Silverlight的运行环境安装都只有几M大小,比较容易下载和安装(Java Applet的运行环境JRE则大多了,不知道这是不是它运行不起来的原因,也不知道Adobe和Microsoft是否也从Java Applet上吸取了很多经验教训)。并且,如果客户端没有安装相应的运行环境,在浏览器中会看到安装提示,只有得到了用户的同意之后才会安装(国内很多软件公司为了提高装机量采用了不提示或者误导的方式让用户安装,这其实是病毒的做法)。除此之外,它对所在的服务器上的资源的访问权限也不大,比如在Silverlight中进行数据绑定就要使用WCF。

Silverlight的开发工具VS2010以及XAML语言等知识。

创建Silverlight项目

当我们创建一个Silverlight项目时会提示是否创建一个承载项目,如下图所示:

clip_image002

由于在VS2010中已经直接支持创建ASP.NET MVC2的Web项目,所以承载Silverlight的Web项目类型有三种:ASP.NET Web应用程序项目、ASP.NET 网站及ASP.NET MVC Web应用程序项目。ASP.NET Web应用程序项目可以提供与Visual Studio .NET 2003 Web 项目相同的 Web 项目语义,它的编译模型与 Visual Studio .NET 2003 编译模型类似。项目中的所有代码文件(独立文件、代码隐藏文件以及类文件)将被编译成单个程序集并存储在 Bin 目录中。由于编译会创建单个程序集,因此可以指定程序集名称和版本等属性。如果我们仅仅是开发Silverlight的话,这个可以随便选择一种类型,以便能在网页中查看Silverlight的运行效果。

VS2010界面

创建Silverlight项目成功之后可以看到如下的界面:

clip_image004

在VS2010中可以直接从工具箱中向Silverlight界面中拖控件。在开发ASP.NET时一个页面会分为两部分.aspx.cs和.cs,前者包含了设计代码,后者则是包含业务逻辑代码,和ASP.NET开发一样,Silverlight的项目中每个控件或者页面也是

分为.xaml和.xaml.cs,

.xaml代码也是包含设计代码,

.xaml.cs则是包含业务逻辑代码。

在每个Silverlight项目中默认会有一个app.xaml文件,这个文件有点类似于WinForm项目中的program.cs类,包含了项目启动时的设置,在创建一个Silverlight项目成功后app.xam.cs的代码如下:

using System;  

using System.Collections.Generic;  

using System.Linq;  

using System.Net;  

using System.Windows;  

using System.Windows.Controls;  

using System.Windows.Documents;  

using System.Windows.Input;  

using System.Windows.Media;  

using System.Windows.Media.Animation;  

using System.Windows.Shapes;  

namespace SilverlightDemo1  

{  

    public partial class App : Application  

    {  

        public App()  

        {  

            this.Startup += this.Application_Startup;  

            this.Exit += this.Application_Exit;  

            this.UnhandledException += this.Application_UnhandledException;  

            InitializeComponent();  

        }  

        private void Application_Startup(object sender, StartupEventArgs e)  

        {  

            this.RootVisual = new MainPage();  

        }  

        private void Application_Exit(object sender, EventArgs e)  

        {  

        }  

        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)  

        {  

            // 如果应用程序是在调试器外运行的,则使用浏览器的

            // 异常机制报告该异常。在 IE 上,将在状态栏中用一个

            // 黄色警报图标来显示该异常,而 Firefox 则会显示一个脚本错误。

            if (!System.Diagnostics.Debugger.IsAttached)  

            {  

                // 注意: 这使应用程序可以在已引发异常但尚未处理该异常的情况下

                // 继续运行。

                // 对于生产应用程序,此错误处理应替换为向网站报告错误

                // 并停止应用程序。

                e.Handled = true;  

                Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });  

            }  

        }  

        private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)  

        {  

            try 

            {  

                string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace;  

                errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n");  

                System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");");  

            }  

            catch (Exception)  

            {  

            }  

        }  

    }  

可以在Application_Startup()方法中通过设置RootVisual属性来指定启动那个页面作为Silverlight的启动界面。

关于XAML

在Silverlight中使用XAML作为Silverlight的界面设计语言,XAML是一种特殊的XML格式(就像XHTML也是一种特殊的XML格式一样),现在我们不用太关注它,经过以后的练习就会慢慢理解XAML语言的特点。下面是一个普通的XAML页面代码:

clip_image006

对于普通的Silverlight控件或者页面(也就是除app.xaml),这个xaml代码主要包含了几部分:

它对应的后台代码的类名(在上图所示的是SilverlightDemo1.MainPage);

用户控件的高度和宽度(在上图中所示的高宽分别是300和400像素);

顶级布局所使用的容器(在上图中使用的是Grid)。

在Silverlight中定义了一个Panel类,这个类是一个抽象类,它是用来界面布局的,Panel类有如下子类:

System.Windows.Controls.Canvas、

System.Windows.Controls.DockPanel、

System.Windows.Controls.Grid、

System.Windows.Controls.Primitives.TabPanel、System.Windows.Controls.Primitives.ToolBarOverflowPanel、System.Windows.Controls.Primitives.UniformGrid、

System.Windows.Controls.StackPanel及

System.Windows.Controls.VirtualizingPanel和

System.Windows.Controls.WrapPanel。

在WinForm中我们直接往界面中拖控件就可以了,因为在WinForm中我们使用X和Y坐标来定位控件的左上角顶点的位置,在ASP.NET中则可以使用Table或者DIV来布局,在Silverlight中界面布局和ASP.NET及WinForm中都不一样,相对要稍微复杂一些(个人感觉倒是有些和Java SE类似,在Java SE中有FlowLayout、BorderLayout、CardLayout及GridLayout),在Silverlight中界面布局则有上面提到的那些类(莫非和Java SE一样,因为要跨平台,所以才会复杂一些)。利用这些界面布局类及它们的组合可以实现复杂的界面布局,当然如果你觉得仍不能满足要求的话,可以界面布局类。关于这些布局类的用法将在下一篇讲解。

Silverlight项目的编译

大家都知道.NET项目都可以使用csc.exe编译,其实VS在编译项目是也是调用了csc.exe的,所以对于平常的简单的想法的代码测试周公都是用记事本写代码然后在命令行下调用csc.exe编译。用VS编译项目最终也会调用csc.exe来编译,和以前编译.NET项目不同的是,编译Silverlight项目多加了一个命令行参数,这个命令行参数是“nostdlib”,它的作用说明如下:clip_image008

从上图可以看出,使用“nostdlib”参数的作用是不引用标准库。在.NET类库中核心类库是mscorlib.dll,使用这个参数的作用就是不引用mscorlib.dll这些类(因为Silverlight要跨操作系统要支持多浏览器,所以不能带有Windows系统的特色)。尽管在Silverlight中可以使用C#或者VB.NET编程,但是Silverlight中所使用的是.NET类库的子集,在某些用法上受到限制。甚至有些类或者有些方法出现在Silverlight的运行环境中,但是这些类和这些方法并不能为开发者所用,所幸的是为了不必要的麻烦在编写代码时出现的智能感知中不会出现这些类或者这些方法。

当我们编译Silverlight项目成功之后会得到一个dll文件,它的文件结构如下:

clip_image010

也就是除了一个dll文件之外还会生成如下文件:

一个.pdb文件。假设Silverlight项目名为SilverlightDemo1,那个pdb文件名为SilverlightDemo1.pdb,这个文件包含了VS需要的调试信息。

一个名为AppManifest.xaml的文件。不管Silverlight项目名是什么,这个文件名总不会变,在这个文件里包含了当前Silverlight项目所需要的程序集。在本实例中AppManifest.xaml的内容如下所示:

clip_image012

一个测试用的html文件。假设Silverlight项目名为SilverlightDemo1,那个html文件名为SilverlightDemo1TestPage.html,即{Silverlight项目名}TestPage.html,也就是会以Silverlight项目名加TestPage作为文件名的html文件。这个文件文件演示了如何在页面中嵌入Silverlight控件,它的代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<html xmlns="http://www.w3.org/1999/xhtml" > 

<!-- saved from url=(0014)about:internet --> 

<head> 

    <title>SilverlightDemo1</title> 

    <style type="text/css"> 

    html, body {  

        height: 100%;  

        overflow: auto;  

    }  

    body {  

        padding: 0;  

        margin: 0;  

    }  

    #silverlightControlHost {  

        height: 100%;  

        text-align:center;  

    }  

    </style> 

    <script type="text/javascript"> 

        function onSilverlightError(sender, args) {  

            var appSource = "";  

            if (sender != null && sender != 0) {  

              appSource = sender.getHost().Source;  

            }  

            var errorType = args.ErrorType;  

            var iErrorCode = args.ErrorCode;  

            if (errorType == "ImageError" || errorType == "MediaError") {  

              return;  

            }  

            var errMsg = "应用程序中未处理的错误" +  appSource + "\n" ;  

            errMsg += "代码: "+ iErrorCode + "    \n";  

            errMsg += "类别: " + errorType + "       \n";  

            errMsg += "消息: " + args.ErrorMessage + "     \n";  

            if (errorType == "ParserError") {  

                errMsg += "文件: " + args.xamlFile + "     \n";  

                errMsg += "行: " + args.lineNumber + "     \n";  

                errMsg += "位置: " + args.charPosition + "     \n";  

            }  

            else if (errorType == "RuntimeError") {             

                if (args.lineNumber != 0) {  

                    errMsg += "行: " + args.lineNumber + "     \n";  

                    errMsg += "位置: " +  args.charPosition + "     \n";  

                }  

                errMsg += "方法名称: " + args.methodName + "     \n";  

            }  

            throw new Error(errMsg);  

        }  

    </script> 

</head> 

<body> 

    <form id="form1" runat="server" style="height:100%"> 

    <div id="silverlightControlHost"> 

        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> 

          <param name="source" value="SilverlightDemo1.xap"/> 

          <param name="onError" value="onSilverlightError" /> 

          <param name="background" value="white" /> 

          <param name="minRuntimeVersion" value="3.0.40818.0" /> 

          <param name="autoUpgrade" value="true" /> 

          <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0" style="text-decoration:none"> 

              <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="获取 Microsoft Silverlight" style="border-style:none"/> 

          </a> 

        </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div> 

    </form> 

</body> 

</html> 

从代码中可以看到,如果浏览此页面的客户端没有安装Silverlight客户端的话,就会看到一个下载安装Silverlight客户端的提示,用户点击链接就可以跳转到微软官方网站下载Siverlight。下载完成会提示用户是否安装。也就是在下载和安装Silverlight时都会得到明确的提示,用户可以明确地选择是否下载或安装,这一点很值得国内的公司学习。国内很多以“正当”、“安全”自居的公司提供的软件总是模糊、混淆某些概念来达到自己不可告人的目的,甚至还有几款装机量上亿的软件经常扫描用户的非系统盘之外的敏感区域,这些公司都应该好好学习一下国外软件的做法。

一些dll文件。这些dll文件是运行当前Silverlight项目所需要的依赖文件。在AppManifest.xaml文件中我们看到了里面有除SilverlightDemo1.dll之外的如下文件:

System.ComponentModel.DataAnnotations.dll

System.Windows.Controls.Data.dll

System.Windows.Controls.Data.Input.dll

System.Windows.Controls.dll

System.Windows.Controls.Navigation.dll

System.Windows.Data.dll

zh-Hans/System.ComponentModel.DataAnnotations.resources.dll

zh-Hans/System.Windows.Controls.resources.dll

zh-Hans/System.Windows.Controls.Data.resources.dll

zh-Hans/System.Windows.Controls.Data.Input.resources.dll

zh-Hans/System.Windows.Controls.Navigation.resources.dll

zh-Hans/System.Windows.Data.resources.dll

在上面的文件都是运行Siverlight项目的依赖文件。其中zh-Hans文件夹中的文件都是一些资源文件,当然除了zh-Hans之中的文件之外,还有其它类似于zh-Hans文件夹的文件夹,它们里面的文件文件名和zh-Hans文件夹下的文件名一样,这些文件是语言资源文件。

说到Silverlight项目的依赖文件就不得不说一下Silverlight项目的核心文件。大家知道Siverlight程序是被下载到客户端执行的,所以需要在客户端安装Silverlight运行环境,为了方便在不同网络环境下的用户下载和安装Silverlight运行环境,所以这个运行环境要尽可能的精简(实际上只有5M),既然它被精简了所以自然不可能提供.NET Framework提供的所有功能。在Silverlight运行环境中包含了如下程序集:

mscorlib.dll:注意此mscorlib.dll非.NET Framework中的mscorlib.dll,不过在Silverlight运行环境中它提供了类似于NET Framework中的mscorlib.dll的功能,是Silverlight运行环境中核心类库(核心中的核心)。

System.dll:提供了泛型、URI处理及正则表达式相关功能的类。

System.Core.dll:提供了对LINQ的支持。

System.Net.dll:提供了网络功能,这样我们下载Web文件和创建基于Socket的网络连接。

System.Window.dll:提供在Silverlight中创建UI的类。

System.Window.Browser.dll:提供了对HTML元素交互操作的类。

System.Xml.dll:提供了XmlReader和XmlWriter两个类用以处理Xml。

可以看出在AppManifest.xaml中出现的那些除SilverlightDemo1.dll之外的dll都没有在Silverlight运行环境所提供的核心类库中。前面说了Siverlight运行环境的设计理念是尽可能使其安装和下载体积小,所以Siverlight运行环境的设计者在核心类库之外又提供了一些类,这些类提供的功能并不是在每个Silverlight应用中都会用到(如果总会用到就会放到核心类库中了),所以以附加类库的形式提供。当利用VS编译Silverlight项目的时候,VS会自动检测那些本项目需要但又不在Silverlight核心类库中的dll,所以我们会在bin目录下会看到一些“不知道怎么跑出来的”dll文件。

在这些附加类库中经常会用到的dll有如下几个:

System.Window.Controls.dll、

System.Window.Controls.Data.dll、

System.Window.Controls.Data.Input.dll、

System.Window.Controls.Input.dll及

System.Windows.Controls.Navigation.dll,

确实这几个文件都在我们的AppManifest.xaml文件中出现了。

一个.xap文件。同样假设Silverlight项目名为SilverlightDemo1,那个xap文件名为SilverlightDemo1.xap,这个xap文件其实是一个压缩文件,我们可以直接使用WinRAR或者其它压缩软件打开这个xap文件(如果不嫌麻烦可以将SilverlightDemo1.xap改名为SilverlightDemo1.xap.zip再用解压软件打开)。下图是使用WinRAR直接打开SilverlightDemo1.xap的情景:

clip_image014

从上图中我们看到了前面提到的AppManifest.xaml和SilverlightDemo1.dll文件以及上面提高的“一些”dll文件,这些dll文件都是在AppManifest.xaml中出现过的、SilverlightDemo1项目所需的附加dll。适用xap的方式发布Silverlight有两个好处:一是xap文件是压缩过的文件,可以减少网络流量以提高下载速度;二是便于部署,如果你想部署Silverlight部署到别某个网站仅仅需要将这个xap文件拷贝到网站所在的目录然后创建一个嵌入Silverlight的页面就够了,超级简单。

Silverlight项目的部署

前面讲到部署Silverlight项目需要的仅仅是一个xap文件和一个包含了如何设置Silverlight项目的网页文件。有时候出现这样的情况,创建项目的时候取了一个名字,到最后发布的时候觉得这个名字不直观,那么我们可以通过VS来设置最终生成的Silverlight项目的文件名,具体方法就是在Silverlight项目上点击鼠标右键选择“属性”,出现如下窗口:

clip_image016

从上图可以看出,在这个面板中我们可以指定如下跟Silverlight项目有关的东西:

应用程序集名称:即最终生成的dll文件名;

默认空间名:即创建代码文件时默认所使用的namespace名称;

启动对象名:可以看出为什么在App.xaml中的Application_Startup()方法中指定启动那个窗体就会启动那个窗体,因为默认是启动App对象的,所以它的制定才有用,如果在项目中创建了多个类似于App.xaml的文件,则会在这里出现多个选择;

生成的Silverlight应用的版本:如果安装了多个Silverlight SDK的话,这里会有多种选择;

最终生成xap文件名:可以将其改为更直观的名字。

从本篇前面的讲解可以知道Silverlight最终会被下载到客户端运行,客户端下载到的是一个xap文件,这个xap文件从本质上来说其实就是一个压缩文件。如果解压这个文件就可以得到Silverlight项目的dll文件,遇上有心人是可以利用一些反编译工具得到源代码的,如下图就是使用著名的反编译软件Reflector打开本篇中的dll文件的情形:

clip_image018

从图中可以看到利用这个软件可以很方便地查看没有经过任何处理的.NET程序集或者Silverlight程序集代码(以MSIL形式存储的),所以为了安全起见不要再Silverlight中保存任何隐私的、安全相关的数据。除此之外还可以对生成的程序集进行进行加密或者混淆,加密就是利用MSIL指令的一些特点使反编译工具无法反编译出源代码,混淆时是对程序集中的变量名和方法名加以变化,使其不再具有望文知义的特点从而达到了即使反编译得到了源代码也难以阅读的目的。在VS中本身就提供了一个代码混淆工具Dotfuscator的社区版,这是一个免费但功能有限的.NET代码混淆工具,它的界面如下:

clip_image020

如果是一般的项目,可以使用Dotfuscator进行混淆,这样可以使一般的窥探者望而生畏。

对于软件的代码保护是一个长期以来一直在探讨的话题,在本篇里提出来只是让大家有这方面的意识,而不是让大家把大部分精力都放在如何组织窥探自己的源代码上了。对软件源代码的反保护和保护一直是矛和盾的问题,二者相互竞争相互发展,不管使用什么语言都难以彻底有效地避免这个问题。

声明:这篇文章转载自周公。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页