CLR via C#:與WinRT組件互操作

基礎知識:如下所示:
1.WinRT組件使用由ECMA協會標準化的.NET元數據格式(ECMA-335)來描述其API,這些元數據會嵌入到擴展名爲.winmd的文件中。
2.RCW(運行時可調用包裝器)內部引用了WinRT組件。
3.CCW(COM可調用包裝器)內部引用了CLR對象。

CLR投射:CLR通過元數據將WinRT類型隱式投射成FCL類型。

框架投射:CLR通過代碼將WinRT類型顯式投射成FCL類型。具有以下特性:
1.可以使用WindowsRuntimeSystemExtensions類中的輔助函數來完成.NET異步調用WinRT。
2.可以使用WindowsRuntimeStorageExtensions和WindowsRuntimeStreamExtensions類中的輔助函數來完成.NET和WinRT之間互傳輸數據流。
3.可以使用WindowsRuntimeBufferExtensions類中的輔助函數來完成.NET和WinRT之間傳輸數據塊。

用C#定義WinRT組件:具有以下特性:
1.使用該WinRT組件時,一般會造成的額外性能損失和內存消耗。
2.使用ildasm的/project命令開關可以查看將WinRT類型投射成FCL等價類型之後的元數據。
3.C#實現WinRT組件的模板如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Metadata;

// The namespace MUST match the assembly name and cannot be "Windows"
namespace LearnCLR.WinRTComponents {
   // [Flags]  // Must not be present if enum is int; required if enum is uint
   public enum WinRTEnum : int {    // Enums must be backed by int or uint
      None,
      NotNone
   }

   // Structures can only contain core data types, String, & other structures
   // No constructors or methods are allowed
   public struct WinRTStruct {
      public Int32 ANumber;
      public String AString;
      public WinRTEnum AEnum;    // Really just a 32-bit integer
   }

   // Delegates must have WinRT-compatible types in the signature (no BeginInvoke/EndInvoke)
   public delegate String WinRTDelegate(Int32 x);

   // Interfaces can have methods, properties, & events but cannot be generic.
   public interface IWinRTInterface {
      // Nullable<T> marshals as IReference<T>
      Int32? InterfaceProperty { get; set; }
   }

   // Members without a [Version(#)] attribute default to the class's 
   // version (1) and are part of the same underlying COM interface
   // produced by WinMDExp.exe.
   [Version(1)]
   // Class must be derived from Object, sealed, not generic, 
   // implement only WinRT interfaces, & public members must be WinRT types
   public sealed class WinRTClass : IWinRTInterface {
      // Public fields are not allowed 

      #region Class can expose static methods, properties, and events
      public static String StaticMethod(String s) { return "Returning " + s; }
      public static WinRTStruct StaticProperty { get; set; }

      // In JavaScript 'out' parameters are returned as objects with each 
      // parameter becoming a property along with the return value
      public static String OutParameters(out WinRTStruct x, out Int32 year) {
         x = new WinRTStruct { AEnum = WinRTEnum.NotNone, ANumber = 333, AString = "Jeff" };
         year = DateTimeOffset.Now.Year;
         return "Grant";
      }
      #endregion

      // Constructor can take arguments but not out/ref arguments
      public WinRTClass(Int32? number) { InterfaceProperty = number; }

      public Int32? InterfaceProperty { get; set; }

      // Only ToString is allowed to be overridden
      public override String ToString() {
         return String.Format("InterfaceProperty={0}",
            InterfaceProperty.HasValue ? InterfaceProperty.Value.ToString() : "(not set)");
      }

      public void ThrowingMethod() {
         throw new InvalidOperationException("My exception message");

         // To throw a specific HRESULT, use COMException instead
         //const Int32 COR_E_INVALIDOPERATION = unchecked((Int32)0x80131509);
         //throw new COMException("Invalid Operation", COR_E_INVALIDOPERATION);
      }

      #region Arrays are passed, returned OR filled; never a combination
      public Int32 PassArray([ReadOnlyArray] /* [In] implied */ Int32[] data) {
         // NOTE: Modified array contents MAY not be marshaled out; do not modify the array
         return data.Sum();
      }

