Windows 运行时组件

.NET 领域内的 Windows 运行时组件

Jeremy Likness

下载代码示例

称为一个 Windows 存储应用程序的新类型 — — 优化运行在 Windows 8 的设备 — — 具有默认的视图与全屏窗口和没有分散注意力的铬,所以内容是协调中心。 Windows 存储应用程序支持流动布局的调整和扩展到各种各样的屏幕大小和分辨率。 它们提供触摸首次体验,同时为传统键盘和鼠标提供充分支持。

在一套新的 Api 调用 Windows 运行库 (WinRT) 上运行 Windows 存储的应用程序。 Windows 运行时公开作为 Windows 8 操作系统以及第三方组件,您可以开发自己的一部分生成的组件。 虽然一些核心 Windows 运行时组件都可以从桌面应用程序访问,第三方 Windows 运行时组件是只可从 Windows 8 环境内。 WinRT 类型的介绍使用 WinRT 元数据文件具有.winmd 扩展名。 这些文件使用同样的标准 Microsoft.NET 框架使用,为类,ECMA 335 提供元数据定义和语义编码 (见 bit.ly/sLILI)。

可以通过更改为 windows (通常 c:\windows\system32) 包含系统文件的目录的快速导航到 Windows 8 的计算机上的类型定义。 一个文件夹内的所有­称为 WinMetadata 的保守党包含所有的类型定义。 可以使用 ILDasm.exe 实用程序来浏览类型。 打开 Visual Studio 2012 命令提示符下,导航到 c:\windows\system32\WinMetadata 文件夹和在命令行中键入以下:

Ildasm.exe windows.web.winmd

您应该看到类似的结果图 1。 可以使用 ILDasm.exe 实用程序检查的所有命名空间和类型为该特定的 Windows 运行时组件定义。

Inspecting WinRT Types
检查 WinRT 类型的图 1

值得注意的是有没有代码包含在文件 ; 只有元数据信息是可用的。 该组件是底层操作系统的一部分。 使用本机代码编写的它是最有可能。 一种称为语言投影的独特功能允许 Windows 运行时组件 (本机和托管) 访问从 Windows 存储应用程序开发支持的任何语言。

投影和映射

许多语言,包括 C#、 Visual Basic、 c + + 和 JavaScript,已通过 Windows 8,以支持语言投影进行更新。 这允许 Windows 运行时组件中使用多种语言的自然方式访问。 投影处理公开 WinRT 类型作为对象或类是本机开发 Windows 存储应用程序所使用的语言。 下面的代码从使用 C# 生成 Windows 存储应用程序直接访问本机 Windows 运行时组件:

var ui = new CameraCaptureUI();

CameraCaptureUI 是一个 Windows 运行时组件。 该组件不是托管的 C# 类型,但它可以轻松地访问和好像它是,从 C# 代码中引用。 这是因为 CLR 为使用它的元数据的 Windows 运行时组件自动生成运行时可调用包装 (RCW),使它显示为一个本机到托管代码的 CLR 类型。 更多关于这,在看到 MSDN 库条,"运行库可调用包装," bit.ly/PTiAly。 Rcw 的功能使得它容易和简单与这些组件进行交互。 反之也是如此。 投影使 Windows 运行时组件创建与托管代码来引用像 c + + 类型从本机代码和作为一个 JavaScript 对象从 HTML/JavaScript 项目内。

基本类型自动显示为 C# 的类型。 Windows 运行时的 ELEMENT_TYPE_STRING 类型出现在.NET 代码作为字符串对象中。 ELEMENT_TYPE_I4 标量显示为 Int32。 CLR 还将采取某些 WinRT 类型并将其映射为它们的等效.NET 将显示在代码中。 例如,大小固定的集合的 WinRT 类型是 IVector <T>,但这种类型将自动显示为 IList <T> 在.NET 代码。 WinRT HRESULT 显示.NET 框架中的异常类型。 CLR 将自动封送托管和本机表示形式之间的这些类型。 某些类型,如流,可以使用一组由 CLR 提供的扩展方法显式转换。 以这种方式映射的类型的完整列表,在参考 MSDN 开发中心主题,".NET 框架的映射的 WinRT 类型," bit.ly/PECJ1W

