65.9K
CodeProject 正在变化。 阅读更多。
Home

如何从 .NET 桌面应用调用 WinRT API

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (12投票s)

2012年9月11日

公共领域

6分钟阅读

viewsIcon

94027

downloadIcon

1423

介绍如何在 Windows 8 上创建调用 WinRT API 的 .NET 应用。

引言

本文介绍了在 Windows 8 中创建调用新的 **WinRT API** 的 .NET 桌面应用所需的步骤。

File > NewProject > Online > Templates > VisualBasic > WinRT Console Application Template

您只需要这些(需要 VS2012 和 Windows 8)。

什么是 WinRT API?

  1. WinRT API 是 **Windows 8 中引入的一系列新的、现代化的 API**。其中一些是专为“应用商店风格”应用程序(以前称为 Metro)的 XAML UI 设计的 UI API。其他 API 则提供了核心操作系统功能,如文件、流、位图。在许多情况下,.NET 和 Win32 已经提供了类似的功能。在某些情况下,WinRT API 更简洁、更优越。
  2. WinRT API 是您在**构建应用商店风格应用程序(Metro 应用)**时将主要使用的 API。这些应用商店风格的应用也被允许使用部分 .NET API、部分 Win32 API 和部分 COM API。通常,如果 .NET API 可用,您会使用它们,否则会回退到 WinRT API。
  3. WinRT API 是一个**跨语言 API**。微软为此付出了大量努力,以便从 JavaScript、C++、VB、C# 和 F# 轻松访问这些 API。

  4. WinRT API 是一种**新型的互操作方式**。它们存在于一种新型的程序集中,称为 **.WinMD**。您可以在 Win8 计算机上的 `c:\windows\system32\WinMetadata` 目录下找到这些程序集,也可以(如果您安装了 Windows SDK)在 `C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral` 目录下找到。此外,第三方也将生产自己的 WinMD。
  5. WinRT API 是**增强型的 COM**。特别是,WinMD 文件等同于旧的 COM 类型库(.TLB),但使用与 .NET 程序集相同的二进制格式存储。这意味着您可以使用 Redgate Reflector 等您喜欢的工具加载 .WinMD 文件进行检查。幸运的是,微软所做的额外工作使得使用 WinRT API 比使用 COM 更加容易。

随附的代码是一个 VB 控制台应用程序,演示了几个 WinRT API - StorageFolder(用于查看文件和文件夹)、BitmapDecoder(用于加载图像)、StreamSocketListener(用于处理 TCP 请求)。

从桌面应用使用 WinRT API 可能有两个原因:

  • 首先,如果您正在为 Windows 应用商店应用程序编写原型代码,有时先在控制台应用程序中进行原型设计和调试会更容易:设置断点、使用 `Console.WriteLine` 更方便,启动速度更快,样板代码更少等。
  • 其次,新的 WinRT API 简洁而现代化,在许多情况下优于替代方案。如果您愿意将代码的受众限制在 Windows 8 用户,那么它们就值得研究。

快速检查列表

或者,如果您想手动完成...如果您以前阅读过本文,只是来回顾步骤,那么以下就是:

卸载 .vbproj 或 .csproj

<TargetPlatformVersion>8.0</TargetPlatformVersion>

添加引用

Windows

  • System.Net.Http.dll(选择 4.0.0.0)
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll

项目级导入

  • System.IO
  • System.Runtime.InteropServices.WindowsRuntime

异步 Main

Sub Main
    MainAsync().GetAwaiter().GetResult()
End Sub 

Async Function MainAsync() As Task 
    ... async code goes here
End Function

入门

您需要 VS2012 和 Windows 8 才能使用此代码并遵循本文。我用 VB 编写了所有内容,但同样的技术在 C# 中也同样有效。

  1. 开始新项目
  2. 文件 > 新建 > 项目 > VisualBasic > Windows > ConsoleApplication

  3. 将您的项目标记为以 Windows 8 平台为目标(需要)。
  4. 这在 MSDN 上有记录,如何:使用引用管理器添加或删除引用,“核心子组”标题 - 藏在楼梯下方的文件柜里,上面贴着“当心老虎”的标志!

    • 卸载您的项目
    • 编辑它并添加一个目标框架指令
    • <TargetPlatformVersion>8.0</TargetPlatformVersion>

    • 重新加载您的项目。
  5. 添加引用
  6. AddReference > WindowsCore > Windows.winmd

    只有当您的项目设置为以 Windows 8.0 平台为目标时,“Windows”选项卡才会出现在 AddReference 对话框中。

    AddReference > 程序集 > Framework > System.Net.Http.dll(v4,不是 v2!)

    **AddReference** 以下文件

    • AddReference > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
    • AddReference > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
    • AddReference > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
    • AddReference > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
    • AddReference > 浏览 > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll

    **Project > Properties > References > ImportedNamespaces >** 以下命名空间

    • System.IO
    • System.Runtime.InteropServices.WindowsRuntime
    • 这些导入的命名空间是为了让您能够使用方便的扩展方法,如 IBuffer.ToArray() 和 IInputStream.AsStreamForRead()。

Sub Main 和 Async

