【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 3715|回复: 13

windows8开发系列教程

[复制链接]
发表于 2013-8-8 09:37:38 | 显示全部楼层 |阅读模式
my_business


软件开发者真是闲不下来的命,技术更新太快,这不,微软这位大哥又高举windows8闪亮登场了,关于win8的战略意义,时代背景,偶这样的小人物在这里就不多说了,google上这类文章不胜枚举,作为开发者,在这个系列里会从技术角度去一探win8的一些特性和风采。


       说到为什么写win8方向博客,原因有二:
       第一,最近在抽时间看MSDN提供的windows8 app开发文档,内容不少,个人觉得还是边看边总结效果会比较好,所以有了写windows8 App开发这个系列的打算,而这个系列后续所有的文章都会是基于MSDN文章的内容外加上自己的一些理解和尝试。
       第二,对自己的耐心没有十分的信心,光看不写,说不定研究个两天就不想再弄了,写博客公开了,至少有读者监督,可以加强一定持续性,拒绝TJ。


       也想声明下,本人绝对不是MS的拥簇,没有看了Metro UI就惊为天人,当然心里也不抵触,同时本人也非赶时髦之人(ios从未涉足,android只略微了解),想一窥win8也不过是一个偶然的念头使然,总觉得但凡这些大牛公司推出的新技术新框架,都会有值得我们这些开发者推敲和学习的地方,既然移动领域自己已经错过了ios和android,这次好歹在win8上留下点印迹。


       Ok,windows8 App开发系列,今天开始起航,希望大家多多捧场,一起研究,一起分享。
      最后,以一张Metro风格的桌面作为结尾吧!



 楼主| 发表于 2013-8-8 09:38:02 | 显示全部楼层
开门见山,先把windows8 开发平台和开发框架分享给大家。



      win8 App开发主要分成两部分,desktop app和metro app。右边蓝色的部分就是desktop app的部分,也是从win7延续过来没有发生变动的部分,前段在论坛上经常看到有人说在win8中不再支持MFC,ATL等,这些绝对是谣言,以前桌面开发支持的,win8桌面程序也都支持,所以搞MFC,Com,ATL等开发的不用觉得是世界末日,地球还是绕着太阳转着呢。


     重点是绿色的部分,Metro风格App的开发平台,这是win8中全新的架构,让我们一层层来看吧。
     底层是windows内核,往上是windows Runtime层,你可以把WinRT理解为升级版的Windows API,比如它提供了更易使用的Metro UI的API,比如异步编程的全面支持,比如对多种开发语言交互的支持等等,可以说是微软继.Net之后的又一重要框架。它是由C++实现的,所以性能应该会有优势。


     从WinRT上方的几部分可以看出,win8支持3种方式开发Metro App,第一种是Html+CSS+JS,第二种是XAML+C++,第三种是XAML+C#.Net或VB.Net。对Javascript的支持,绝对是Web前端开发者的福音,至少UI这部分的开发几乎是无缝就能上手的,JS也终于走出浏览器这个沙盒,开始支持开发native app了,从Win8也对JS提供支持可以看出目前JS有多火,曾经小看或轻视这门语言的人该醒醒了。而C++开发者也可以走第二条路,而且从XAML设计UI和WinRT提供的UI接口来看,易用性和扩展性绝对要比桌面时代C/C++开发UI要强很多。第三种.Net的开发方式,让原来.Net的开发者也可以无缝过渡到Win8的Metro App开发上。所以,不管是Web开发者,C++开发者,还是.Net开发者进军Win8 Metro App开发的路都是走的通的。从技术上来看,虽然支持三种开发形式,但最后其实还是映射到WinRT这个沙盒中,所以性能都不会差。


     也许还有人会问,那原来的Win32 API和Com API跟Metro App开发中就完全无关了吗?其实不然。因为有了WinRT API了,可以说大部分Win32和Com的API确实对Metro App开发不再有用,但是仍旧有一小部分API在WinRT中是没有被Cover到的,而这些应用仍旧是可以被使用的。据说在SDK代码头文件或者MSDN新的说明文档中都会指明各个API可以应用的范围(桌面或Metro或两者都支持)。其实.Net也是类似的,原来.Net Framework中的也是只有一部分API仍旧保留作为Metro App开发的API,只是.Net的这部分应该比Win32和Com中保留在Metro开发中能使用的要多的多了。


     如果让我来进行开发技术选型的话,我还是倾向逻辑层用C++实现封装成WinRT组件,Metro UI层用JS来做,基于WinRT的特性,JS调用WinRT组件的话几乎是无缝的,只是对Win8(IE10)中JS引擎的性能略有点担心。(呵呵,因为本人对.Net了解不多,所以暂不会考虑上面第三种开发方式)


     当然上面这些都只是初探Win8后自己的理解,可能多少有些偏颇,看到这里,大家对Win8这个开发框架有什么想法吗,疑问或指正也行,有的话别忘了留言分享下!

 楼主| 发表于 2013-8-8 09:39:20 | 显示全部楼层
【windows8开发】C++开发WinRT组件和JS调用   

通过Windows Runtime(以下简称WinRT),可以用C++或C#或VB很方便的开发组件(dll),并且这些组件在用Javascript开发的Metro app中可以几乎无缝的被(javascript)调用。由于win8开发平台下,Javascript并不能访问原生的C++代码(虽然可以访问WinRT API),而实际开发中,经常会有一些既存的组件,或者需要用一些第三方库,这时就可以考虑把这些组件或希望利用的库包装成WinRT组件供UI层(JS)调用,
让我们来看一个具体的例子吧。以下代码在Beta版VS2011中可以编译运行。


创建WinRT Dll工程,工程名为TestLib,代码如下
.h文件:
  1. #pragma once
  2. #include <amp.h>
  3. #include <collection.h>

  4. namespace TestLib
  5. {
  6.     public ref class WinRTComponent sealed
  7.     {
  8.     public:
  9.         WinRTComponent();

  10.               void SyncFunc(int number);
  11.               Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ AsyncFunc(int number);

  12.        private:
  13.               bool is_prime(int n);
  14.     };
  15. }
复制代码
组件中类名为WinRTComponent,作为组件类,考虑到外部调用,声明为public,同时也声明为引用类型ref,它包含两个public方法,
SyncFunc方法为以同步调用方式计算出number值以下所有的质数,并返回结果
AsyncFunc方法为以异步调用方式计算出number以下所有的质数,并返回结果


cpp文件:
  1. #include "pch.h"
  2. #include "WinRTComponent.h"
  3. using namespace TestLib;
  4. using namespace Platform;
  5. using namespace Concurrency;
  6. using namespace Platform::Collections;
  7. using namespace Windows::Foundation::Collections;
  8. using namespace Windows::Foundation;

  9. WinRTComponent::WinRTComponent()
  10. {
  11. }

  12. bool WinRTComponent::is_prime(int n)
  13. {
  14.    if (n < 2)
  15.       return false;
  16.    for (int i = 2; i < n; ++i)
  17.    {
  18.       if ((n % i) == 0)
  19.          return false;
  20.    }
  21.    return true;
  22. }

  23. void WinRTComponent::SyncFunc(int number)
  24. {
  25.        auto primes = ref new Vector<int>();
  26.        for (int i = 0; i < number; i++) {
  27.               if (is_prime(i)) {
  28.                      primes->Append(i);
  29.               }
  30.        }
  31. }

  32. IAsyncOperationWithProgress<IVector<int>^, double>^ WinRTComponent::AsyncFunc(int number)
  33. {
  34.        return create_async([this, number](progress_reporter<double> reporter)-> IVector<int>^ {
  35.               auto primes = ref new Vector<int>();
  36.               double progress = 0.0;
  37.               double curProgress = 0.0;
  38.               for (int i = 0; i < number; i++) {
  39.                      if (this->is_prime(i)) {
  40.                            primes->Append(i);
  41.                      }
  42.                      curProgress = 100.0 * i / number;
  43.                      if (curProgress > progress) {
  44.                            progress = curProgress;
  45.                            reporter.report(curProgress);
  46.                      }
  47.               }
  48.               reporter.report(100.0);
  49.               return primes;
  50.        });
  51. }
