如何从 .NET 桌面应用调用 WinRT API
介绍如何在 Windows 8 上创建调用 WinRT API 的 .NET 应用。
引言
本文介绍了在 Windows 8 中创建调用新的 **WinRT API** 的 .NET 桌面应用所需的步骤。
File > NewProject > Online > Templates > VisualBasic > WinRT Console Application Template
您只需要这些(需要 VS2012 和 Windows 8)。
什么是 WinRT API?
- WinRT API 是 **Windows 8 中引入的一系列新的、现代化的 API**。其中一些是专为“应用商店风格”应用程序(以前称为 Metro)的 XAML UI 设计的 UI API。其他 API 则提供了核心操作系统功能,如文件、流、位图。在许多情况下,.NET 和 Win32 已经提供了类似的功能。在某些情况下,WinRT API 更简洁、更优越。
- WinRT API 是您在**构建应用商店风格应用程序(Metro 应用)**时将主要使用的 API。这些应用商店风格的应用也被允许使用部分 .NET API、部分 Win32 API 和部分 COM API。通常,如果 .NET API 可用,您会使用它们,否则会回退到 WinRT API。
-
WinRT API 是一个**跨语言 API**。微软为此付出了大量努力,以便从 JavaScript、C++、VB、C# 和 F# 轻松访问这些 API。
- WinRT API 是一种**新型的互操作方式**。它们存在于一种新型的程序集中,称为 **.WinMD**。您可以在 Win8 计算机上的 `c:\windows\system32\WinMetadata` 目录下找到这些程序集,也可以(如果您安装了 Windows SDK)在 `C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral` 目录下找到。此外,第三方也将生产自己的 WinMD。
- 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# 中也同样有效。
- 开始新项目
- 将您的项目标记为以 Windows 8 平台为目标(需要)。
- 卸载您的项目
- 编辑它并添加一个目标框架指令
- 重新加载您的项目。
- 添加引用
- 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
System.IO
System.Runtime.InteropServices.WindowsRuntime
文件 > 新建 > 项目 > VisualBasic > Windows > ConsoleApplication
这在 MSDN 上有记录,如何:使用引用管理器添加或删除引用,“核心子组”标题 - 藏在楼梯下方的文件柜里,上面贴着“当心老虎”的标志!
<TargetPlatformVersion>8.0</TargetPlatformVersion>
AddReference > WindowsCore > Windows.winmd
只有当您的项目设置为以 Windows 8.0 平台为目标时,“Windows”选项卡才会出现在 AddReference 对话框中。
AddReference > 程序集 > Framework > System.Net.Http.dll(v4,不是 v2!)
**AddReference** 以下文件
**Project > Properties > References > ImportedNamespaces >** 以下命名空间
这些导入的命名空间是为了让您能够使用方便的扩展方法,如 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# 语言团队工作,但本文纯属个人业余作品,基于公开信息和实验——它不属于我的专业领域,是业余时间撰写的,微软和我均不对其准确性做任何声明。