这些内置功能使开发人员能够创建 C# 和 Visual Basic 中使用托管的代码自己 Windows 运行时组件。 Visual Studio 2012 从 Visual Basic、 C# 和 c + + 创建 Windows 运行时组件提供一个模板。 这些组件可以消耗,并从支持 Windows 运行时,包括 JavaScript 的任何其他语言中调用。 为此,您必须遵循一些特定的规则,在 C# 中创建一个 Windows 运行时组件。

游戏规则

一般情况下,在 C# 中创建 WinRT 类型的规则与任何公开可见的类型和成员您的组件提供了有关。 因为 Windows 运行时组件必须受 WinRT 类型系统,将存在限制。 MSDN 开发中心主题,"创建 Windows 运行时组件在 C# 和 Visual Basic 中,"列出完整的规则集是在 bit.ly/OWDe2A。 字段、 参数和返回值,您公开必须全部是 WinRT 类型 (它是精细,公开将自动映射到 WinRT 类型的.NET 类型)。 您可以创建您自己的 WinRT 类型公开提供这些类型,反过来,遵循相同的规则集。

另一个限制是任何公共类或接口您公开不能是泛型或执行任何非 WinRT 接口。 他们不必须从非 WinRT 类型派生。 Windows 运行时组件的根命名空间必须匹配的程序集名称,又不能启动的"窗口"。公共结构也限制要只是值类型的公共字段。 多态性没有到 WinRT 类型,并且最近你可以来实现 WinRT 接口 ; 任何由您的 Windows 运行时组件公开的类必须声明为密封。

这些限制可能会考虑替代方式,将您的应用程序中的组件,尤其是如果您处理的旧式代码,将需要进行重大重构的理由。 我稍后将讨论可能的替代办法。 重要的是要确保 Windows 运行时组件可以适当地在 WinRT 环境中的功能和可以将引用并从所有的语言环境,包括 c + + 和 JavaScript 调用限制。

缩略图生成器

一个简单的应用程序将演示如何使用 C# 创建一个托管的 Windows 运行时组件和消耗从用 C#、 JavaScript 或 c + + 生成 Windows 存储应用程序。 该组件接受对 WinRT IStorageFile 接口所传递的图像文件的引用。 然后,创建一个 100 x 100 像素的图像的缩略图,并将其保存到 Windows 存储应用程序的本地存储。 最后,它返回一个指向缩略图的 URI。 所涉及的步骤包括:

  1. Visual Studio 2012 年创建解决方案。
  2. 生成 Windows 运行时组件。
  3. 创建 C#、 JavaScript 或 c + + 语言特定的项目。
  4. Windows 运行时组件的引用。
  5. 设置每个项目的 UI,使用户选取一个图像。
  6. 显示的图像。
  7. 调用 Windows 运行时组件。
  8. 显示缩略图。

创建项目和解决方案

从内 Visual Studio 2012 年从你开始,指定 (在本例中,C#) 选择的语言,然后选择 Windows 存储应用程序模板。 专门为生成 Windows 运行时组件存在的模板。 选择此模板,并创建一个称为 ThumbnailLibrary 使用相同名称的解决方案的组件,如中所示图 2

Creating the Windows Runtime Component Project
图 2 创建 Windows 运行时组件项目

对于此示例,创建名为 ThumbnailMaker 的单个类。 私有方法返回任务以异步方式生成缩略图:

private static async Task<Uri> GenerateThumbnail(IStorageFile file)
{
}

在方法内的第一步是从存储中打开文件,并使用 WinRT BitmapDecoder 解码图像流:

using (var fileStream = await file.OpenReadAsync())
{
  var decoder = await BitmapDecoder.CreateAsync(fileStream);
}

下一步,app 举行缩略图的本地存储区中创建一个文件。 它将与源文件相同的扩展名命名"缩略图"。 生成一个唯一的名称的选项将确保可以生成多个缩略图,而不会覆盖前面的操作:

var thumbFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
  string.Format("thumbnail{0}", file.FileType),
  CreationCollisionOption.GenerateUniqueName);