复制代码
AsyncFunc方法中,create_async函数是通过异步的方式创建一个异步调用,使计算在后台进行,而程序不会阻塞在质数统计的计算中。其中有个匿名函数Lambda表达式的使用,这是C++ 0x/11中支持的新特性,不明白可朋友可以看我另外一篇博客。
http://blog.csdn.net/my_business/article/details/7477615


另外,以上代码中ref,^等与传统C++不同的特性在本文中就不作说明了,在这个系列的其他文章中会有说明。


最后是javascript对以上组件dll的调用,代码如下:
  1. // 新建WinRTComponent对象
  2. var testlib = new TestLib.WinRTComponent();
  3. // 异步方法调用
  4. testlib.asyncFunc(1000).then(
  5.     function (v) {
  6.         // get result from v
  7.     },
  8.     function (error) {
  9.     },
  10.     function (p) {
  11.         // get progress
  12.     }
  13. );
  14. // 同步方法调用
  15. var vec = testlib.syncFunc(1000);
复制代码
异步方法调用后会立刻返回,在后台计算结束后会调用第一个回调函数,可以从参数v中取得计算结果。另外在计算中途,可以从最后一个回调函数中得到后台计算的进度。

总的来说,组件的封装和调用还是挺简单的,大家的意见又如何呢?






 楼主| 发表于 2013-8-8 09:41:51 | 显示全部楼层
【windows8开发】异步编程入门篇之 Concurrency::task(C++)



从以前的windows应用程序开发转变到Windows8 Metro App开发,如果非要说最需要改变的观念有哪些,显而易见的Metro UI风格大家肯定都认同,而另外一个就是异步处理。从win32早期几乎全盘的同步API,到后来.Net开始支持异步API,微软其实已经做出了API风格上的转变,但是晦涩的回调处理,异常,调试难度让大多数开发者对异步模型开发望而却步,同步处理的观念紧锁住了几乎绝大部分开发者的思想,试问在某些库宣称同时支持同步API和异步API后,包括我自己,又有多少开发者选择去尝试用异步API来构筑他们的应用程序呢?
      如果有公司宣称,“在我的平台上开发App,只要你的应用程序在UI线程上运行超过50ms就会被禁止,你必须把50ms以上的阻塞处理都搬到后台处理。”你可能觉得发表这样言论的人在做梦!但是,这正是windows8告诉我们开发Metro App必须遵循的规则。个人觉得,这个定义虽然会让我们在开发中多少感觉到一点别扭,但却会把windows程序UI操作的用户体验推上一个台阶。
进入正题,关于win8异步模型,让我们从Concurrency::task这个C++中支持异步API的类开始。
       先看一个例子:
  1. #include <ppltasks.h>
  2. #include <iostream>
  3. using namespace Concurrency;
  4. using namespace std;
  5. int main()
  6. {
  7.     task<int> t([]() {
  8.         return 10;
  9.     });

  10.     t.wait();
  11.     wcout << t.get() << endl;
  12.      return 0;
  13. }
复制代码
输出 10


       先解释下这段程序做了什么,task<int>定义了一个并行任务并设定会返回int型值,t的初始化用到了Lambda表达式,其中就涉及到一个异步处理(return 10),wait()会等待异步处理结束,从get()方法能得到异步处理的返回值。这里主要用到了Concurrency::task这个类。
在异步处理中,经常希望在一个异步操作结束后唤起另外一个异步处理。Concurrency::task也支持这种功能。
  1. #include <ppltasks.h>
  2. #include <iostream>
  3. using namespace Concurrency;
  4. using namespace std;
  5. int main()
  6. {
  7.     task<int> t([]() {
  8.         return 10;
  9.     });
  10.     auto ta =  t.then([](int n){ return n + 10;});
  11.     wcout << ta.get() << endl;
  12.      return 0;
  13. }
复制代码
输出 20


        这里通过then()方法又加了一个异步处理,第一个return返回的10会作为第二个异步处理Lambda匿名函数的参数,get()方法得到n+10的结果。在这里then方法返回的还是task<int>对象,所以如果后续还要追加其他任务可以这样写:
t.then([]{}).then([]{}).then([]{})..........
      这里通过then()方法追加的延长任务(Continuation task)有Value-Based和Task-Based两种
  1. // Value-Based:
  2. t.then([](int n) {});
  3. // Task-Based:
  4. t.then([](task<int> preTask) {});
复制代码
两者的区别是: Value-Based类型的延长任务在前序任务被取消或发生异常时就不会被执行,而Task-Based类型延长任务则仍旧会被执行。


     怎么样,Concurrency::task的使用很简单吧。但是上面这些其实只是task类的一部分功能,还有很多其他功能,比如任务组合功能Concurrency::when_all,Concurrency::when_any,任务延时,任务取消等,本文只是作为一个引子,有兴趣的可以参考微软的MSDN,自己学习吧。
