简介 欢迎回到 Mobile Ink Jots。现在是七月份,发布 Windows XP Service Pack 2 (SP2) 已进入倒计时了。如果您读过我以前的专栏,或以任何方式一直跟着最新 Tablet PC 开发的脚步,毫无疑问您已经知道 Windows XP SP2 将包括 Tablet PC 的“Lonestar”版 — 一个正式名称为 Windows XP Tablet PC 2005 版的版本。
对于 Lonestar 备有的所有新功能,最为开发人员所热切期望的是新添加的对在 Web 上手写的支持。那么,让我们开始深入研究一下它带来了什么好处,以及在各种基于 Web 的应用程序方案中运行手写应用程序实际意味着什么。
什么是 Web 应用程序? 它对支持手写的应用程序来说意味着什么?这些是有关存在的问题,但就技术而言,即便是在 2004 年,Web 仍然是一个非常混乱的概念区域。谈到这类内容时,我们必须非常谨慎而且非常明确。对不同的人来说,“Web 应用程序”这一短语意味着截然不同的东西。
对于初学者来说,我们不是在讨论 ASP.NET 对比 PHP 对比 JavaServer Pages (JSP) 或您有的东西。这是因为根本没有这个必要 — 所有这些服务器端 Web 技术都能用于为 Tablet PC 客户端提供丰富的、支持手写的内容(稍后会介绍更多有关此方面的内容)。更确切地说,我们需要关心的 Web 技术几乎全部是客户端层:如何提供优于 Web 浏览器内置功能的丰富用户体验,从而允许手写的收集和呈现。例如,如何加载(和编写)一个嵌入在 Web 页中的启用手写的控件?
当然,目前 Web 不再与浏览器密切结合。成熟的富客户端应用程序与 Web 服务通讯,而其中从没有显示任何 HTML,这种情况也并非罕见。但是,这类启用 Web 的客户端应用程序对“Web 应用程序”来说并不是一个非常有用的定义。很多 Tablet PC 应用程序都是按这种方式设计的。为了本次讨论,我会将 Web 应用程序定义为任何程序或组件代码 — 其自身通过 HTTP 部署,从而使对访问 Web 服务器的客户端变得无缝可用,而无须安装、配置或维护任何东西。
在过去十年的大部分时间中,Java 小程序和 Microsoft ActiveX_ 组件构成了富客户端层 Web 应用程序技术的基础。但是对于 .NET Framework 来说,ActiveX 控件是一项被极力反对的技术(而且在 Tablet PC 的开发期间,使用 Java 是无法成功的)。没关系。.NET Framework 给双方提供了一个更好的选择:我们可以使用 C# 或 Microsoft Visual Basic_ .NET 以及 .NET Framework 的 Windows Forms 类在我们的 Web 应用程序中实现富客户端层功能。这是真的,无论我们需要在 Internet Explorer Web 页中添加一个自定义控件,还是开发一个带有其自己的框架窗口的独立可执行文件(或者可能是二者的某种结合)都是如此。相同的 .NET Framework 技术 — Windows Forms、代码访问安全性,以及现在的 Microsoft.Ink,应用于所有这两个解决方案。
从 1.0 版本开始,.NET Framework 已经支持通过 HTTP 进行代码的部署和分发。Windows Forms 可执行文件可以很简单地部署在 Web 服务器上,并在 URL 上运行 — 一种称为无接触部署的方法 — Internet Explorer 5.01 版引入了 object 标记的一种新语法,它允许加载托管 Windows Forms 控件而不是 ActiveX 控件。不过,支持在这些“移动代码”方案中运行手写对 Tablet PC 的 Lonestar 版来说是个新功能。
在上述任一情况下,只要 .NET Framework 运行库执行移动代码(任何从远程源— 通常是 Web 服务器,但也可能是 FTP 站点或映射网络驱动器 — 获得的代码),该代码就会受名为沙箱(听起来有点古怪)的东西的约束。Java 是沙箱安全模型的先驱,使得全世界的用户可以安全地在任意 Web 站点上与 Java 小程序进行交互,而无须过多地担心这些小程序会将硬盘重新格式化,或窃取我们的个人信息,或有其他不轨行为。
通过自动映射一组基于证据 的权限,.NET Framework 在 Java 沙箱安全模式上进行了改进,该证据有效地具有关于代码来自何处和/或由谁编写的可信信息。这意味着,在默认情况下,与从本地 Intranet 下载的控件和程序相比,从 Internet 下载的程序和控件普遍以一个更受限制的权限集(将其看作一个更小的沙箱)运行。从一个逻辑极端来讲,从计算机的本地硬盘运行的代码得到整个权限集(或者根本没有沙箱,或者有一个无穷大的沙箱,这取决于您的观点)。从另一个逻辑极端来讲,从一个在 Internet Explorer 的受限站点区域中列出的站点运行的代码根本得不到任何权限去运行。
随着讨论的进行,我们将对代码访问安全做更详细的探讨。首先,让我们开始学习如何在 Internet Explorer(毫无疑问它还宿主大多数瘦客户端 Web 应用程序)中宿主启用手写的自定义控件。
在 Internet Explorer 中宿主启用手写的控件 在 MSDN Magazine 的 2002 年一月刊中,Jay Allen 的文章 Host Secure, Lightweight Client-Side Controls in Microsoft Internet Explorer 提供了一个关于在 Internet Explorer 中宿主托管 Windows Forms 控件的非常好的背景。本文中的示例使用了一个 ActiveX-era <object> 标记的重载。对于那些缺乏经验的人来说,这里有一个必要的 HTML 代码的简单示例,如下所示:
<object classid="MyControls.dll#MyControls.UserControl1" width="300" height="200" id="uc1"> <param name="BackColor" value="#808080" /> <param name="Text" value="You get the idea..." /> Sorry, your browser doesn't seem to support .NET controls. </object>
与宿主 ActiveX 控件相比,关于这个新的 HTML 语法,的确有一些重要的事情需要注意。正如您会看到的,classid 属性已经被重载,以指定该控件的程序集的位置,以及它的与一个“#”字符连接在一起的完整托管类型名称。该程序集的位置被指定为一个 URL,相对于该页的基 URL,就像任何普通的超级链接,或者到外部图像、样式表或脚本文件的链接。
官方说法是,为了在使用这一新语法时加载托管控件,需要 Internet Explorer 5.01 版或更高版本;但我建议使用更高版本。Internet Explorer 5.5 和 6.0 版在这一方面提供了若干种极其有效的修补程序,其中有一些特别针对于与 Microsoft.Ink 命名空间组件的交互。
关于这个新的object标记语法,有一件重要的事情需要注意:直接从全局程序集缓存加载托管控件是不可能的。该控件的程序集必须通过指定的 URL 访问。例如,将一个 Windows Forms PictureBox 控件直接加载到 Web 页中是不可能的:
<!-- THIS DOESN'T WORK --> <object classid="System.Windows.Forms.dll#System.Windows.Forms.PictureBox" width="300" height="200" id="pb1"> </object>
不过,从 Windows Forms 的 PictureBox 类派生您自己的控件,并将此控件宿主到 Web 浏览器中是完全可能的。System.Windows.Forms.dll 上的程序集依赖项将通过 .NET Framework£¨它当然包括全局程序集缓存)的默认探测路径解析。
那么,手写支持的情况又怎样呢?由于相同的原因,我们不能直接加载 Windows Forms PictureBox 的实例,我们不能从驻留在客户端的全局程序集缓存中的 Microsoft.Ink 程序集直接将一个 InkPicture 或 InkEdit 控件实例化。我们必须派生自己的控件,这样做很好,因为实际上,无论如何我们都不希望将自己限于 InkPicture 和 InkEdit。通常,我们当然希望编写自己的用户界面控件,附加 InkCollector 或 InkOverlay 对象,并完全按照我们希望的方式在我们希望的地方执行识别。
下列的示例代码演示了一个简单但完整的、由 UserControl 派生的 Windows Forms 类,它带有一个附加的 InkCollector 对象。
namespace MyControls { public class InkableUserControl : System.Windows.Forms.UserControl { private Microsoft.Ink.InkCollector inkCollector;
public InkableUserControl() { // This call is required by the Windows.Forms Form Designer. InitializeComponent();
// Further initialization this.BackColor = Color.FromKnownColor(KnownColor.Window); this.inkCollector = new InkCollector(this); // secure! this.inkCollector.Enabled = true; } } }
对 Lonestar 来说,有什么新内容呢?您可能注意到的第一件事(尤其是如果您没有安装 Tablet PC 平台 SDK 1.7 版的话)是 InkCollector 对象的新构造函数重载,这将带来一个对托管 Windows Forms 控件实例而非原始 Win32 窗口句柄的引用。精细,是吗?(没有 Lonestar SDK,上面的代码产生一个更不精细的编译器错误。)InkCollector 要与 Windows Forms 对象在一个安全的、严格受约束的、适用于 Internet 应用程序的沙箱中进行交互,则需要这个新的构造函数重载。
如果您使用旧式构造函数(例如,用前面代码中的 new InkCollector(this.Handle) 替换 new InkCollector(this)),控件将要求权限来接触系统上的全部窗口以及执行非托管代码。这是一个极大的沙箱,它实际上根本不适用于运行移动代码。事实上,在默认情况下,试图调用一个旧式构造函数在 Intranet 和 Internet 区域中都会引发 S