从解码流创建编码器。 它只是缩放到 100 x 100 像素的位图,然后将它写入到文件系统:

using (var outputStream =
  await thumbFile.OpenAsync(FileAccessMode.ReadWrite))
{
  var encoder = await BitmapEncoder.CreateForTranscodingAsync(
    outputStream,
    decoder);
  encoder.BitmapTransform.ScaledHeight = 100;
  encoder.BitmapTransform.ScaledWidth = 100;
  await encoder.FlushAsync();
}

最后一步是建立一个指向文件的 URL。 特别 ms appdata 前缀用于引用本地存储中的文件。 若要了解有关如何使用 Uri,阅读 MSDN 开发中心主题,"如何的参考内容"的参考内容的在 bit.ly/SS711o。 虽然这一专题是 HTML 和 JavaScript,用于访问资源的公约 》 是相同无论您使用何种语言选项:

const string URI_LOCAL = "ms-appdata:///Local/{0}";
var storageUrl = string.Format(URI_LOCAL, thumbFile.Name);
return new Uri(storageUrl, UriKind.Absolute);

用 C# 编写的 Windows 运行时组件可以使用 Windows 存储应用程序配置文件允许任何.NET 功能。 如前所述,然而,公共类型和接口必须只公开 WinRT 类型。 因为任务并不是有效的 WinRT 类型,该组件的公共方法必须公开 WinRT IAsyncOperation <T> 键入即可。 幸运的是,扩展方法存在,轻松地将.NET 任务类型转换为 WinRT IAsyncOperation 的类型,如下所示:

public IAsyncOperation<Uri> GenerateThumbnailAsync(IStorageFile file)
{
  return GenerateThumbnail(file).AsAsyncOperation();            
}

与组件现在完成,您可以编译,使它准备好的消费从 Windows 存储的应用程序。

根据胡德:“日期”

生成 Windows 运行时组件并通过右击解决方案资源管理器中的项目名称并选择"打开 Windows 资源管理器中的文件夹",然后导航到输出目录当您定位到 bin/调试的子目录时,你会发现,名为 ThumbnailLibary.winmd 的组件生成的元数据文件。 如果您使用 ILDasm.exe 打开该文件,您将看到一个接口已生成为具有返回类型的组件:

Windows.Foundation.IAsyncOperation <Windows.Foundation.Uri>

那些被映射为组件的 WinRT 类型。 它是可能还检查元数据,请参阅 CLR 对 WinRT 类型的项目。 像这样的特殊 /project 扩展名打开同一个元数据文件:

Ildasm.exe ThumbnailLibrary.winmd /project

返回类型现在显示为:

Windows.Foundation.IAsyncOperation <System.Uri>

请注意 URI 的 WinRT 版本预计为.NET 等效。 方法签名公开所有有效 WinRT 类型用于 Windows 存储的应用程序来消费,但从托管代码类型将显示为.NET 类。 您可以使用 /project 扩展检查投影将如何影响托管和非托管 Windows 运行时组件的签名。

从 C# 消耗

因为它是比引用类库没有什么不同,消费从 C# 组件应似乎熟悉。 请注意如果您唯一的目标是在其他托管的代码生成 Windows 运行时组件没有理由。 你简单地引用 WinRT 项目,然后使用类,如您将从普通 C# 类库。 在代码示例中,CSharpThumbnails 项目已对 ThumbnailLibrary 的引用。 主页面 XAML 定义一个按钮,供用户从中选择一张照片,并包含两个图像来主机的原始图像和缩略图版本。 图 3 显示基本的 XAML 标记。