大多数有趣的 WinRT API 都是异步的,因此它们必须放在异步方法中。但 `Sub Main` 本身不允许是异步的。所以我们这样操作:

Module Module1
    Sub Main() 
        MainAsync().GetAwaiter().GetResult()
    End Sub

    Async Function MainAsync() As Task
        ' code goes here...
    End Function
End Module  

您也可以写 `MainAsync().Wait()`,但这不如前者好。原因在于 `MainAsync()` 中可能出现的任何异常。如上所示,`GetAwaiter().GetResult()` 将显示该异常的详细信息,并允许您“[X] 如果抛出此异常则中断”。但 `Wait()` 只会显示一个 `AggregateException`。

顺便说一句,每次我们的代码在“await”后恢复时,它都可能在不同的线程上恢复。您可以通过编写 `AsyncPump.Run()` 方法来避免这种情况,如下文所述:http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

使用 WinRT API

StorageFolders。注意:您不能在控制台应用程序中使用 FilePickers,因为它们需要 UI!

Dim folder = Windows.Storage.KnownFolders.DocumentsLibrary
Dim opts As New Windows.Storage.Search.QueryOptions(
                      Windows.Storage.Search.CommonFileQuery.OrderByName, {".txt"})
Dim files = Await folder.CreateFileQueryWithOptions(opts).GetFilesAsync(0, 20)
For Each file In files
    Console.WriteLine(file.Path)
Next  

Streams。注意:当您处置 `IO.StreamReader` 时,它会自动处置它包装的底层流。

Using reader = New IO.StreamReader(Await files.First.OpenStreamForReadAsync())
    Dim txt = Await reader.ReadToEndAsync()
    Console.WriteLine(txt.Substring(0, 100))
End Using 

Graphics。顺便说一句,`Windows.Graphics.Imaging` 用于原始图形(可以在控制台应用程序中完成),而 `Windows.UI.Xaml.Media.Imaging` 用于应用商店风格应用程序中显示的图形(不能在控制台应用程序中完成)。

Dim pics = Await Windows.Storage.KnownFolders.PicturesLibrary.GetFilesAsync(
                         Windows.Storage.Search.CommonFileQuery.OrderBySearchRank, 0, 1)
Using stream = Await pics.First.OpenReadAsync()
    Dim decoder = Await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream)
    Console.WriteLine(decoder.OrientedPixelWidth & "x" & decoder.OrientedPixelHeight)
End Using 

HttpClient。实际上,这只是 .NET Framework(不是 WinRT)的一部分,并且是 `WebRequest` 和 `WebClient` 的推荐替代品。我之所以提到它,是因为它位于 `System.Net.Http.dll` 中,该文件在 Windows 应用商店项目中会自动引用,但在桌面项目中不会。

Dim client As New Net.Http.HttpClient()
Dim html = Await client.GetStringAsync("http://www.microsoft.com")
Console.WriteLine(html.Substring(0, 100)) 

WinRT Sockets。最后,这里有一个使用我最喜欢的语言特性——异步和 XML 字面量的更大型示例!请注意“using r=, w=”子句。`AsStreamForRead()` 生成一个 `IO.Stream`,在处置时它也会处置底层的 WinRT `Socket.InputStream`。而 `New IO.StreamReader` 生成的流在处置时会处置其底层流。写入器也是如此。因此,此代码中的所有六个(!)流都将正确处置。

Using server As New Windows.Networking.Sockets.StreamSocketListener
    Dim serverDoneSignal As New TaskCompletionSource(Of Object)
    AddHandler server.ConnectionReceived, 
        Async Sub(sender, args)
            Using r = New IO.StreamReader(args.Socket.InputStream.AsStreamForRead()),
                  w = New IO.StreamWriter(args.Socket.OutputStream.AsStreamForWrite())
                Dim request = ""
                While True
                    Dim s = Await r.ReadLineAsync() 
                    If String.IsNullOrWhiteSpace(s) Then Exit While
                    request &= s & vbCrLf 
                End While 
                Console.WriteLine("<--------" & vbCrLf & request)
                Dim body = <html>
                              <head>
                                   <title>Hello World</title>
                               </head>
                               <body>
                                   <%= Iterator Function()
                                          For i = 0 To 9
                                               Yield <p>Hello word #<%= i %></p>
                                           Next
                                       End Function() %>
                               </body>
                          </html> 
                Dim headers = "HTTP/1.0 200 OK" & vbCrLf &
                              "Content-Type: text/html; charset=utf-8" & vbCrLf & vbCrLf
                Await w.WriteAsync(headers)
                Await w.WriteAsync(body.ToString())
            End Using 
            serverDoneSignal.SetResult(Nothing)
        End Sub 
    Await server.BindServiceNameAsync("")
    Diagnostics.Process.Start("http://127.0.0.1:" & server.Information.LocalPort & "/")
    Await serverDoneSignal.Task
End Using  

免责声明

免责声明:尽管我在微软的 VB/C# 语言团队工作,但本文纯属个人业余作品,基于公开信息和实验——它不属于我的专业领域,是业余时间撰写的,微软和我均不对其准确性做任何声明。

© . All rights reserved.