http://www.ithov.com/article/118831.shtml
对于 Windows 8,我们彻底颠覆改造了平台,您可以选择您已了解的编程语言和技术来构建为设备和外形因素定制的应用。而对于 Windows 运行时,您甚至可以在单一应用中轻松地使用多种语言。通过使用 C++ 来构建您自有的 Windows 运行时组件,您可以通过可与 Xbox 360 控制器交互的 HTML 和 JavaScript 来构建出色的 Metro 风格应用。您可以构建通过 Windows 运行时组件公开的可重用 XAML 控件,这些控件可供使用 C++ 和 C# 编写的 Metro 风格应用即时使用。实质上,我们已允许您在 Windows 8 平台上使用您选择的语言并以毫不迁就的方式来构建应用。
在本篇博文中,我们将讨论构建 Windows 运行时组件所需了解的必要知识。
基础知识
Windows 运行时是实现语言选择的核心。它自身是公开的,从而您可以从 JavaScript、C++、C# 和 Visual Basic 中以自然而熟悉的方式对其进行调用。这种基础方法也同样适用于构建您自有的 API。
您在应用中构建和打包的 Windows 运行时组件通常被称作第三方 Windows 运行时组件。这与已作为 Windows 8 平台中一部分的第一方组件有所不同。您可以使用 C++、C# 或 Visual Basic 编写这些第三方 Windows 运行时组件。您可以从任何位置调入它们公开的 API,包括打包到您的应用中的其他 Windows 运行时组件。您也可以使用任何语言来调入通过 Windows 运行时组件公开的 API。
您为应用编写的 Windows 运行时组件可使用 Windows 运行时 API、Win32、COM、.NET API 或第三方库(只要它们支持 Metro 风格应用开发)。请注意您所构建的 Windows 运行时组件与传统意义上公开 API 的 C++ DLL 或 .NET 程序集并不相同。使用 .Net 创建类库或者使用 C++ 创建独立的 DLL 与构建 Windows 运行时组件完全不同。Windows 运行时组件在公开 Windows 运行时元数据的 .wnmd 文件中声明,并且允许 JavaScript 等语言来自然地使用 Windows 运行时 API(例如,向 JavaScript 公开的 API 的 pascalCasedNames 支持)。Windows 运行时元数据还允许 Visual Studio 提供出色的工具功能,如 IntelliSense 支持。
为何要构建您自有的 Windows 运行时组件
创建 Windows 运行时组件可帮助您对可重用性和语言互操作性进行设计。我们来看一下展示如何使用第三方 Windows 运行时组件来构建更佳体验的一些应用程序方案。
在您的 Metro 风格应用中使用 Win32 和 COM API
Internet Explorer 10 提供的平台允许您使用 HTML、CSS 和 JavaScript 创建出色的 Metro 风格应用体验。但是,如果您已使用 HTML5 Canvas 构建游戏,并希望与 Windows 的 Xbox 360 控制器相集成,那情况会怎样?允许应用从控制器接收输入的 XInput API 会将不可用的 Win32 API 直接向 JavaScript 公开。
这是通过创建 Windows 运行时组件来解决该问题并使您能够在基于 HTML 的 Metro 风格应用中使用 XInput API 的经典示例。XInput 和 JavaScript 控制器草图示例准确地展现了这一点。该示例应用包含一个游戏控制器 Windows 运行时组件,它使用 C++ 编写并用于包装 XInput API 公开的功能。该控制器草图基于 HTML 的应用使用游戏控制器 C++ Windows 运行时组件来实现与 Xbox 360 控制器的交互。
这一方案(无法单独使用 HTML 和 JavaScript 实现)是通过创建第三方 Windows 运行时组件来完成通过其他方法无法完成的复杂方案的完美示例。
大计算量的操作
为诸如科学、工程和地图/地理等字段创建应用通常需要大计算量的操作。这些大量的操作通常需要强大的并行处理且非常适合于使用 C++ 获得最佳性能。在开发 Bing 地图旅行优化器(使用 JavaScript 和 C++ 开发的 Metro 风格应用)中,我们看到了另一种方案,即使用 C++ 创建 Windows 运行时组件使我们可以创建最佳的应用体验。
您可能会考虑为何用户完全使用本地数据计算旅行路线,而他们可以在云端的 Bing 服务器上运行这种大计算量的操作。Bing 地图为公开执行此操作的 JavaScript API,而有时应用必须脱机运行。另外,我们也希望用户通过触控实时拖动和更改路线。如果我们在本地运行此类大量的操作,则我们需要创建甚至更好的体验。
通过使用并行任务类库而用 C++ 编写大计算量的操作,我们可以利用客户端的强大功能来为用户创建出色的使用体验。Windows 运行时可完美适用于该方案,它允许我们使用 Bing 地图 AJAX 控件来创建使用 HTML 和 JavaScript 的丰富用户界面 (UI),而使用 C++ 代码运行的所有大量路线操作可通过并行计算提高计算的速度。
库
社区中拥有着大量而出色的库,开发人员已进行汇总并与广大用户共享。在过去,您可能会认为重用其中的一些库可能很具挑战性,如果它们与实现您的应用的编程语言不匹配的话。例如,您构建了一个出色的 .NET 应用,但必须完成痛苦的互操作蓝框(如 PInvoke),才能使用通过 C++ 编写的库。
Windows 运行时可以弥合 Windows 8 中的语言分歧,使包含单个基本代码的单一 Windows 运行时组件库可以扩展至更广泛的开发人员,无论组件的语言或应用的主要编程语言是什么。
现在,您可以创建一个可供全部 C++ 和 C# 开发人员使用的公开 Windows 运行时的单一 XAML 自定义控件。您可以在您的基于 XAML 或 HTML 的 Metro 风格应用中使用由开发人员共享的各种数据存储 Windows 运行时库。所有这些方案均无需编写互操作代码即可实现。
我们认为 Windows 运行时将惠及由开发人员创建并与社区中广泛的 Metro 风格应用��发人员共享的各种库。现在,我们来看看两个展示使用 C++/CX 和 C# 构建第三该 Windows 运行时组件的具体示例。
应用场景 1:通过本机音频来增强您的应用
假设我们要使用拥有 C# 编写的应用逻辑支持的 XAML 来构建一个软件合成器应用。为了向我们的音乐应用添加过滤器,我们将使用 XAudio 来直接控制音频缓冲器。
将 Windows 运行时组件添加到我们的解决方案
使用 Visual Studio,我们可以将全新的 C++ Windows 运行时组件项目添加到我们现有的解决方案。该 Windows 运行时组件包括音乐处理功能:
图 2:添加一个全新的 C++ Windows 运行时组件
Visual Studio 为我们创建了一个 C++ 项目,用于公开 API,而 API 的实现将打包到一个 DLL 文件,而 Windows 运行时元数据将打包到 winmd 文件中。它们全部可用于我们的 C# 项目。
定义向我们的 XAML C# 项目公开的类
我们使用 C++/CX 来构建向 C# 项目公开的 API,但您也可以使用 Windows 运行时 C++ 模板库 (WRL)。我们首先定义一个非常基本的类以封装 XAudio 功能:
XAudioWrapper.h
#pragma once
#include "mmreg.h"
#include <vector>
#include <memory>
namespace XAudioWrapper
{
public ref class XAudio2SoundPlayer sealed
{
public:
XAudio2SoundPlayer(uint32 sampleRate);
virtual ~XAudio2SoundPlayer();
void Initialize();
bool PlaySound(size_t index);
bool StopSound(size_t index);
bool IsSoundPlaying(size_t index);
size_t GetSoundCount();
void Suspend();
void Resume();
private:
interface IXAudio2* m_audioEngine;
interface IXAudio2MasteringVoice* m_masteringVoice;
std::vector<std::shared_ptr<ImplData>> m_soundList;
};
}
首先,您必须注意类声明中 public、ref 和 sealed 等关键字的使用。对于通过 JavaScript 或 C# 等其他语言在 Metro 风格应用中实例化的类来说,该类必须声明为 public ref class sealed。
类的公共功能(方法、属性等)仅限于 C++ 内置的类型或 Windows 运行时类型。这些是可在 Windows 运行时组件中跨越语言界限的唯一类型。但话虽如此,您仍然可以将常规的 C++ 库(即标准模板库集合)用于您的类中的专用数据成员,如在该代码断中所示。这些专用数据成员无需遵从与跨越语言界限有关的规则。如果您使用不支持的构造,则 Visual Studio 编译器将发出错误消息并提供相应的指导。
实现在我们的 Windows 运行时组件中公开的类
现在,我们已定义了类的基本接口,接下来让我们来看看一些实现方法:
XAudioWrapper.cpp
XAudio2SoundPlayer::XAudio2SoundPlayer(uint32 sampleRate) :
m_soundList()
{
// Create the XAudio2 engine
UINT32 flags = 0;
XAudio2Create(&m_audioEngine, flags);
// Create the mastering voice
m_audioEngine->CreateMasteringVoice(
&m_masteringVoice,
XAUDIO2_DEFAULT_CHANNELS,
sampleRate
);
}
void XAudio2SoundPlayer::Resume()
{
m_audioEngine->StartEngine();
}
bool XAudio2SoundPlayer::PlaySound(size_t index)
{
//
// Setup buffer
//
XAUDIO2_BUFFER playBuffer = { 0 };
std::shared_ptr<ImplData> soundData = m_soundList[index];
playBuffer.AudioBytes = soundData->playData->Length;
playBuffer.pAudioData = soundData->playData->Data;
playBuffer.Flags = XAUDIO2_END_OF_STREAM;
HRESULT hr = soundData->sourceVoice->Stop();
if (SUCCEEDED(hr))
{
hr = soundData->sourceVoice->FlushSourceBuffers();
}
//
// Submit the sound buffer and (re)start (ignore any 'stop' failures)
//
hr = soundData->sourceVoice->SubmitSourceBuffer(&playBuffer);
if (SUCCEEDED(hr))
{
hr = soundData->sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
}
return SUCCEEDED(hr);
}
在该代码段中,我们使用可用于 Metro 风格应用开发的 XAudio2 COM API 来连接我们的音频引擎、播放声音和恢复引擎。另外,我们还可以使用 C++ 构造和除 Windows 运行时类型以外的其他类型来实现必要的功能。
添加和使用 Windows 运行时组件
在定义和实现基本类之后,我们使用 Visual Studio 来将 XAudioWrapper Windows 运行时组件从我们的 C# 项目添加到 C++ 项目:
图 3:将 XAudioWrapper Windows 运行时组件添加到我们的音乐应用
因此,我们从 C++ 项目公开的类可用于我们的 C# 项目:
MainPage.cs
using XAudioWrapper;
namespace BasicSoundApp
{
public sealed partial class MainPage : Page
{
XAudio2SoundPlayer _audioPlayer = new XAudio2SoundPlayer(48000);
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_audioPlayer.Initialize();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_audioPlayer.PlaySound(0);
}
}
}
如在该代码段中所示,我们可以像使用常规 .NET 组件那样与 C# 中的 XAudio 封装程序进行交互。我们将引用其命名空间、举例说明组件并开始调用其公开的各种方法。所有这一切不需要任何 DllImport 来调入本机代码!
应用场景 2:使用内置的 API 在您的应用中打开 zip 文件
假设我们使用 HTML 来构建文件查看器应用,并希望添加功能来允许该应用的用户选取 zip 文件。我们希望将 Windows 中已内置并在 .NET 平台中公开的 API 用于处理 zip 文件。
将 Windows 运行时组件添加到我们的解决方案
该步骤与我们在音乐应用中所描述的步骤完全相同,现在我们将选取 C# Windows 运行时组件来包装 zip 处理功能:
图 4:添加一个全新的 C# Windows 运行时组件
Visual Studio 为我们创建了一个 C# 项目,用于公开 API,而 API 的实现和 Windows 运行时元数据都将打包到 .winmd 文件,并且可用于我们的 Web 项目。
实现在我们的 Windows 运行时组件中公开的类
我们使用 C# 来构建将向我们的 Web 项目公开的 API,但您同样可以使用 Visual Basic。我们首先定义一个简单的 C# 类来封装 zip 功能:
ZipWrapper.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage;
public sealed class ZipWrapper
{
public static IAsyncOperationWithProgress<IList<string>, double> EnumerateZipFileAsync(StorageFile file)
{
return AsyncInfo.Run(async delegate(
System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
{
IList<string> fileList = new List<string>();
progress.Report(0);
using (var stream = await file.OpenStreamForReadAsync())
{
using (var archive = new ZipArchive(stream))
{
for (int i = 0; i < archive.Entries.Count; i++)
{
// add code for processing/analysis on the file
// content here
// add to our list and report progress
fileList.Add(archive.Entries[i].FullName);
double progressUpdate = ((i + 1) / ((double)archive.Entries.Count)) * 100; // percentage
progress.Report(progressUpdate);
}
}
}
progress.Report(100.0);
return fileList;
});
}
}
该类是公共的且已密封。类似于构建 C++ Windows 运行时组件,这对于其他语言实例化类来说是必需的。该类中公开的静态方法混合使用 Windows 运行时类型(例如,StorageFile),并且在方法签名中作为 .NET 类型(例如,IList)。我们的经验法则是使用 Windows 运行时类型来定义向其他语言公开的公共字段、参数和返回类型。尽管如此,您仍然可以原封不动地使用某些 .NET 基本类型(即 DateTimeOffset 和 Uri)以及基元(即 IList)。
您还会注意到上述方法利用了在定义您的 Windows 运行时组件时可以(且应该)使用的用于异步性和进度支持的 Windows 运行时基础结构。就该类中的 Windows 运行时组件或任何专用功能的实现而言,您并不仅限于使用 Windows 运行时类型和 API;您可以自由使用任何向 Metro 应用开发公开的 .NET API 表面,如代码段 ZipArchive API 中所示。
添加和使用 Windows 运行时组件
我们现在已经实现了 zip 工具封装程序,我们将使用 Visual Studio 来添加我们的 JavaScript 项目中对 C# 项目的引用:
图 5:将 ZipUtil Windows 运行时组件添加到我们的文件查看器应用
因此,我们从 C# 项目公开的类将可用于我们的 Web 项目:
program.js
function pickSinglePhoto() {
// Create the picker object for picking zip files
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
openPicker.fileTypeFilter.replaceAll([".zip"]);
// Open the picker for the user to pick a file
openPicker.pickSingleFileAsync().then(function (file) {
if (file) {
ZipUtil.ZipWrapper.enumerateZipFileAsync(file).then(
function (fileList) {
for (var i = 0; i < fileList.length; i++)
document.getElementById('output').innerHTML += " " + fileList[i];
},
function (prog) {
document.getElementById('zipProgress').value = prog;
}
);
} else {
document.getElementById('output').innerHTML = "an error occurred";
}
});
};
正如您所看到的,我们可以与 JavaScript 中我们的 zip 工具封装程序进行交互,如同它是一个常规的 JavaScript 对象。我们可以调入 Windows 运行时组件中公开的静态方法,而我们使用 JavaScript 的异步语言构造,如 .then(),也可以完成此操作。
一般指导
并非您为 Metro 风格应用编写的所有 API 都应公开为第三方 Windows 运行时组件。通常情况下,当您在不同的编程语言之间进行通信时需要使用 Windows 运行时组件类型,并使用语言中内置的类型和构造来实现无法通过 Windows 运行时组件全局公开的功能。另外,跨越语言界限还涉及各种语言特定的功能和规则,当构建 Windows 运行时组件时,您必须将其纳入考虑范围。它们具体包括代理和事件、异步操作、方法重载和处理特定的数据类型(例如,集合、异常处理和调试技巧)。您可以通过访问构建 Windows 运行时组件中面向您的开发语言的部分,来深入研究这些主题。
总结
就 Windows 运行时组件而言,您现在可以混合使用编写语言和 API 技术来构建您设想的应用。毫不迁就的理念贯穿于 Windows 8。即使是在开发阶段情况依然如此,我们允许您混合使用和匹配适用于您的方案的最佳编程语言。我希望这将使您能够用更多的时间来考虑创新,而不是在学习全新的编程语言方面浪费大量的时间。
希望您能充分享受构建应用为您带来的快乐!
-- Windows 项目经理 Ines Khelifi