图 3 XAML 的 Windows 应用程序商店用 C# 生成

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
  </Grid.RowDefinitions>
  <TextBlock
    Text="Tap the button below to choose an image to generate a thumbnail."
    Style="{StaticResource BodyTextStyle}"
    Margin="12"/>
  <Button Content="Pick Image" Grid.Row="1" Margin="12"
    Click="Button_Click_1"/>
  <TextBlock Text="Thumbnail:" Style="{StaticResource BodyTextStyle}"
    Grid.Row="2" Margin="12"/>
  <Image x:Name="ThumbnailImage" HorizontalAlignment="Left" Stretch="None"
    Grid.Row="3" Margin="12"/>
  <TextBlock Text="Source Image:" Style="{StaticResource BodyTextStyle}"
    Grid.Row="4" Margin="12"/>
  <Image x:Name="SourceImage" HorizontalAlignment="Left" Stretch="None"
    Grid.Row="5" Margin="12"/>
</Grid>

代码隐藏创建 WinRT FileOpenPicker 组件的一个实例,并将它配置为浏览图像:

var openPicker = new FileOpenPicker
{
  ViewMode = PickerViewMode.Thumbnail,
    SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");

机械臂被称为,如果找不到有效文件,将显示一个简单的对话框:

var file = await openPicker.PickSingleFileAsync();
if (file == null)
{
  var dialog = new MessageDialog("No image was selected.");
  await dialog.ShowAsync();
  return;
}

源映像然后有线进行显示。 文件传递给 Windows 运行时组件生成一个缩略图,并传递回的 URI 用来设置显示的缩略图图像:

 

using (var fileStream = await file.OpenReadAsync())
{
  SourceImage.Source = LoadBitmap(fileStream);
  var maker = new ThumbnailMaker();
  var stream = RandomAccessStreamReference
    .CreateFromUri(await maker.GenerateThumbnailAsync(file));
  var bitmapImage = new BitmapImage();
  bitmapImage.SetSource(await stream.OpenReadAsync());
  ThumbnailImage.Source = bitmapImage;
}

图 4 显示的这对一张照片,我花了我刻南瓜的运行结果。

Windows Store Thumbnail App Built Using C#
图 4 使用 C# 生成 Windows 存储缩略图应用程序

从 JavaScript 消耗

与常规的 C# 类库,不同可以从创建 Windows 存储应用程序 (核心操作系统的一部分的 Windows 运行时组件可以从桌面应用程序以及调用) 支持任何语言中调用 Windows 运行时组件。 若要查看此行动中,可以创建使用 HTML 和 JavaScript 的缩略图的应用程序。 这一项目中附带的示例代码下载调用 JavaScriptThumbnails。 第一步是创建空应用程序使用空白的 Windows 存储模板使用 JavaScript 构建的应用程序。 一旦创建了模板,您可以使用简单的 HTML 标记来定义使用现有的 default.html 文件的页:

<p>Tap the button below to choose an image to generate a thumbnail.</p>
<button id="pick">Pick Image</button>
<br/><br/>
<p>Thumbnail:</p>
<img id="thumbnail" src="images/logo.png" alt="Thumbnail"/>
<p>Source Image:</p>
<img id="sourceImage" src="images/logo.png" alt="Image"/>

接下来,就像为常规 C# 项目添加对 WinRT 项目 (ThumbnailLibrary) 的引用。 生成项目,以便您可以使用智能感知为新引用的组件。 您可以引用该项目才能看到的 JavaScript 等效的打开文件选择器,然后选择该图像的源代码。 要创建托管 Windows 运行时组件的一个实例、 生成缩略图并显示给用户,请使用下面的 JavaScript:

var thumb = new ThumbnailLibrary.ThumbnailMaker();
thumb.generateThumbnailAsync(file).then(function (fileUrl) {
  var thumbImage = document.getElementById("thumbnail");
  thumbImage.src = fileUrl.rawUri;
  thumbImage.alt = thumbImage.src;
});

正如您所看到的是 C# 项目中使用的几乎相同的 API 调用。 投影自动更改方法签名为 Pascal 大小写为 camel 大小写 (调用生成缩略图小写字符开头,如是在 JavaScript 代码中的常见公约),以及一个名为"诺言"的特殊库用于处理异步使用然后或完成的语句的代码的性质。 您可以通过阅读 MSDN 开发中心主题,了解更多有关承诺"快速入门:使用的承诺,"在 bit.ly/OeWQCQ。 图像标记现成的支持 Url,所以回从 Windows 运行时组件传递的 URL 只需设置直接到图像上的 src 属性。

在 JavaScript 代码中使用托管的组件的重要一点是您不能在同一时间调试 JavaScript 代码和托管的代码。 如果您需要调试您的组件,必须用鼠标右键单击该项目和选择调试选项卡,然后选择包含托管的代码的调试选项。 如图 5 所示。

Setting the Debug Options for a JavaScript Project
图 5 设置一个 JavaScript 项目的调试选项

从 c + + 消费

您还可以使用本机项目从托管的 Windows 运行时组件。 C + + 共享同一呈现引擎作为 C# 中,因此 CPlusPlusThumbnails 项目具有相同的 CSharpThumbnails 项目作为 XAML。 代码隐藏是不同的因为该项目使用本机 c + + 语言选项。 C + + 使用特殊并发库来处理异步操作。 您可以了解更多有关此库通过阅读 MSDN 开发中心主题,"异步编程在 c + +,"在 bit.ly/MUEqnR。 生成的代码类似于您在 JavaScript 版本中看到的承诺:

ThumbnailMaker^ maker = ref new ThumbnailMaker();
create_task(maker->GenerateThumbnailAsync(file)).then([this](Uri^ uri)
{
  RandomAccessStreamReference^ thumbnailStream =
    RandomAccessStreamReference::CreateFromUri(uri);
  create_task(thumbnailStream->OpenReadAsync()).then([this](
    IRandomAccessStream^ imageStream) {
    auto image = ref new BitmapImage();
    image->SetSource((IRandomAccessStream^)imageStream);
    ThumbnailImage->Source = image;
  });
});

当应用程序运行时,你会发现它的外观和行为具有相同的 C# 版本。

了解的代价

创建使用托管的语言的 Windows 运行时组件是一个功能强大的功能。 但是,此功能不会付出了代价,和它是重要的是要了解的代价,当您在项目中使用它。 使用本机代码生成 Windows 存储应用程序不需要,CLR 运行。 这些应用程序可能会直接在 Windows 8 的环境中运行。 同样,还使用 JavaScript 开发的应用程序不需要 CLR 的依赖。 他们依靠的三叉戟渲染引擎和脉轮 JavaScript 引擎 (相同引擎该驱动器互联网浏览器 10) 呈现的 HTML 和 CSS 和 JavaScript 代码解释。 用 JavaScript 生成 Windows 存储应用程序可以直接,调用本机 Windows 运行时组件,但当它们调用托管的 Windows 运行时组件时,他们就对 CLR 的依赖性。

为托管 Windows 运行时组件编写的代码将已编译的实时 (JIT) 时,CLR JIT 编译器第一次访问它。 这可能会导致一些延迟第一次访问。 预编译的服务称为 NGen 手柄编译模块安装在设备上,但它可以采取全天最终编译所有模块封装一旦安装了。 CLR 还通过执行垃圾回收管理内存。 垃圾回收 (GC) 堆分为三代,并收集只使用旨在优化性能的一种算法的堆的部分。

它在执行工作时,GC 可能暂停您的应用程序。 这往往只引入了不可识别给最终用户,稍有延迟和更强烈的垃圾回收操作往往可以在后台运行。 如果您有一个足够大堆 (当您的代码的托管的部分引用数百兆字节或更多内存对象中),垃圾回收可能暂停足够长的用户意识到缺乏响应的应用程序。

这些因素大部分已到位时您正在构建一个托管的 Windows 存储应用程序。 您正在将其添加到 Windows 程序商店建与 c + + 或 JavaScript 时,托管的代码不添加新的关切。 它是重要的是要认识到您的应用程序会占用额外的 CPU 和内存中引入时托管组件。 它还可能采取可识别的性能影响,取决于该组件 (虽然许多应用程序上没有任何明显的影响的托管引用)。 好处是,你不必担心自己管理内存和,当然,您可以利用旧代码和技能。

托管项目的的替代品

如果您正在构建 Windows 存储应用程序使用托管的代码 (C# 或 Visual Basic),你有几个备选方案来创建 Windows 运行时组件,不具有相同的限制。 您可以轻松创建可重用的组件使用一个简单的 C# 类库。 如果类库生成用于 Windows 存储的应用程序中,您可以从您的 Windows 应用程序商店引用项目。 班库的建立也会删除不必公开只有 WinRT 类型和不能够使用的功能,不是 WinRT 的类型系统,例如泛型的一部分的限制。

另一种选择来考虑是便携式类图书馆 (PCL)。 这是库的一个特殊类型的类可以从各种平台无需重新编译引用。 使用此选项,如果你有代码你想分享的其他平台之间 — — 例如 Windows 演示文稿基金会、 Silverlight 和 Windows Phone — — 与您的 Windows 存储应用程序。 您可以了解更多有关 PCL 通过阅读我的三个部分的博客系列"理解便携式库的追逐 ICommand,"在 bit.ly/pclasslib

当您的组件包括不止是代码时,则可以考虑创建一个扩展 SDK。 这是一种特殊形式的 Visual Studio 2012 可以视为单个项目的 SDK。 包可能包含源代码、 资产、 文件和甚至二进制文件,其中包括 Windows 运行时组件。 您还可以创建设计时扩展,以使它更易于消耗和使用您的组件从 Visual Studio 2012 内。 扩展的 Sdk 无法发布到 Windows 存储区,因为他们不是独立的应用程序。 您可以了解更多有关扩展 Sdk 通过阅读 MSDN 库条中,"如何:创建一个软件开发工具包"在 bit.ly/L9Ognt

何时创建托管的 Windows 运行时组件

很多可能的替代办法,与以往任何时候都有意义吗来创建 Windows 运行时组件使用托管的代码? 是的 — — 但是,请考虑以下问题。 问到第一个问题是您是否需要您要从 Windows 存储区使用 JavaScript 或本机代码中使用 c + + 编写的应用程序引用的组件。 如果这不是这种情况,没有任何理由类库和其他选项将转而工作时使用 Windows 运行时组件。 如果这种情况,您必须创建一个 Windows 运行时组件,可从所有可用的语言选项。

接下来的问题是是否应在 c + + 创建您的组件或使用托管的代码。 有大量使用托管的代码的原因。 一个原因可能是您的团队是 C# 或 c + + 中比 Visual Basic 中经验更丰富,可以利用现有的技能,将生成组件。 另一个原因可能是您有现有的算法将会更容易端口,如果你保持相同的语言选择托管语言编写的。 有一些可能会更容易地编写和维护而不使用本机代码中,使用托管的语言和类库的任务,习惯于在托管语言发展的团队将更为有益。

总结,在本文中,您已经了解您可以创建可重复使用 Windows 运行时组件使用托管 C# 和 Visual Basic 代码。 这些组件可以方便地引用和消耗从 Windows 存储应用程序任何语言,包括 JavaScript 和 c + + 编写的。 重要的是要了解创建 Windows 运行时组件,并选择要使用托管的代码的影响的规则时,此选项提供了一个独特的机会,以使用您选择的语言,并利用现有的代码,以创建可由所有 Windows 存储应用程序的组件。

Jeremy Likness  *是在亚特兰大 Wintellect LLC 的主要顾问。 他是三年微软 Silverlight MVP 和几本书,包括即将"建筑 Windows 8 的应用程序与 C# 和 XAML"(艾迪生 - 韦斯利专业、 2012年) 的作者。 在更多在线学习 bit.ly/win8design ,并按照他在博客上的 csharperimage.jeremylikness.com。   *

衷心感谢以下技术专家对本文的审阅:莱拉德里、 刷新、 约翰 · 加兰、 杰夫 Prosise 和杰弗里 · 里氏