最近从架构的角度做了一个 Windows 8 下 Metro Style 应用程序开发介绍的讲座。以下是讲稿。
如有问题欢迎指正。
下载地址:
1 概述
这篇的标题叫做Windows RT Introduction而非Windows 8 Introduction是想强调此次介绍是从开发人员的角度而不是普通用户的角度出发的。同时,我们关注的是Metro Style应用而不是传统的Win32应用程序的开发。
实际上,使用C#或者HTML + Javascript书写一个Hello world应用的代码例子已经在网上泛滥了。但是仅有一个Hello world并不能够说你掌握了Win RT的开发。从Pro的角度来说我们应该弄清楚整件事情的细节。那么首先就应当是他的架构。这样写起程序来才能心定。
2 Windows 8 Metro与Desktop模式
2.1 两种模式
Windows 8的应用程序显示模式目前有两种,定义在METRO_MONITOR_MODE中:即传统的桌面模式(MMM_Desktop)以及Metro模式(MMM_Metro)。如果你是Windows Phone用户的话可能就会对Metro比较熟悉。事实上,微软在2009年启动Windows 8的研发工作时目标是创造一个完全不同以往的操作系统,完全不以之前的操作系统为蓝本。而后发现Desktop应用是不可或缺的部分而将两个部分进行合并。一开始用可能会有些别扭,但是我估计开发人员半天之内就能够熟练使用这个系统了。
2.2 Metro和Desktop的一些不同
既然有两种模式那么我们自然就会关注他们的不同点。这个问题应该从架构图上做一下说明但是我们可以先有一些直观的认识。
2.2.1 Message Loop
消息处理的编程是传统Desktop应用程序的重要部分。你需要书写维护Message Loop的代码。例如:在WinMain调用(或者其子例程中)你需要书写类似
while (::GetMessage(&message, NULL, 0, 0)) { ::TranslateMessage(&message); ::DispatchMessage(&message); } |
而在Window创建之前候你一定指定了
WNDCLASS wndClass; // ... wndClass.lpfnWndProc = WndProc; |
这样你就可以在WndProc函数中决定特定message的流向了。对于绘图来说,你一定是接受了WM_PAINT消息,然后执行了区域重绘。
但在Metro App中这些都已经隐藏了,而且消息的细节也可能发生了变化。Metro App中你看不到消息循环。一切关于界面消息的分发都隐藏在了CoreDispatcher中。因此如果你用Spy++去试探Metro App的消息循环那么你什么都抓不到。
2.2.2 Display
在传统的Desktop应用程序中,绘图可能通过GDI,GDI+,DirectDraw,DirectX进行。同样通过捕获WM_PAINT消息或者当系统处于IDLE的时候进行绘图(对于游戏编程来说)。
而Metro App不会再支持GDI和GDI+,在Metro App中绘图只能通过DirectX来进行。确切的说是Direct3D和新公布的Direct2D、Direct Write API。因此Metro应用的所有绘图都是希望是硬件加速的。这种绘图更高效,解放CPU,而且一般不需要处理复杂的Dirty Region Repaint。
2.2.3 Life Cycle
Metro App并没有关闭窗口这种按钮。其生命周期是由系统托管的。系统会决定仅仅是挂起应用执行还是需要完全销毁应用进程。这和一般意义上的Desktop应用程序不一样。(当然,你也可以使用Alt+F4显示的结束Metro App的执行)。
2.2.4 Share & Communication
传统的桌面应用程序有多种手段进行公共组建的公用或IPC。但是在Metro App中,隔离是一个很重要的概念,应用的可执行部分,运行库,Isolated Storage都是独立的,不能够共用。同样,不能够使用传统的IPC机制。应用程序的互动仅仅可以通过内置的Contracts进行,关于这一部分内容可以查看MSDN:
http://msdn.microsoft.com/en-us/library/windows/apps/hh464906.aspx
2.2.5 Portability
传统的Desktop应用程序的支持大多为x86/64架构的处理器。由于Metro环境可以完整运行在ARM处理器上是一个重要的特性,因此Metro App可以运行在ARM处理器上,即同时部署在PC和移动设备上。
2.2.6 OS Access
当然为了Portability的要求,必然要求应用不能够越过Win RT的抽象,因此Metro是不能像Desktop App那样访问所有的Windows API的。
3 从Windows 8 API的架构图看Windows RT
我们对Windows RT的介绍都将围绕着这个图展开。
在这个图中,最底层的是NT的内核;在其上是Windows子系统。实际上NT至少有三个子系统,Windows子系统,POSIX子系统(Unix)和OS/2子系统。POSIX子系统和OS/2子系统实际还是在使用Windows子系统。 在Windows子系统上划分了不同的运行时(橙色)和程序库(浅蓝色),最上面的绿色是我们使用的各种开发语言。
这个架构图实际上说明了一切。并且消除了很多误解:
(1) 第一个误解是INFOQ指出的Windows RT和Win32是完全分开的。这源于微软发布的一幅饱受批评的架构图,在那张架构图中,Windows RT和Windows子系统竟然是并排排列的。这是很荒谬的,Windows RT实际上基于Windows子系统。首先Windows RT完全基于COM;其次Windows RT利用了一部分现有的Win32 API;其余的部分Windows RT则直接访问NT内核。
(2) 第二个误解是C++/CX。C++/CX是微软推荐的开发Windows RT的方式。他主要隐藏了COM的复杂性。关于这个问题我们后续会有说明。这个误解是C++/CX实际就是C++ CLI。实际上这是两个完全不同的东西,C++ CLI是运行在托管环境下的,而C++/CX完全是Native的。
3.1 Windows RT仅用于Metro应用
从架构图中可以看出,Win RT仅仅用于Metro应用。并秉承了我们刚才介绍的,简单部署,没有共享的组件,没有IPC,等等。
3.2 Windows RT构建与COM之上
这也是为什么说Windows RT是构建与Win32之上,因为COM是Win32重要的组成部分。这意味着:
(1) 你可以用之前所有的消费COM的方式来使用Windows RT,你可以用C,你可以用ATL或者新的WRL;
(2) WRL完全符合传统的C++语法,这意味着你可以使用不同的编译器(例如Intel C++编译器)来构建Metro应用。但是微软显然希望大家都来使用C++/CX,WRL的文档跟没有差不多,现在也看不到一个完整的例子出现。
3.3 Windows RT限制了系统API的调用
Win RT是基于COM的,但是COM仅仅是一个二进制协议而已。在COM Interface实现中从技术上讲还是在调用Win32 API。但由于前面介绍的Win RT的设计要求,系统API的调用需要受到严格的限制。仅仅支持有限的API调用,因此在你希望使用一个Win32 API时,一定要查询MSDN上的Applied To一节,看看是否是Metro Style App | desktop App。
同样的道理,.NET的某些方法也在进行着系统调用,因此在使用.NET开发Metro Style应用程序的时候也并不是所有的程序集都能够支持。当然,如果使用P-Invoke的方式调用Win32 API那么危险性就会更大。
总之,在Metro应用中调用不支持的Win32 API会有如下的后果:
(1) 发生一个Runtime Exception;
(2) 应用程序失去响应,尤其是在使用和消息循环相关的代码时。例如对Metro App进程使用WaitForSingleObject(hProcess)。
(3) 调用成功,但是你的Metro App应用会被Windows Store驳回。
按照上述分析,那么即使你存在相当可观的COM代码库,也需要巨大的努力才能够保证他们在Metro App上正确运行(消除非法的系统调用)。对于新的应用来说,为了避免书写大量的COM开发代码,最好使用C++/CX进行开发了。
3.4 C++/CX
为什么会有C++/CX呢?这可以联想n年前我们为了避免C++开发COM的冗长的代码,转而使用C开发关键程序,而使用Visual Basic创建COM组件。现在时间到了2012年,VB6已经不在考虑范围之内了,于是C++/CX取代了他的位置。
C++/CX是Native的,但是它的语法为什么能够和C++ CLI保持近乎一致呢?这是因为Win RT本身虽然是Native的,但它以.NET兼容的方式暴露了元数据。但是我们在编程中要时刻想到,我们在操作实打实的Native对象。根本没有什么垃圾收集器在帮助我们。
那么为什么不单纯使用.NET开发Metro App呢?这是因为对于移动设备来说,CPU的速度和电池是两大局限,因此在近一年,Go Native的大潮终于袭来。目前:
(1) iOS使用Objective-C进行程序开发,而且在移动设备上也是没有垃圾收集器的,需要手动释放使用的内存;
(2) Android一开始使用Java进行开发,但是在糟糕的性能和社区的强大压力下,终于开放了C/C++开发接口;
(3) WP7/8也出现了类似Android的情况。
目前客户端应用向更薄(核心应用向服务器移动),更快(运行速度快,耗电小),交互更丰富(没有动画你都对不起观众)的方向发展。因此开放Native接口是大势所趋,C/C++顺理成章的在Windows 8强势回归了。
但是,用.NET开发Metro应用也是一个不错的选择,尤其你的应用没有密集的运算(游戏)的情况下。你可以参考幻灯片中的Cheat Sheet。