另外还想说明一点,Concurrency::task的API并不支持来自C++以外语言的调用,所以如果你想封装异步接口的组件供其他语言(JS,C#)调用,就不能使用Concurrency::task直接进行异步操作。针对这种情况Win8还提供了其他的API,我会在下一篇文章中进行说明。其实在前面写的《【windows8开发】C++开发WinRT组件和JS调用》这篇文章中也有关于这方面的内容,JS调用C++组件的实例中就用到了异步处理。

 楼主| 发表于 2013-8-8 09:47:32 | 显示全部楼层
【windows8开发】异步编程入门篇之 Concurrency::create_async(C++)


前文中提到过,Concurrency::task支持异步处理,但是由于它不支持跟其他语言的交互,所以如果希望开发的组件能在其他语言,比如C#,Javascript,VB中使用的话,就需要多语言交互的API,它就是Concurrency::create_async。

       我们把Concurrency::task和Concurrency::create_async两种处理方式对照来看。

       如果我们的组件本身以及调用方都只可能是C++的话,我们会使用Concurrency::task来实现异步处理。因为它更直接,所以效率也更高。假设也许我们会设计如下这样的异步处理方法:
  1. <div style="text-align: left;"><span style="line-height: 1.5;">Concurrency::task<vector<int>> GetFileContentAsync() {</span></div><div style="text-align: left;"><span style="line-height: 1.5;">return Concurrency::task<vector<int>>([]() {</span></div><div style="text-align: left;"><span style="line-height: 1.5;">// 异步处理</span></div><div style="text-align: left;"><span style="line-height: 1.5;">});</span></div><div style="text-align: left;"><span style="line-height: 1.5;">}</span></div>
复制代码
Concurrency::task的说明请参照《【windows8开发】异步编程之Concurrency::task(C++)

       那么反之,如果组件会在C++之外的语言中使用,我们就应该使用Concurrency::create_async方法,所以上面使用Concurrency::task的处理我们会改为使用Concurrency::create_async:
  1. IAsyncOperation<IVector<int>^>^ GetFileContentAsync() {
  2.      return Concurrency::create_async([]() ->IVector<int>^ {
  3.                // 异步处理
  4.      });
  5. }
复制代码
是不是有点难理解,让我来解释下上面的异步处理方式吧。
WinRT提供了4种异步处理的接口方式,
1. Windows::Foundation::IAsyncAction : 无返回值,无处理进度报告
2. Windows::Foundation::IAsyncActionWithProgress<TProgress>:无返回值,有处理进度报告
3. Windows::Foundation::IAsyncOperation<TResult>:有返回值,无处理进度报告
4. Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>:有返回值,有处理进度报告


        而Concurrency::create_async方法的参数是个Lambda表达式,也就是匿名函数指针。而create_async能根据Lambda表达式中的参数和返回值来决定4种接口中的一种作为返回类型。现在可以看出上面的异步处理中,create_async参数Lambda表达式(匿名函数)是无参数的,所以它就是无处理进度报告的,而Lambda表达式的返回值类型为IVector<int>^,所以create_async就是有返回值得。综合来看上述异步处理就应该是有返回值,无处理进度报告对应的IAsyncOperation<TResult>。


如果想参照create_async的使用实例,可以参考这篇这篇文章中,其中就有返回值为IAsyncOperationWithProgress<TResult, TProgress>的实例。【windows8开发】C++开发WinRT组件和JS调用



 楼主| 发表于 2013-8-8 09:50:01 | 显示全部楼层
【windows8开发】异步编程之Promise(Javascript)

Javascript是一种单线程语言,一旦运行一些耗时的处理,其他一切处理都会被阻塞。所以在Javascript中,异步处理显得尤为重要。由于Javascript只会运行在一个线程中,它的异步模式本质是把一些耗时的处理推迟到未来的某个时间点来运行,也正因如此,在Javascript的代码中往往充满了很多的回调处理。
Windows Runtime中提供了Promise接口,利用这个接口可以很方便的实现异步处理和回调。
看一段代码:
  1. var test = asyncFunc().then(
  2.     function (result) {
  3.          console.log("async completed and get result");
  4.     },
  5.     function (error) {
  6.          console.log("error");
  7.     },
  8.     function (progress) {
  9.          console.log("get progress");
  10.     }
  11. );
复制代码
这里先解释以下promise.then方法,定义如下:
  1. promise.then(onComplete, onError, onProgress)
复制代码
onComplete为asyncFunc成功后执行的回调函数,OnError为asyncFunc发生错误时的回调处理,onProgress为asyncFunc中每次进行进度报告时的回调处理。
promise.then算是promise最常用的一个方法,此外还有一个方法是promise.done,它的定义跟then完全相同,如下所示:
  1. promise.done(onComplete, onError, onProgress)
复制代码
它们之间有何区别呢?目前发现两点:
1.then支持延续任务调用方式(Continuation tasks),而done不支持
比如then可以这样用,而done不可以:
  1. func().then().then().then()
复制代码
2. then会捕获未处理的异常然后把错误状态作为返回值返回,而done则会把异常直接抛出

在以下文章中有promise.then方法的具体使用实例,大家可以参考下。
【windows8开发】C++开发WinRT组件和JS调用

 楼主| 发表于 2013-8-8 10:00:15 | 显示全部楼层
【windows8开发】C++开发Metro风格App


在win8系列前面的文章里跟大家分享过,win8下基于WinRT有3种开发Metro UI App的方式,一种是XAML+C++,一种是html+css+javascript,一种是XAML+C#。开发平台和框架相关的详细内容可以参考这篇文章:
【windows8开发】开发平台与开发框架
先说明下自己的开发环境吧,我是用VMWare装了Win8的消费者预览版,然后又装了Visual Studio 11 Express Beta for Windows 8,大家也可以尝试下,毕竟实际动手敲下代码跑一下,感觉会更直观一点。


在本文中会跟大家一起来讨论下用C++结合XAML怎么去开发Metro App。还是通过一个实际例子来进行说明吧,这里让我们来开发一个Rss订阅的客户端。假设就订阅我自己CSDN博客里的《windows8 app开发专栏》,最后实现效果如下图所示:




首先,打开Vs2011,新建一个Visual C++,Metro Style,Blank Applicaiton的工程。新工程大致如下:




简单介绍下工程里的文件吧,
1. App.xaml:本应用程序对象,虽然也是xaml格式,但是不包含任何UI。
2. BlankPage.xaml: 默认空白页面,可以直接修改XAML或通过designer设计页面UI。
3. BlankPage.xaml.h, BlankPage.xaml.cpp:包含默认页面UI的event和基本逻辑,但不包含BlankPage.xaml里UI生成的代码。

4. App.xaml.h, App.xaml.cpp:Application相关事件和处理,其中会调用BlankPage页面。
5. Package.appxmanifest:定义App相关的基本信息。包括App名字,描述,logo等。
6. pch.h, pch.cpp: 预编译文件
7. BlankPage.g.h, BlankPage.g.cpp: 展开External Dependencies会看到这个文件,它包含了BlankPage.xaml里UI生成的代码,其中的class通过partial关键字定义,其实跟BlankPage.xaml.h里声明的是同一个class。需要注意的是,一般情况下都不要自己去修改这个文件,它会由开发环境自动跟相关Page同步更新。
关于partial关键字以及后续代码中会出现的很多C++/CX的新特性,如果不明白可以参考下面这篇文章:
【windows8开发】深入浅出C++/CX


接着我们就开始实际功能的开发吧。
由于需要通过Rss获取信息,所以肯定需要一个存放数据的容器。新建一个Module.h文件,实际代码如下所示:
  1. #pragma once
  2. #include "pch.h"
  3. #include <collection.h>

  4. namespace RssReader
  5. {
  6.    // To be bindable, a class must be defined within a namespace
  7.    // and a bindable attribute needs to be applied.
  8.    [Windows::UI::Xaml::Data::Bindable]
  9.    public ref class DataItem sealed
  10.    {
  11.    public:
  12.       DataItem(void){}
  13.       ~DataItem(void){}

  14.       property Platform::String^ Title;
  15.       property Platform::String^ Author;
  16.       property Platform::String^ Content;
  17.       // Temporary workaround (use Object^ not DateTime):
  18.       // property Windows::Foundation::DateTime PubDate;
  19.       property Platform::Object^ PubDate;
  20.    };


  21.    [Windows::UI::Xaml::Data::Bindable]
  22.    public ref class ModuleData sealed
  23.    {
  24.    public:
  25.       ModuleData(void)
  26.       {
  27.          m_items = ref new Platform::Collections::Vector<Platform::Object^>();
  28.       }
  29.       ~ModuleData(void){}
  30.      
  31.       property Platform::String^ Title;           
  32.       property Windows::Foundation::Collections::IVector<Platform::Object^>^ Items
  33.       {
  34.          Windows::Foundation::Collections::IVector<Platform::Object^>^ get() {return m_items; }
  35.       }
  36.    private:
  37.        Platform::Collections::Vector<Platform::Object^>^ m_items;
  38.    };  
  39. }
复制代码
这里包含了两个class,ModuleData类是Rss订阅信息的容器,DataItem类则定义了容器中每条单独的信息的格式。
我们再把上面定义的数据容器集成到BlankPage中,在BlankPage.xaml.h中,追加以下代码:
  1. private:
  2.      void GetModuleData(Platform::String^ feedUriString);
  3.      ModuleData^ moduleData;
复制代码
并且include相关文件:
  1. #include "Module.h"
复制代码
而在BlankPage.xaml.cpp中,加上以下代码:
首先,是加上关联文件和命名空间:
  1. #include <ppltasks.h>
  2. using namespace Windows::Web::Syndication;
  3. using namespace Concurrency;
复制代码
其次,在构造函数中创建moduleData:
  1. moduleData = ref new ModuleData();
复制代码
然后,追加GetModuleData方法的实现:
  1. void BlankPage::GetModuleData(Platform::String^ feedUriString)
  2. {
  3.    // Create the SyndicationClient and the target uri
  4.    SyndicationClient^ client = ref new SyndicationClient();
  5.    Uri^ feedUri = ref new Uri(feedUriString);

  6.    // Create the async operation. feedOp is an
  7.    // IAsyncOperationWithProgress<SyndicationFeed^, RetrievalProgress>^
  8.    auto feedOp = client->RetrieveFeedAsync(feedUri);
  9.    feedOp = client->RetrieveFeedAsync(feedUri);
  10.   
  11.    // Create the task object and pass it the async operation.
  12.    // SyndicationFeed^ is the type of the return value
  13.    // that the feedOp operation will eventually produce.
  14.    task<SyndicationFeed^> createTask(feedOp);

  15.    // Create a "continuation" that will run when the first task completes.
  16.    // The continuation takes the return value of the first operation,
  17.    // and then defines its own asynchronous operation by using a lambda.
  18.    createTask.then([this] (SyndicationFeed^ module) -> SyndicationFeed^
  19.       {
  20.          // Get the title of the feed (not the individual posts).
  21.          moduleData->Title = module ->Title->ToString();
  22.         
  23.          // Retrieve the individual posts from the feed.
  24.          auto dataItems = module->Items;

  25.          // Iterate over the posts. You could also use
  26.          // std::for_each( begin(feedItems), end(feedItems),
  27.          // [this, feed] (SyndicationItem^ item)
  28.          for(int i = 0; i < (int)dataItems->Size; i++)
  29.          {        
  30.             auto item = dataItems->GetAt(i);
  31.             DataItem^ dataItem = ref new DataItem();
  32.             dataItem->Title = item->Title->Text;

  33.                      const wchar_t* title = dataItem->Title->Data();
  34.                      wchar_t key = '8';
  35.                      const wchar_t* result = wcschr((wchar_t*)title, key);
  36.                      if (result != 0) {
  37.                            dataItem->PubDate = ref new Platform::Box<Windows::Foundation::DateTime>(item->PublishedDate);

  38.                            dataItem->Author = item->Authors->GetAt(0)->Name;

  39.                            if (module->SourceFormat == SyndicationFormat::Atom10)
  40.                            {
  41.                               dataItem->Content = item->Content->Text;
  42.                            }
  43.                            else if (module->SourceFormat == SyndicationFormat::Rss20)
  44.                            {
  45.                               dataItem->Content = item->Summary->Text;
  46.                            }
  47.                            moduleData->Items->Append((Object^)dataItem);
  48.                      }
  49.          }
  50.         
  51.          this->DataContext = moduleData;
  52.          return module;        
  53.    }).then ([] (task<SyndicationFeed^> t)
  54.    {
  55.       try
  56.       {
  57.          auto f = t.get();
  58.       }
  59.       catch (std::exception e)
  60.       {
  61.          //Handle exception
  62.       }
  63.    });
  64. }
复制代码
这里的GetModuleData方法是通过Rss订阅URL来获得订阅信息,然后把信息存储到moduleData以及BlankPage的DataContext属性中(关于DataContext后续会有说明),这是是通过异步处理来获取信息的,不理解WinRT异步task的还是先参考下上面提到的C++/CX的文章吧。
备注:上述代码中的如下代码只是为了过滤出window8专栏的文章,如果要显示所有文章,可以不需要。
  1. wchar_t key = '8';
  2. const wchar_t* result = wcschr((wchar_t*)title, key);
复制代码
然后我们可以开始开发UI了,如下是最终BlankPage.xaml的代码:
  1. <Page
  2.     x:Class="RssReader.BlankPage"
  3.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.     xmlns:local="using:RssReader"
  6.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  7.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  8.     mc:Ignorable="d"
  9.     Loaded="PageLoadedHandler">

  10.     <Grid Background="{StaticResource ApplicationPageBackgroundBrush}" Name="Grid1">
  11.         <Grid.RowDefinitions>
  12.             <RowDefinition Height="140" />
  13.             <RowDefinition Height="*" />
  14.         </Grid.RowDefinitions>
  15.         <TextBlock x:Name="TitleText" Text="{Binding Path=Title}" VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>


  16.         <Grid Name="Grid2" Grid.Row="1">
  17.             <Grid.ColumnDefinitions>
  18.                 <ColumnDefinition Width="2*" />
  19.                 <ColumnDefinition Width="3*" />
  20.             </Grid.ColumnDefinitions>
  21.             <ListView x:Name="ItemListView" ItemsSource="{Binding Path=Items}" SelectionChanged="ItemListView_SelectionChanged" Margin="10,0,0,10">
  22.                 <ListView.ItemTemplate>
  23.                     <DataTemplate>
  24.                         <StackPanel>
  25.                             <TextBlock Text="{Binding Path=Title}"  FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
  26.                             <TextBlock Text="{Binding Path=Author}" FontSize="16" Margin="15,0,0,0"/>
  27.                         </StackPanel>
  28.                     </DataTemplate>
  29.                 </ListView.ItemTemplate>
  30.             </ListView>
  31.             <Grid Name ="Grid3" Grid.Column="1" DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}">
  32.                 <Grid.RowDefinitions>
  33.                     <RowDefinition Height="Auto" />
  34.                     <RowDefinition Height="*" />
  35.                 </Grid.RowDefinitions>
  36.                 <TextBlock Text="{Binding Path=Title}" FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
  37.                 <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
  38.             </Grid>
  39.         </Grid>
  40.     </Grid>
  41. </Page>
复制代码
让我们简单分析下上面的XAML。


1. 显示Page对应的XAML的最顶层永远都是<page>节点,我们关注下<page>的以下三个属性:
x:Class="RssReader.BlankPage" :指本Page对应的类名
xmlns:local="using:RssReader" : 指本Page用到的命名空间名

Loaded="PageLoadedHandler" :Page加载时的处理,也就是页面Load事件的Handler方法
(事件处理方法到后面再追加)


2.<TextBlock>中,都有这样的属性:
Text="{Binding Path=Title}"
GetModuleData
这里的Binding其实是绑定了BlankPage类的成员变量,还记得前面GetModuleData的处理中有如下代码:
  1. this->DataContext = moduleData;
复制代码
这里其实就是绑定到了BlankPage类DataContext成员变量,而DataContext被设置成为了moduleData,所以其实就是指本文本框显示的内容为this->DataContext->Title(即moduleData的成员变量Title)。也许有人会感到疑惑,因为在BlankPage类的定义中并没有定义DataContext这个成员变量。其实前面提到过了,BlankPage.g.h中有UI生成的相关代码是通过partial关键字也被定义为了BlankPage类的内容。而DataContext在UI的Framework层应该有被定义,DataContext就是用作UI对象的数据绑定。


3. 在<ListView>,<WebView>等标签中,还看到x:Name的属性,它指代的是本UI空间对应的实例对象名字,比如<ListView>中为x:Name="ItemListView",那么<ListView>的对象名就是ItemListView,BlankPage类中可以直接通过ItemListView对象来处理<ListView>的行为。


最后,在以上XAML中,<Page>和<ListView>中还分别定义了一个事件,
<Page>:Loaded="PageLoadedHandler"
<ListView>:SelectionChanged="ItemListView_SelectionChanged"


所以还需要在代码里添加事件处理:
BlankPage.xaml.h中为BlankPage类添加如下方法,方法名跟上面属性值对应:
  1. void ItemListView_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
  2. void PageLoadedHandler(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
复制代码
BlankPage.xaml.cpp中添加以上事件处理方法的具体实现:
  1. void BlankPage::PageLoadedHandler(Platform::Object^ sender,
  2.           Windows::UI::Xaml::RoutedEventArgs^ e)
  3. {
  4.        GetModuleData("http://blog.csdn.net/my_business/rss/list");
  5. }

  6. void BlankPage::ItemListView_SelectionChanged (Platform::Object^ sender,
  7.           Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
  8. {
  9.    DataItem^ dataItem = safe_cast<DataItem^>(ItemListView->SelectedItem);
  10.    if (dataItem != nullptr)
  11.    {
  12.       // Navigate the WebView to the blog post content HTML string.
  13.       ContentView->NavigateToString(dataItem->Content);
  14.    }
  15. }
复制代码
http://blog.csdn.net/my_business/rss/list是博客Rss订阅的URL。
OK,至此大功告成,编译运行一下就可以看到开始的那个画面的。

总结一下,
1. C++结合XAML的形式加上Framework提供的很多接口确实可以非常方便的开发Metro UI风格的App。
2. 对于没了解过C++/CX以及XAML的C++普通开发者,要马上上手有点难度,前期还是需要一点投入。投入多少因人而异。
3. 个人还是更倾向于C++开发底层和逻辑层组件,Javascript开发UI的开发模式。
 楼主| 发表于 2013-8-8 10:04:41 | 显示全部楼层

【windows8开发】javascript开发Metro风格App


javascript在web前端的地位不需要再花任何言语去证明了,NodeJS的爆发又给JS带来了Web后台开发的新定义。而如今Windows8又提出了一种新的开发框架:用javascript开发Native App。可以说,锋利的javascript必然让windows平台Native App开发的生产力再上一个台阶。不吹嘘javascript的威力了,进入主题。


本文主要讨论javascript开发Metro App的一些细节以及windows 8中程序的生命周期。
首先用VS新建一个工程:选择【javascript】【Windows metro style】【Blank Application】。可以看到在这个工程中有如下默认的文件:

1.default.html:程序执行时的起始页面,你可以再这里修改你起始页面的UI。

2.default.js:default.html中加载了这个js,所以在程序执行显示起始页面时会加载和执行这个js。可以看到工程中会有如下默认的代码:
  1.     var app = WinJS.Application;
  2.     app.onactivated = function (eventObject) {
  3.         if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
  4.             if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
  5.                 // TODO: This application has been newly launched. Initialize
  6.                 // your application here.
  7.             } else {
  8.                 // TODO: This application has been reactivated from suspension.
  9.                 // Restore application state here.
  10.             }
  11.             WinJS.UI.processAll();
  12.         }
  13.     };
  14.     app.oncheckpoint = function (eventObject) {
  15.         // TODO: This application is about to be suspended. Save any state
  16.         // that needs to persist across suspensions here.
  17.     };
  18.     app.start();
复制代码
app.onactivated在程序开始或状态被激活时会触发,可以在这里执行一些初始化操作。而app.oncheckpoint则是在程序即将被挂起时会触发,可以在这里保存当前运行中的程序状态和数据,以便在下次触发运行时恢复。

WinJS.UI.processAll函数主要是用来执行UI框架中的一些初始化和事件绑定。


3.default.css: 这个文件干什么用的就不多废话解释了。


就使用默认的工程默认的代码,编译运行,可以看到程序启动后页面显示“Content goes here”。

到这里可以看出,用html+js+css来开发Native App的话还是相当犀利的,可以想象的出因为Js的登场,在windows 8上开发Native App会变得多么方便。

然后,我又做了一些尝试,首先把JQuery也加载到工程中:


一起简单验证下jquery是不是能正常工作,就验证两点,一个是jquery的选择器,一个是ajax。
在default.html中作如下修改:
1.导入jquery。
  1. <head>
  2.      ......
  3.     <script src="/js/jquery-1.7.1.js"></script>
  4.     <script src="/js/default.js"></script>
  5. </head>
复制代码
<body>节点中加入如下代码:
  1. <body>
  2.     ......
  3.     <div id="contentid"></div>
  4.     <button id="testid">ajax</button>
  5.     <script>
  6.         $("#testid").click(function () {
  7.             var requestData = {
  8.                 url: "http://www.tornadoweb.org/documentation/_sources/web.txt",
  9.                 type: "get",
  10.                 dataType: "html",
  11.                 success: function (data) {
  12.                     $("#contentid").text(data);
  13.                 },
  14.                 error: function () {
  15.                     $("#contentid").text("call error");
  16.                 }
  17.             };
  18.             $.ajax(requestData);
  19.         })
  20.     </script>
  21. </body>
复制代码
简单解释下以上代码,首先加入了一个名为ajax的按钮,然后在点击这个按钮时执行一个ajax call,把ajax call的结果写在id为"contentid"的<div>中。

好了,修改完毕,编译运行看下结果。如果网络连接正常的话应该会发现页面上多了一大串内容,其实就是ajax call后请求到的http://www.tornadoweb.org/documentation/_sources/web.txt的内容,如下图:


ok,至少能明白如下几点:
1. 对jquery的支持应该没有问题。
2. 支持XMLHttpRequest。
3. 不存在浏览器中ajax call的cross domain(跨域)限制的问题。

4. 另外我还尝试使用html5的WebSocket,也是支持的。


最后,再来探讨下App的生命周期吧。


在windows 8中,程序会由于使用者的切换或者系统进入低功耗状态而被挂起(suspended),当程序被挂起时,它仍旧驻留在内存中,当用户resuming时会被快速激活恢复running状态。但是,系统也可能因为需要释放内存或者节约电量而终止某些被挂起的程序。并且,系统只会在挂起程序时通过事件通知程序,而不会在终止程序时通知。所以如果你忽略这种情况,很可能程序会经常莫名其妙就down掉。
那么,怎么解决呢?
无疑我们需要在系统通知程序被挂起时,就要保存当前程序的执行状态(上下文)以及用户数据,以便于在程序被系统强行终止后用户重启时还能恢复挂起前的状态。系统提供了Windows.Storage.ApplicationData来帮助我们持久化保存数据,代码如下:
  1. var appData = Windows.Storage.ApplicationData.current;  
  2. var localSettings = appData.localSettings;         
  3. localSettings.values["state"] = currentState;
复制代码
另外,系统还提供WinJS.Application.sessionState来保存session数据(程序终止后会被清空),有想了解的可以参考MSDN。

好了,就写到这里。win8下js开发Native App的方式确实是让自己眼前一亮,我想即使是C++程序员在win8下也很难再愿意去用C++来开发Metro UI了,html+js在某些方面确实是太锋利了。
 楼主| 发表于 2013-8-8 10:12:15 | 显示全部楼层
【windows8开发】回复g10589119提出的问题(c#调用C++/CX dll)


g10589119的问题如下:
----------------------------------------------------------------------------------------------
From g10589119
楼主你好,最近遇到一个问题,就是在metro的开发中,想用c#调用C++ /CX写的DLL
  1. public ref class test_class sealed
  2. {
  3. public:
  4. void fun(int a, int& b,int c[10])
  5. {
  6. ....
  7. }
  8. };
复制代码
类似上面的函数,从C#中如何调用,数组和引用的参数不知在C#中怎么传递。
----------------------------------------------------------------------------------------------
首先上面的代码编译时应该会有问题,因为在C++ CX中,public类中的所有public方法参数必须使用WinRT支持的类型,有些C++原生的类型并不支持,比如int&。另外如果是要传递数组的话,也不能以上面的这种方式,C++中上面的传递只等同于传递了int*,而多语言交互时,比如C#调用C++,是不能用来传递数组的。

那么C++里类似的接口应该怎么来实现呢?
C++类中可以声明为:
  1. void fun(int a,  int* b, Platform::Array<int>^ c);
复制代码
由于WinRT对外的public类的方法参数不支持C++原生的引用int&,所以如果要实现类似功能可以用int*,其实MSDN中提到有Windows::Foundation::IReference<int>这个类似引用的类型,但是在我的VS2011环境中暂时找不到这个类型的定义,编译通不过,暂时用不了。而数组的参数可以用Platform::Array<int>^类型。我随便写了些处理如下所示:
  1. int fun(int a,  int* b, Platform::Array<int>^ c) {
  2.      int sum = 0;
  3.      (*b) = 5;
  4.      for (int i = 0; i < c->Length; i++) {
  5.           sum += c[i];
  6.      }
  7.      return sum * a + (*b);
  8. }
复制代码
C#怎么调用呢?本人对C#.Net不是很擅长,只是做了下尝试,可以如下调用:
  1. test_class test= new test_class();
  2. int a = 2;
  3. int b = 0;
  4. int[] c = new int[5]{1,2,3,4,5};
  5. int x = test.fun(a, out b, a);
复制代码
第一个参数是整型,直接传递int数值就可以,第二个参数在C++方法中是int*,映射到C#中就应该是out int,所以我们可以使用C#中的out关键字来传递b,最后一个参数是数组,可以直接传递C#中的数组类型。

关于WinRT下多语言交互调用是接口参数其实还有很多问题,有时间的话会整理下另外再写篇文章来说明,本文只是简单解决一下“g10589119”留言提的问题。
 楼主| 发表于 2013-8-8 10:18:39 | 显示全部楼层
【windows8开发】WinRT组件封装及与javascript的交互


/******************************************
开始本文之前,借块地方说明下,本文之前的所有win8系列的文章是在windows 8 consumer preview(消费者预览版)的上进行的一些尝试和分享,本文开始会切换到windows 8 release preview(发行者预览版),因为从老版本到现在的版本间,framework中一些API有所改变,所以有些示例代码并不兼容,特此说明下。
******************************************/
windows 8通过windows runtime framework支持不同语言间的交互,比如在《【windows8开发】C++开发WinRT组件和JS调用》一文里曾经介绍过javascript如何去调用C++组件,而在本文中会更关注于怎么去封装支持多语言交互的组件,组件方使用C++来描述,调用方则使用javascript,因为实例代码中会有很多关于C++ /CX的特性,所以还不了解C++ /CX的可以先阅读下这篇文章:

【windows8开发】深入浅出C++/CX

希望大家带着如下问题去阅读这篇文章。
1. javascript如何调用C++ API
2. C++如何返回数据给javascript
3. js和C++交互时的数据类型问题
4. 事件绑定,调用与回调

ok,进入正题。

Metro App中,要支持多语言调用,必须封装成windows runtime的组件dll,所谓windows runtime组件就是要基于windows runtime定义好的形式去封装组件API,而这些定义好的形式其实大部分都属于C++ /CX的特性,换句话说就是用C++ /CX的一些C++扩展特性来封装windows runtime组件。


0. 如何创建windows runtime组件工程
很简单,如果使用C++来封装组件的话,就在创建新工程时,选择 Visual C++ --> Windows Metro style --> Windows Runtime Component。

1. javascript调用C++类
windows runtime组件都会以类的形式给外部提供API,至少目前为止我还不知道是否可以封装成C风格函数的API。
看如下C++示例:
  1. namespace RuntimeC
  2. {
  3.     public ref class Test sealed
  4.     {
  5.     public:
  6.        Test() {}
  7.     };
  8. }
复制代码
js中的调用:
  1. var nativeObject = new RuntimeC.Test();
复制代码

很简单吧,通过new和命名空间名,直接访问类来实例化对象。组件中要提供可被javascript实例化的类则必须把该类定义为public ref(不理解public ref的请参考C++ /CX一文)


2.类方法名大小写
如果类中的暴露给外部的方法首字母是大写,那在javascript中调用时该方法首字母应该写为小写,比如:假设如上Test1类中有如下方法:
  1. void Func1() {}
复制代码
那么js中应该如下来调用:
  1. nativeObject.func1();
复制代码

3. 不同语言间的数据交互

大家都知道C++是强类型语言,那么C++传递数据给javascript时,对数据类型有什么要求呢?windows runtime中提供了一些内置类型来供多语言交互时使用,也就是说如果要封装给不同语言调用的接口,就要使用这些内置类型。

a. 基本数据类型(int,double等)
接口中可以直接使用基本类型来传递数据,它们会被自动转化成window runtime的内置类型int32,float64等。
  1. double Func2(double pa) {
  2.      return pa;
  3. }
复制代码
b. 返回结构体或类对象(C++ --> javascript)
如果Test类中有个方法,它的返回值是自定义的结构体,怎么做?
  1. public value struct DataStruct {
  2.      int value;
  3. };
  4. public ref class Test sealed
  5. {
  6. public:
  7.      Test() {
  8.      }
  9.      DataStruct Func3() {
  10.           DataStruct data;
  11.           return data;
  12.      }
  13. };
复制代码
注意,这里把DataStruct声明为public value,否则会出错,见C++ /CX。
javascript侧的调用:
  1. var data = nativeObject.func3();
复制代码
c. 函数参数类型为结构体或类 (javascript --> C++)
当组件中提供的函数参数类型为结构体和类时,也就意味着需要在javascript中把数据传递给C++,此时我们必须把参数定义为ref引用类型。
  1. public ref struct DataRef sealed {
  2. private:
  3.      int value;
  4. public:
  5.      DataRef() {}
  6.      property int val {
  7.           int get() {return value;}
  8.           void set(int v) {value = v;}
  9.      }
  10. };
  11. public ref class Test sealed
  12. {
  13. public:
  14.      Test() {
  15.      }
  16.      void Func4(DataRef^ data) {
  17.           data->val = 0;
  18.      }
  19. };
复制代码
由于需要从javascript侧传数据,所以需要把DataRef暴露出来,由js侧实例化DataRef对象并传递给C++。这里用到了property关键字,见C++ /CX一文。
javascript侧的调用如下:
  1. var nativeObject = new RuntimeC.Test();
  2. var obj = new RuntimeC.DataRef();
  3. nativeObject.func4(obj);
  4. var value = obj.val;
复制代码
d. Windows::Foundation
Windows::Foundation中提供了很多供不同语言交互使用的内置类型,
比如DateTime,假设在C++中要把当前时间返回给javascript:
  1. Windows::Foundation::DateTime Func5() {
  2.      auto cal = ref new Windows::Globalization::Calendar();
  3.      cal->SetToNow();
  4.      return cal->GetDateTime();
  5. }
复制代码
js侧,调用Func5方法会返回一个Date类型的对象:
  1. var date = nativeObject.func5();
  2. var day = date.getDate();
复制代码
又比如各种Collections,假设要在C++中清空js传过来的array类型数据:
  1. Windows::Foundation::Collections::IVector <int>^ ClearVector(Windows::Foundation::Collections::IVector<int>^ vec)     {
  2.      for (unsigned int i = 0; i < (vec->Size); i++) {
  3.           vec->SetAt(i, 0);
  4.      }
  5.      return vec;
  6. }
复制代码
js调用:
  1. var list = [1, 3, 2, 5];
  2. nativeObject.clearVector(list);
复制代码
4. 方法重载的限制
javascript中对C++中的函数重载形式有一定限制,比如它可以区分出如下的重载函数:
  1. int GetData(int i);
  2. int GetData(int i, MyType^ t);
复制代码
但是由于javascript是弱类型语言,它不能区分出如下C++支持的重载方式:
  1. int GetData(int i, MyType1^ t);
  2. int GetData(int i, MyType2^ t);
复制代码
所以在要重载对外的接口方法时,需要注意这一点。

5. 委托与事件(调用与回调)
关于delegate和event的详细说明请参照C++ /CX一文。这里主要关注C++和javascript之间的调用和回调,先看如下代码:
  1. public delegate void ChangedHandler();
  2. public ref class Test sealed
  3. {
  4. public:
  5.      Test() {}
  6.       void FireEvent() {
  7.           changedEvent();
  8.       }
  9.       event ChangedHandler^ changedEvent;
  10. };
复制代码
JS代码中绑定事件并调用FireEvent方法触发事件:
  1. var eventHandler = function (evt) {
  2.      ......
  3. };
  4. nativeObject.onchangedevent = eventHandler;
  5. nativeObject.fireEvent();
复制代码
通过onchangedevent把eventHandler方法绑定到changedEvent事件,当调用FireEvent触发事件后,js代码中eventHandler会被触发。(这里的onchangedevent是依据C++中的事件名changedEvent来决定的)

除了使用onchangedevent来绑定外,还可以使用addEventListener来绑定多个处理,如下所示:
  1. var eventHandler1 = function (evt) {
  2.      ......
  3. };
  4. var eventHandler2 = function (evt) {
  5.      ......
  6. };
  7. nativeObject.addEventListener("changedevent", eventHandler1);
  8. nativeObject.addEventListener("changedevent", eventHandler2);
  9. nativeObject.fireEvent();
复制代码

这里eventHandler1和eventHandler2都会被触发。注意,事件名里的大写字母在用addEventListener绑定是都应该用小写,比如在C++里事件为changedEvent,js绑定时则应该是"changedevent"。


最后,总结一下,在Metro app中封装能给不同语言使用的组件,必须基于windows runtime组件的封装原则,使用C++ /CX的一些扩展特性来封装外部方法。所以如果要复用以前的windows desktop app的通用组件,就需要为这些组件再包一层windows runtime的wrap。


 楼主| 发表于 2013-8-8 10:19:44 | 显示全部楼层
【windows8开发】现有代码移植到Metro App所必须的API整合

windows 8 推出在即,众多软件,代码都面临移植到这个新平台的问题,由于Metro App需要基于windows runtime framework来开发,而很多传统的windows API在winRT下并不被支持,所以本文会讨论哪些现存的windows API在Metro App中能被支持,哪些不被支持需要被替换,当然讨论的内容不会涵盖所有的API,只会把重点放在一些比较基本和常用的API,比如多线程相关,文件读写,同步与事件,网络编程等API。这里的API特指win32 API和Com API, 也就是C/C++风格的API。另外说明下,本文只关注API在Metro App中是否支持,不会讨论各个API的用法和细节,想了解这点的,自己google吧。

先说明下,有两个方法可以判断当前API是否支持Metro App。
第一个方法是查MSDN中相关API的说明,在页面中会有“Applies to”这么一行,如下所示:

Applies to: desktop apps | Metro style apps

它会告诉你当前API是否支持Metro App。
第二个方法是可以去查windows 8 SDK的源码,看包含相关API的头文件中会有如下指令:
#pragma region Application Family(同时支持desktop app 和 metro app)

#pragma region Desktop Family(仅支持desktop app)


好,下面开始进入具体的各类API。

1. 绘图API
Metro App中不再支持传统的GDI以及GDI+的API,而部分支持以下API:
Direct2D
Direct3D

DirectWrite

至于部分支持哪些,可以参照如下MSDN链接。
http://msdn.microsoft.com/en-us/library/windows/apps/br205756.aspx

当然作为替代,winRT下不管是XAML还是Html+JS都有各自支持的绘图接口。html下还支持html5的canvas,css3等特性。


2. 网络Socket API
很不幸 的告诉大家,Metro App中不支持传统的winsock的API,甚至不能一一对应的去找到大家耳熟能详的那一套socket编程接口,比如socket, bind, listen, accept, connect等。对于原本就是windows平台的代码或程序就需要使用WinRT新的API(Windows.Networking.Sockets),但对于本来是跨平台的一些库或应用,就要多费些手脚了,由于在WinRT中不能一一找到原来socket API的替代,势必需要重新调整原有的架构和一些封装形式。毕竟曾经的winsock跟posix socket风格还是很相似的,想象一下要把传统的linux/posix socket API和现在风格迥异的WinRT socket API封装成统一的跨平台接口还是有点工作量的。

当然,WinRT也支持一些当前比较热的特性,比如Websocket,比如访问Rest API 常用的XMLHttpRequest(Ajax请求)等。


3. 文件存储及管理
Metro App中由于存在对本地文件读取的限制,所以不再支持大家熟悉的fopen等一系列文件读写的API,只是部分支持一些如CreateFile2,CreateDirectory等API,还是推荐开发者使用WinRT的新API,比如:

文件读取可以使用Windows.Storage.Pickers.FileOpenPicker,Windows.Storage.Pickers.FileSavePicker等API,而存储管理,文件夹管理则可以使用Windows.Storage.StorageFile,Windows.Storage.StorageFolder下的API。


4. 线程

线程这块也有较大变化,也会给既有的代码带来一定的冲击,除了还暂时支持_beginthreadex和CreateThread外(之所以说暂时,是因为这个API并不在MSDN中列出所支持的范围,只是目前在release preview版本上还可以正常工作),其他如ResumeThread,SuspendThread,TerminateThread等都不再支持,而在WinRT中,Windows.System.Threading下提供了进行多线程开发的API。


5. 同步管理与事件
Metro App中不再支持如下常用的同步管理和事件的API:
CreateEvent
CreateSemaphore
InitializeCriticalSection
WaitForSingleObject
PulseEvent

但还是部分支持带Ex后缀的一些API,比如:
CreateEventEx
CreateSemaphoreEx
InitializeCriticalSectionEx
WaitForSingleObjectEx
InterlockedDecrement
InterlockedIncrement

详细请参照如下MSDN:
http://msdn.microsoft.com/en-us/library/windows/apps/br205762.aspx

6.Dll访问
Metro App中可以正常支持FreeLibrary和GetProcAddress,但不再支持LoadLibraryEx,作为替代,可以使用LoadPackagedLibrary来载入dll。

由此可见,如果要把现有程序整合到Metro App中,还是需要一些工作才可以做到的。在ARM-based的windows 8上由于只支持Metro App,对MS以外的应用更是直接限制了对原有win32 API的访问,可想而知这对很多外部第三方软件的冲击会有多大,由于MS的限制,Chrome,Firefox将不再能支持Arm版Win8,也就是说Arm平台的Win8只可能存在MS自己的浏览器,相信现在google,firefox们也在为MS这种变相垄断而咬牙切齿抱怨不已吧,而对我们开发者而言,能做的只有一个---妥协。
 楼主| 发表于 2013-8-8 10:20:30 | 显示全部楼层
【windows8开发】Metro App生命周期之概述

Metro App和传统的Desktop App虽然可以共存于Windows8中,但Metro App本身却是为Touch设备而生的。所以不管是其UE风格还是技术架构其实都跟传统的App存在很大差异。本文会概要性的介绍一下Metro App的生命周期有哪些状态,当然后续还会跟进其他一些文章来详细讨论生命周期中的各种状态以及相关程序中的一些细节。

先看一张图:

其实在《【windows8开发】javascript开发Metro风格App》 一文中曾经介绍过Metro App的生命周期,只是当时说明的内容比较简单。

从图中可以看出,Metro程序主要存在三个状态:Not Running,Running,Suspended。


1. 程序启动

用户启动后,程序并不是直接进入Running状态,而是先进入NotRunning状态,在此状态下如果程序发生crash或被suspended,则程序会被终止,不会保留在内存中。如果初始化正常,则程序会进入Running状态。


2. 程序挂起

由于Metro App都运行于全屏模式,当用户从当前程序切换到其他程序时,当前程序会被suspended,停止运行,仅保留于内存中。也就是说Metro模式下前台仅会有一个应用处于Running状态。这其实是考虑到节约系统资源,提高性能和用户体验以及节省设备能源(电量)。另外,当系统进入低电量状态时,程序也有可能被挂起(suspended)。


3. 恢复程序

当用户又切换回程序或者从低电量状态恢复回来时,相关程序会被Resume,重新进入Running状态。但是当程序被挂起时,系统也有可能因为需要释放内存或者节约电量而强行终止某些被挂起的程序。而且并不会在终止程序时通过事件通知程序。所以,一个稳定而又健全的程序应该在被suspended时持久化保存当前状态和必要的数据,以便在被迫终止后重启时还可以恢复之前的状态。


4. 关闭程序

Metro程序一般不需要由用户来关闭,可以完全交给系统来管理。当然用户也可以通过特定的手势(貌似我用鼠标试了下,关不掉,郁闷)或者按Alt+F4来关闭程序。需要注意的是,不应该由应用程序中的某些代码来终止程序,这样系统会当做是一次程序的Crash。同理,你也不可以在应用的UI中提供给用户关闭程序的按钮,此类程序应该不能通过App Store的审查。


5. 安装与卸载

开发者不再需要为Metro App开发安装或者卸载程序,仅需要把当前应用打包后上传至App Store。而用户可以直接从App Store下载到应用程序包,系统会根据根据相关信息安装应用程序。Metro模式下程序的安装或卸载都是对每位登录用户而言的,也就是说当你安装时仅仅是为自己这个登录的ID安装,其他用户并不能共享用这个应用,卸载时也是一样,只有所有安装了同一应用的用户都卸载了该应用,该应用才会从这台设备上真正完全删除。


本文只是概要性的介绍一些原理,程序生命周期内各个状态中的一些技术细节和程序实现会在这个系列的其他文章中继续说明,感兴趣的继续关注吧,别忘了顶一下。
 楼主| 发表于 2013-8-8 10:22:01 | 显示全部楼层
【windows8开发】Metro App生命周期之启动



前文介绍过Metro App的生命周期中的各个状态(NotRunning, Running, Suspended)以及概要性的说明了程序的启动,挂起,恢复,关闭等概念,而在本文中会通过相关代码,着重介绍Metro App在启动时的一些细节。本文中的示例代码会使用C++,因为都是基于WinRT,所以Javascript的实现的原理应该是类似的,如果有希望具体了解Javascript的实现方式的,可以在回复中留言。


前文中提到过,当用户启动Metro程序时,程序并不会马上进入Running状态,开始只是NotRunning状态,如果在此状态中程序发生异常导致crash或者被Suspended,则该程序会被终止而不会保留在内存中。从NotRunning状态切换到Running状态过程中,程序会显示一个splash screen,其实就是一张启动时等待用的图片,如下图:




在VS工程的manifest(.appxmanifest)文件中可以修改希望显示的splash图片,




显示Splash后,程序开始启动,这意味着可以开始执行程序的一些初始化工作,比如设定初期显示页面,事件绑定等。那么,在哪里能执行这些处理呢?在新建C++ Metro程序的工程后,可以看到有个App.xaml.h文件,里面会override一个OnLaunched方法,启动时,onLaunched方法会被调用,看下这个方法的具体实现:
  1. void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ pArgs)
  2. {
  3.      if (pArgs->PreviousExecutionState == ApplicationExecutionState::Terminated)
  4.      {
  5.           //TODO: Load state from previously suspended application
  6.      } else {
  7.           ......
  8.      }
  9.      // Create a page which is defined in MainPage.xaml
  10.      auto page = ref new MainPage();

  11.      // Place the page in the current Window and ensure that it is active
  12.      Window::Current->Content = page;
  13.      Window::Current->Activate();
  14. }
复制代码
在OnLaunched中可以通过其参数的PreviousExecutionState属性来获得程序之前的执行状态,被正常关闭了,还是被挂起了,还是被强制终止了等等,在Windows::ApplicationModel::Activation::ApplicationExecutionState中定义了这些状态的值。程序可以根据之前状态的值执行相应的操作,一般的逻辑如下:
PreviousExecutionState值为Running, 则还原数据
PreviousExecutionState值为ClosedByUser或NotRunning,则使用默认数据启动
PreviousExecutionState值为Running或Suspend,则一般不需要额外处理(Suspended后被Resume时,其实正常情况下OnLaunched并不会被调用)

由于程序被Suspended后系统可能会在一些情况下强制终止(Terminate)该程序,所以在PreviousExecutionState值为Terminated时,程序可以恢复之前Suspended时保存的数据和状态(在后续的文章《【windows8开发】Metro App生命周期之Suspended与Resume》中会详细讨论Suspend时如何保存当前状态,Resume时如何恢复等细节)。之后,还创建了MainPage.xaml中定义的UI page,并且设置为当前页,同时通过Activate函数来激活UI。


这里或许有人会有这样的疑问,从App.xaml.h来看,App这个类没有继承于任何类,那为什么会说override了OnLaunched方法呢?其实VS会根据工程中的App.xaml的配置另外生成App.g.h和App.g.cpp,在其中通过C++ /CX的关键字partial扩展定义了App类(编译的时候会跟App.xaml.h中的内容合并),而在App.g.h中可以看出,App类是继承于Windows::UI::Xaml::Application的,因此才有override一说。


另外,在Windows::UI::Xaml::Application中,还有一个方法OnActivated方法可以被重载,这里容易被误解的是,很多人会认为启动时OnActivated也会被触发,其实在程序一般的启动过程(用户正常打开程序)中,即使你重载了OnActivated方法,它也不会被调用,会被触发的只有OnLaunched方法,而OnActivated方法只有在一些特殊的启动过程中才会触发Activate事件,比如用户在另外一个程序中希望通过本程序来执行相关检索而启动本程序时,也就是非直接启动时。


还有一点需要注意的是,在OnLaunched方法中,程序初始化的过程必须在短时间(几秒)内完成,如果初始化处理会花费较长时间,比如需要连接远程网络,读取大数据块等,那么怎么来解决这个问题呢?
这里提供一种解决方案,首先在OnLaunched方法中不进行上述费时的处理,可以考虑在其中把程序设定好的Splash的图片在OnLaunched中通过自己的代码让它继续显示出来,让用户感觉到程序还在初始化过程中(其实程序已经执行完OnLaunched了)。
  1. // Create a page which is defined in SplashPage.xaml
  2. auto splashPage = ref new SplashPage();

  3. // Place the customized splash page in the current Window and ensure that it is active
  4. Window::Current->Content = splashPage;
  5. Window::Current->Activate();
复制代码
SplashPage是我们在新建的SplashPage.xaml中定义的Page,其内容可以跟工程设定的Splash Screen一样显示同一张图片,当然也可以根据需要设定为其他内容。在splashPage中可以绑定事件,通过异步任务来执行那些我们需要的长时间初始化的操作,当初始化执行完成后,触发事件来从SplashPage切换到程序的初始页面。


最后,总结一下,程序启动时,会先显示一个Splash Screen,期间程序在override的Application::OnLaunched方法中会基于之前的运行状态执行相应的处理以及一些其他的初始化工作,初始化完成后会显示初始UI,这就是Metro程序的启动过程。

 楼主| 发表于 2013-8-8 10:23:37 | 显示全部楼层
【windows8开发】Metro App生命周期之Suspended与Resume


先来看这样一个场景:我们打开一个Metro程序,进行了一系列的编辑工作,突然想要查看另外一个Metro程序,然后ALT+TAB切换到这个App,确认完需要查看的内容后想切换回原来那个正在编辑的程序,突然发现那个程序已经被系统终止了,重新打开发现所有已编辑的东西都没了,于是大呼一声,“这是什么破系统.......”。
呵呵,其实这不是系统的问题,而是这个应用程序应该解决的问题,本文会讨论Metro程序的Suspended与Resume,看看如何解决以上问题,当然还会有一些关于Suspended与Resume的引申,希望对大家能有所帮助。本文代码都使用C#来表述。


曾经在其它文章中简单说明过,什么时候程序会被Suspended?一般来说都是由于使用者切换到了其他的Metro程序,当然如果当前设备电量不足等情况下,也会被Suspended。其实并不是Metro程序被切换后就马上会进入Suspended状态,系统会经过一定时间才会挂起此程序(大概10s左右),在挂起前如果又切回该程序,则程序会继续保持在Running状态。另外,我尝试了下,貌似从Metro程序切换回Metro桌面时,之前的Metro程序是不会被Suspended的。
再回到开始谈到的那个问题,为什么程序会被无故终止呢?由于Suspended的程序都还是保留在内存中,当此类程序过多时会占用系统很多资源,当系统内存资源紧张时,就会强制去关闭某些Suspended的程序,所以为了防止这种情况,一个稳定安全的程序即使被强制终止,也要求能够恢复Suspended之前的数据和一些程序状态(熟悉移动开发的朋友可能都知道,ios或者Android都有类似的特性)。但是,在系统强制关闭Suspended的程序时,是没有事件来通知程序的,所以我们只能在响应到Suspended事件即将被Suspended时来进行数据和状态的备份处理,看一段代码:
  1. public sealed partial class MainPage : Page
  2. {
  3.      public MainPage()
  4.      {
  5.           this.InitializeComponent();
  6.           Application.Current.Suspending += new SuspendingEventHandler(OnSuspending);

  7.      }
  8.      private void OnSuspending(object sender, SuspendingEventArgs e)
  9.      {
  10.                //TODO: Save application state and release exclusive resources
  11.      }
  12. }
复制代码
从代码可以看到,可以通过Application的属性Suspending来绑定Suspended事件,如上代码所示,当程序被Suspended时,OnSuspended方法会被触发,我们可以在这里备份一些数据和状态,也可能需要释放一些占用的资源,比如关闭被打开的文件。WinRT提供了可供备份数据用的Storgae,我们可以用来按照Key-Vaule的形式保存我们希望的数据。
  1. Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
  2. roamingSettings.Values["status"] = "app status";
复制代码
但是需要注意的是,你必须再很短的事件内完成数据的备份工作,如果5s内没有完成处理,系统会认为被阻塞了而强制关闭该程序。
当程序在被Terminate之后又启动时,可以在App的OnLaunched方法中恢复之前的数据:
  1. protected override void OnLaunched(LaunchActivatedEventArgs args)
  2. {
  3.      ......
  4.      if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
  5.      {
  6.           //TODO: Load state from previously suspended application
  7.           Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
  8.           string status = (string)roamingSettings.Values["status"];
  9.      }
  10.      ......
复制代码
由于程序被Terminate了,所以又恢复时不会再触发Resume事件,而是触发OnLaunched。除了使用RoamingSettings外还可以使用LocalSettings来持久化数据,至于它们的区别会在后续其他文章中来介绍。还有,当你有一些处理不希望在程序被Suspended时打断时,就需要一些额外的考虑,比如播放音频,上传下载文件等。WinRT提供了一些API来完成这类功能,如BackgroundTransfer。

Suspended差不多讲完了,接着是Resume。其实大部分情况下,当Suspended的程序被重新Resume时是不需要做任何处理的,因为Suspended后程序还是都在内存中,不会丢失任何数据,Resume后也不需要执行额外的恢复工作。但是有些情况下,比如程序会跟Server建立Socket连接,当程序被Suspended后,长时间没有进行收发心跳包(heart beats)时,服务器可能会断开连接,所以在Resume时需要在判断连接已断开后重新尝试去连接服务器恢复通信。这里介绍下如何响应Resume事件,跟Suspending事件类似,也是通过Application的Resuming来绑定Resume的事件,代码如下:
  1. public sealed partial class MainPage : Page
  2. {
  3.     public MainPage()
  4.     {
  5.         this.InitializeComponent();
  6.         Application.Current.Suspending += new SuspendingEventHandler(OnSuspending);
  7.         Application.Current.Resuming += new EventHandler<Object>(OnResuming);
  8.     }
  9.     private void OnResuming(object sender, Object e)
  10.     {
  11.     }
  12.         ......
复制代码
如果需要,我们可以再OnResuming方法中恢复网络连接等处理。


Suspended和Resume到这里介绍完了,下回会介绍Metro App的安装部署与卸载,希望大家继续捧场。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2024-6-8 19:40

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表