      public Int32 FillArray([WriteOnlyArray] /* [Out] implied */ Int32[] data) {
         // NOTE: Original array contents MAY not be marshaled in; 
         // write to the array before reading from it
         for (Int32 n = 0; n < data.Length; n++) data[n] = n;
         return data.Length;
      }

      public Int32[] ReturnArray() {
         // Array is marshaled out upon return
         return new Int32[] { 1, 2, 3 };
      }
      #endregion

      // Collections are passed by reference
      public void PassAndModifyCollection(IDictionary<String, Object> collection) {
         collection["Key2"] = "Value2";  // Modifies collection in place via interop
      }

      #region Method overloading
      // Overloads with same # of parameters are considered identical to JavaScript
      public void SomeMethod(Int32 x) { }

      [Windows.Foundation.Metadata.DefaultOverload]  // Attribute makes this method the default overload
      public void SomeMethod(String s) { }
      #endregion

      #region Automatically implemented event
      public event WinRTDelegate AutoEvent;

      public String RaiseAutoEvent(Int32 number) {
         WinRTDelegate d = AutoEvent;
         return (d == null) ? "No callbacks registered" : d(number);
      }
      #endregion

      #region Manually implemented event
      // Private field that keeps track of the event's registered delegates
      private EventRegistrationTokenTable<WinRTDelegate> m_manualEvent = null;

      // Manual implementation of the event's add and remove methods
      public event WinRTDelegate ManualEvent {
         add {
            // Gets the existing table, or creates a new one if the table is not yet initialized
            return EventRegistrationTokenTable<WinRTDelegate>
               .GetOrCreateEventRegistrationTokenTable(ref m_manualEvent).AddEventHandler(value);
         }
         remove {
            EventRegistrationTokenTable<WinRTDelegate>
               .GetOrCreateEventRegistrationTokenTable(ref m_manualEvent).RemoveEventHandler(value);
         }
      }

      public String RaiseManualEvent(Int32 number) {
         WinRTDelegate d = EventRegistrationTokenTable<WinRTDelegate>
            .GetOrCreateEventRegistrationTokenTable(ref m_manualEvent).InvocationList;
         return (d == null) ? "No callbacks registered" : d(number);
      }
      #endregion

      #region Asynchronous methods
      // Async methods MUST return IAsync[Action|Operation](WithProgress)
      // NOTE: Other languages see the DataTimeOffset as Windows.Foundation.DateTime
      public IAsyncOperationWithProgress<DateTimeOffset, Int32> DoSomethingAsync() {
         // Use the System.Runtime.InteropServices.WindowsRuntime.AsyncInfo's Run methods to 
         // invoke a private method written entirely in managed code
         return AsyncInfo.Run<DateTimeOffset, Int32>(DoSomethingAsyncInternal);
      }

      // Implement the async operation via a private method using normal .NET technologies
      private async Task<DateTimeOffset> DoSomethingAsyncInternal(
         CancellationToken ct, IProgress<Int32> progress) {

         for (Int32 x = 0; x < 10; x++) {
            // This code supports cancellation and progress reporting
            ct.ThrowIfCancellationRequested();
            if (progress != null) progress.Report(x * 10);
            await Task.Delay(1000); // Simulate doing something asynchronously
         }
         return DateTimeOffset.Now; // Ultimate return value
      }

      public IAsyncOperation<DateTimeOffset> DoSomethingAsync2() {
         // If you don't need cancellation & progress, use 
         // System.WindowsRuntimeSystemExtensions' AsAsync[Action|Operation] Task 
         // extension methods (these call AsyncInfo.Run internally)
         return DoSomethingAsyncInternal(default(CancellationToken), null).AsAsyncOperation();
      }
      #endregion

      // After you ship a version, mark new members with a [Version(#)] attribute
      // so that WinMDExp.exe puts the new members in a different underlying COM 
      // interface. This is required since COM interfaces are supposed to be immutable.
      [Version(2)]
      public void NewMethodAddedInV2() { }
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章