在 C++ 和 Visual Basic 中建立 Windows 執行階段元件
從 .NET Framework 4.5 開始,您可以使用 Managed 程式碼自行建立封裝在 Windows 執行階段元件中的 Windows 執行階段型别。您可以在 Windows 市集應用程式中,將元件與 C++、JavaScript、Visual Basic 或 C# 搭配使用。本文將說明建立元件的規則,並就某些層面討論 .NET Framework 對於 Windows 執行階段的支援。一般而言,該支援依設計應可讓 .NET Framework 程式設計人員清楚理解。但當您建立要與 JavaScript 或 C++ 搭配使用的元件時,您必須了解這些語言對於 Windows 執行階段的支援方式有何差異。
注意事項 |
---|
如果您要建立僅在 Windows 市集應用程式中與 Visual Basic 或 C# 搭配使用的元件,而元件中不含 Windows 市集控制項,請考慮使用 [類別庫 (Windows 市集應用程式)] 範本,而不要使用 [Windows 執行階段元件] 範本。簡單類別庫的限制較少。 |
本文章包含下列各節:
在 Windows 執行階段元件中宣告型别
偵錯您的元件
將 Windows 執行階段型別傳遞至 Managed 程式碼
將 Managed 型别傳遞至 Windows 執行階段
傳遞陣列
多載方法
實作 IStringable
非同步作業
擲回例外狀況
宣告和引發事件
後續步驟
宣告 Windows 執行階段元件中的型别
就內部而言,您的元件中的 Windows 執行階段型別可以使用 Windows 市集應用程式允許的任何 .NET Framework 功能。(如需詳細資訊,請參閱適用於 Windows 市集應用程式的 .NET 概觀。)就外部而言,您的型別成員只能公開其參數和傳回值的 Windows 執行階段型別。下列清單將說明從 Windows 執行階段元件公開的 .NET Framework 型別有何限制。
在您的元件中,所有公用型別和成員的欄位、參數和傳回值都必須是 Windows 執行階段型別。
這項限制也適用於您所建立的 Windows 執行階段型别以及 Windows 執行階段本身提供的型别。其中也包括多種 .NET Framework 型别。納入這些型别,是 .NET Framework 為了方便您在 Managed 程式碼中使用 Windows 執行階段而提供的支援:您的程式碼能夠使用常見的 .NET Framework 型别,而非基礎 Windows 執行階段型别。例如,您可以使用 .NET Framework 基本型別 (例如 Int32 和 Double)、特定的基礎型別 (例如 DateTimeOffset 和 Uri) 和一些常用的泛型介面型別,例如 IEnumerable<T> (在 Visual Basic 中為 IEnumerable(Of T)) 和 IDictionary<TKey,TValue>。(請注意,這些泛型型別的型別引數必須是 Windows 執行階段型別。)這將在本文稍後的將 Windows 執行階段型别傳遞至 Managed 程式碼與將 Managed 型别傳遞至 Windows 執行階段中討論。
公用類別與介面可包含方法、屬性與事件。您可以為您的事件宣告委派,或使用 EventHandler<T> 委派。公用類別或介面不可:
屬於泛型。
實作不是 Windows 執行階段介面的介面。(不過,您可以建立自己的 Windows 執行階段介面並加以實作。)
從不在 Windows 執行階段中的型别 (如 System.Exception 與 System.EventArgs) 衍生型别。
所有公用型別都必須具有符合組件名稱的根命名空間,且組件名稱的開頭不可以是 "Windows"。
注意事項 根據預設,Visual Studio 專案具有符合組件名稱的命名空間名稱。在 Visual Basic 中,此預設命名空間的 Namespace 陳述式不會顯示在您的程式碼中。
公用結構不可以有公用欄位以外的成員,且這些欄位必須是實值型別或字串。
公用類別必須是 sealed (在 Visual Basic 中為NotInheritable)。如果您的程式設計模型需使用多型,您可以建立公用介面,然後在必須是多型的類別上實作該介面。
偵錯您的元件
如果您的 Windows 市集應用程式與元件都是以 Managed 程式碼建立而成,則兩者可同時進行偵錯。
當您使用 C++ 在 Windows 市集應用程式中測試您的元件時,您可以同時對 Managed 程式碼和原生程式碼進行偵錯。預設值為僅限原生程式碼。
同時對原生 C++ 程式碼與 Managed 程式碼進行偵錯
為您的 Visual C++ 專案開啟捷徑功能表,然後選擇 [屬性]。
在屬性頁中的 [組態屬性] 下,選擇 [偵錯]。
選擇 [偵錯工具類型],然後在下拉式清單方塊中,將 [僅限原生] 變更為 [混合 (Managed 與原生)]。選擇 [確定]。
在原生與 Managed 程式碼中設定中斷點。
當您使用 JavaScript 在 Windows 市集應用程式中測試您的元件時,方案依預設會處於 JavaScript 偵錯模式。在 Visual Studio 和 Visual Studio Express 中,您無法同時偵錯 JavaScript 和 Managed 程式碼。
偵錯 Managed 程式碼,而不偵錯 JavaScript
為您的 JavaScript 專案開啟捷徑功能表,然後選擇 [屬性]。
在屬性頁中的 [組態屬性] 下,選擇 [偵錯]。
選擇 [偵錯工具類型],然後在下拉式清單方塊中,將 [僅限指令碼] 變更為 [僅限 Managed]。選擇 [確定]。
在 Managed 程式碼中設定中斷點,然後如常進行偵錯。
將 Windows 執行階段型別傳遞至 Managed 程式碼
如先前在宣告 Windows 執行階段元件中的型别一節中所說明,特定的 .NET Framework 型别可出現在公用類別成員的簽章中。這是 .NET Framework 為了方便您在 Managed 程式碼中使用 Windows 執行階段而提供的支援。其中包括基本型別以及某些類別和介面。從 JavaScript 或 C++ 程式碼使用您的元件時,請務必了解您的 .NET Framework 型别會如何對呼叫端顯示。如需 JavaScript 的範例,請參閱逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件。本節將討論常用的型別。
在 .NET Framework 中,基本型别 (如 Int32 結構) 具有許多有用的屬性和方法,例如 TryParse 方法。相對地,Windows 執行階段中的基本型別和結構就只有欄位而已。這些型別傳遞至 Managed 程式碼時,將會顯示成 .NET Framework 型別,而您可以如常使用這些 .NET Framework 型別的屬性和方法。下列清單將摘要說明在 IDE 中自動執行的替代:
對於 Windows 執行階段基本 Int32、Int64、Single、Double、Boolean、String (Unicode 字元的不可變集合)、Enum、UInt32、UInt64 與 Guid,在 System 命名空間中請使用相同名稱的型别。
對於 UInt8,請使用 System.Byte。
對於 Char16,請使用 System.Char。
對於 IInspectable 介面,請使用 System.Object。
如果 C# 或 Visual Basic 為其中任何型别提供了語言關鍵字,您就能改用該語言關鍵字。
除了基本型別以外,有些基本而常用的 Windows 執行階段型別在 Managed 程式碼中也會顯示為其 .NET Framework 對等項目。例如,假設您的 JavaScript 程式碼使用 Windows.Foundation.Uri 類別,而您要將其傳遞至 C# 或 Visual Basic 方法。在 Managed 程式碼中的對等型别會是 .NET Framework System.Uri 類別,而這就是要用於方法參數的型别。由於 Visual Studio 中的 IntelliSense 在您撰寫 Managed 程式碼時會隱藏 Windows 執行階段型别,並顯示對等的 .NET Framework 型别,因此您知道 Windows 執行階段型别何時會呈現為 .NET Framework 型别。(這兩個型別通常會有相同的名稱。但請注意,Windows.Foundation.DateTime 結構在 Managed 程式碼中會顯示成 System.DateTimeOffset,而不是 System.DateTime)。
對於某些常用的集合型别,會在 Windows 執行階段型别所實作的執行個體與對應的 .NET Framework 型别所實作的介面之間進行對應。如同先前提到的型别,您也可以使用 .NET Framework 型别來宣告參數型别。這會隱藏不同型別之間的某些差異,以方便撰寫 .NET Framework 程式碼。下表列出其中最常見的泛型介面型別,以及其他通用類別與介面的對應。如需 .NET Framework 對應之 Windows 執行階段型别的完整清單,請參閱 Windows 執行階段類型的 .NET Framework 對應。
Windows 執行階段 |
.NET Framework |
---|---|
IIterable<T> |
IEnumerable<T> |
IVector<T> |
IList<T> |
IVectorView<T> |
IReadOnlyList<T> |
IMap<K, V> |
IDictionary<TKey, TValue> |
IMapView<K, V> |
IReadOnlyDictionary<TKey, TValue> |
IKeyValuePair<K, V> |
KeyValuePair<TKey, TValue> |
IBindableIterable |
IEnumerable |
IBindableVector |
IList |
Windows.UI.Xaml.Data.INotifyPropertyChanged |
System.ComponentModel.INotifyPropertyChanged |
Windows.UI.Xaml.Data.PropertyChangedEventHandler |
System.ComponentModel.PropertyChangedEventHandler |
Windows.UI.Xaml.Data.PropertyChangedEventArgs |
System.ComponentModel.PropertyChangedEventArgs |
當一個型别實作多個介面時,您可以將它所實作的任何介面都當做成員的參數型别或傳回型别。例如,您可以將 Dictionary<int, string> (在 Visual Basic 中為 Dictionary(Of Integer, String)) 傳遞或傳回為 IDictionary<int, string>、IReadOnlyDictionary<int, string> 或 IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>。
重要
JavaScript 會使用 Managed 型別所實作的介面清單中第一個出現的介面。例如,如果您將 Dictionary<int, string> 傳回至 JavaScript 程式碼,它將會顯示為 IDictionary<int, string>,無論您將哪個介面指定為傳回型別。這表示,如果第一個介面不包含出現在後續介面上的成員,該成員即不會對 JavaScript 顯示。
在 Windows 執行階段中,IMap<K, V> 與 IMapView<K, V> 可透過 IKeyValuePair 反覆執行。當您將其傳遞至 Managed 程式碼時,它們會顯示成 IDictionary<TKey, TValue> 與 IReadOnlyDictionary<TKey, TValue>,以便您使用 System.Collections.Generic.KeyValuePair<TKey, TValue> 加以列舉。
介面在 Managed 程式碼中的顯示方式,會影響到實作這些介面之型别的顯示方式。例如,若 PropertySet 類別實作 IMap<K, V>,它在 Managed 程式碼中會顯示為 IDictionary<TKey, TValue>。就其顯示來看,PropertySet 像是實作了 IDictionary<TKey, TValue>,而不是 IMap<K, V>,因此它在 Managed 程式碼中會顯得具有 Add 方法,其運作方式如同 .NET Framework 字典上的 Add 方法。它看起來並沒有 Insert 方法。您可以在逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件一文中檢視此範例。
將 Managed 型别傳遞至 Windows 執行階段
如上一節所討論,某些 Windows 執行階段型别在您的元件成員的簽章中 (如果在 IDE 中使用這些型别,則為 Windows 執行階段成員的簽章中),會顯示為 .NET Framework 型别。當您將 .NET Framework 型别傳遞給這些成員,或以這些型别作為元件成員的傳回值時,它們在另一端的程式碼上便會顯示為對應的 Windows 執行階段型别。若想透過範例了解這在從 JavaScript 呼叫您的元件時將有何效用,請參閱逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件中的「從您的元件傳回 Managed 型别」一節。
傳遞陣列
Windows 執行階段中只有輸入參數或輸出參數,而沒有 ref 參數 (在 Visual Basic 中為 ByRef)。傳遞至您的 Windows 執行階段元件的陣列內容,必須有要輸入或輸出的預定用途。也就是說,不可將陣列視為可變動陣列。如果陣列是以傳值方式傳遞的 (在 Visual Basic 中為ByVal),則您必須套用 ReadOnlyArrayAttribute 屬性或 WriteOnlyArrayAttribute 屬性,以建立目的。請參閱傳遞陣列給 Windows 執行階段元件。
多載方法
在 Windows 執行階段中,方法可以是多載的。不過,如果您宣告多個具有相同參數數量的多載,則只能將 Windows.Foundation.Metadata.DefaultOverloadAttribute 屬性套用至其中一個多載。這就是您唯一可從 JavaScript 呼叫的多載。例如,在下列程式碼中,使用 int (在 Visual Basic 中為 Integer) 的多載是預設多載。
public string OverloadExample(string s)
{
return s;
}
[Windows.Foundation.Metadata.DefaultOverload()]
public int OverloadExample(int x)
{
return x;
}
Public Function OverloadExample(ByVal s As String) As String
Return s
End Function
<Windows.Foundation.Metadata.DefaultOverload> _
Public Function OverloadExample(ByVal x As Integer) As Integer
Return x
End Function
警告
JavaScript 可讓您將任何值傳遞至 OverloadExample,並將該值強制轉型為參數所需的型別。您可以使用 "forty-two"、"42" 或 42.3 來呼叫 OverloadExample,但這些值全都會傳遞至預設多載。前述範例中的預設多載分別會傳回 0、42 和 42。
您不能將 DefaultOverloadAttribute 屬性套用至建構函式。一個類別中的所有建構函式必須要有不同數量的參數。
實作 IStringable
從 Windows 8.1 開始,Windows 執行階段包含 IStringable 介面。這個介面的唯一方法 IStringable.ToString 與 Object.ToString 相比,可提供基本格式支援。如果您選擇在公用 Managed 類型中實作 IStringable,而這個類型又在 Windows 執行階段元件中匯出,那麼下列限制即成立:
您只能在「類別實作」關聯性中定義 IStringable 介面,例如:
public class NewClass : IStringable
在 C# 中,或
Public Class NewClass : Implements IStringable
在 Visual Basic 中。
您無法針對介面實作 IStringable。
您不能宣告型別為 IStringable 的參數。
IStringable 不能是方法的傳回型別、屬性或欄位。
您不能藉由使用如下所示的方法定義,從基底類別隱藏自己的 IStringable 實作:
public class NewClass : IStringable { public new string ToString() { return "New ToString in NewClass"; } }
相反地,IStringable.ToString 實作一律必須覆寫基底類別實作。您只能藉由針對強型別類別執行個體叫用 ToString 實作來隱藏該實作。
請注意,在各種情況下,從機器碼傳送至實作 IStringable 或隱藏其 ToString 實作的 Managed 類型可能會導致非預期的行為。
非同步作業
若要在您的元件中實作非同步方法,請在方法名稱結尾處加上 "Async",並傳回代表非同步動作或作業的其中一個 Windows 執行階段介面:IAsyncAction、IAsyncActionWithProgress<TProgress>、IAsyncOperation<TResult> 或 IAsyncOperationWithProgress<TResult, TProgress>。
您可以使用 .NET Framework 工作(Task 類別和泛型 Task<TResult> 類別) 執行您的非同步方法。您必須傳回代表進行中作業的工作,例如,從使用 C# 或 Visual Basic 撰寫的非同步方法傳回的工作,或是從 Task.Run 方法傳回的工作。如果您使用建構函式建立這項工作,則必須先呼叫其 Task.Start 方法,再加以傳回。
使用 await (在 Visual Basic 中為 Await) 的方法,必須要有 async 關鍵字 (在 Visual Basic 中為 Async)。如果您從 Windows 執行階段元件公開此類方法,請將 async 關鍵字套用至您傳遞給 Run 方法的委派。
對於不支援取消或進度報告的非同步動作與作業,您可以使用 WindowsRuntimeSystemExtensions.AsAsyncAction 或 AsAsyncOperation<TResult> 擴充方法,將工作包覆在適當的介面中。例如,下列程式碼會使用 Task.Run 方法啟動工作,以實作非同步方法。AsAsyncOperation<TResult> 擴充方法會以 Windows 執行階段非同步作業的形式傳回工作。
public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
return Task.Run<IList<string>>(async () =>
{
var data = await DownloadDataAsync(id);
return ExtractStrings(data);
}).AsAsyncOperation();
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
As IAsyncOperation(Of IList(Of String))
Return Task.Run(Of IList(Of String))(
Async Function()
Dim data = Await DownloadDataAsync(id)
Return ExtractStrings(data)
End Function).AsAsyncOperation()
End Function
下列 JavaScript 程式碼示範如何使用 WinJS.Promise 物件呼叫方法。當非同步呼叫完成時,便會執行傳遞至 then 方法的函式。stringList 參數會包含 DownloadAsStringAsync 方法所傳回的字串清單,而該函式會執行任何必要的處理。
function asyncExample(id) {
var result = SampleComponent.Example.downloadAsStringAsync(id).then(
function (stringList) {
// Place code that uses the returned list of strings here.
});
}
對於支援取消或進度報告的非同步動作與作業,請使用 AsyncInfo 類別產生啟動的工作,並且將該工作的取消和進度報告功能與 Windows 執行階段介面的取消和進度報告功能相連結。如需支援取消與進度報告的範例,請參閱逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件。
請注意,即使您的非同步方法不支援取消或進度報告,您仍可使用 AsyncInfo 類別的方法。如果您使用 Visual Basic Lambda 函式或 C# 匿名方法,請不要提供語彙基元和 IProgress<T> 介面的參數。如果您使用 C# Lambda 函式,請提供語彙基元參數,但加以忽略。上一個範例中使用了 AsAsyncOperation<TResult> 方法,如果改用 AsyncInfo.Run<TResult>(Func<CancellationToken, Task<TResult>>) 方法多載,將如下所示:
public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
return AsyncInfo.Run<IList<string>>(async (token) =>
{
var data = await DownloadDataAsync(id);
return ExtractStrings(data);
});
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
As IAsyncOperation(Of IList(Of String))
Return AsyncInfo.Run(Of IList(Of String))(
Async Function()
Dim data = Await DownloadDataAsync(id)
Return ExtractStrings(data)
End Function)
End Function
如果您建立選擇性支援取消或進度報告的非同步方法,請考慮新增不為取消語彙基元 IProgress<T> 或介面使用參數的多載。
擲回例外狀況
您可以擲回適用於 Windows 市集應用程式的 .NET 應用程式開發介面 中所包含的任何例外狀況型别。您無法在 Windows 執行階段 元件中宣告自己的公用例外狀況型别,但是可以宣告和擲回非公用型別。
如果您的元件未處理例外狀況,呼叫該元件的程式碼中將會引發對應的例外狀況。例外狀況對呼叫端的顯示方式,取決於呼叫的語言支援 Windows 執行階段的方式。
在 JavaScript 中,例外狀況會顯示為物件,其中例外狀況訊息會被堆疊追蹤取代。當您在 Visual Studio 中偵錯應用程式時,可以在偵錯工具的 [例外狀況] 對話方塊中看到標示為「WinRT 資訊」的原始訊息文字。您無法從 JavaScript 程式碼存取原始訊息文字。
注意事項 雖然堆疊追蹤目前包含 Managed 例外狀況型別,但是不建議透過剖析追蹤的方式來辨識例外狀況型別,而是改用 HRESULT 值 (本節稍後會加以說明)。
在 C++ 中,例外狀況會顯示為平台例外狀況。如果 Managed 例外狀況的 HResult 屬性可以對應至某特定平台例外狀況的 HRESULT,就會使用該特定例外狀況,否則會擲回 Platform::COMException 例外狀況。C++ 程式碼無法使用 Managed 例外狀況的訊息文字。如果是擲回特定平台例外狀況,則會顯示該例外狀況型別的預設訊息文字,否則不會顯示任何訊息文字。請參閱例外狀況 (C++/CX)。
在 C# 或 Visual Basic 中,例外狀況就是一般的 Managed 例外狀況。
當您從自己的元件擲回例外狀況時,為了讓 JavaScript 或 C++ 呼叫者更容易處理此例外狀況,可以擲回非公開的例外狀況型別,並且將其 HResult 屬性值設定為自己元件專屬的值。JavaScript 呼叫者可以透過例外狀況物件的 number 屬性存取 HRESULT,而 C++ 呼叫者則可以透過 COMException::HResult 屬性來存取。
注意事項 |
---|
請使用負值做為 HRESULT 的值。負值會被解譯為成功,如此在 JavaScript 或 C++ 呼叫者中,就不會擲回任何例外狀況。 |
宣告和引發事件
當您宣告某型別包含事件的資料時,請從 Object 衍生,而不要從 EventArgs 衍生,因為 EventArgs 不是 Windows 執行階段型別。請使用 EventHandler<TEventArgs> 做為事件的型别,並以您的事件引數型別作為泛型型別引數。引發事件的方式與 .NET Framework 應用程式中相同。
從 JavaScript 或 C++ 使用您的 Windows 執行階段元件時,事件會依循這些語言所預期的 Windows 執行階段事件模式。當您從 C# 或 Visual Basic 使用元件時,事件會顯示為一般的 .NET Framework 事件。逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件中提供了相關範例。
如果您實作自訂事件存取子 (在 Visual Basic 中為使用 Custom 關鍵字宣告事件),則您在實作時必須依循 Windows 執行階段事件模式。請參閱Windows 執行階段元件中的自訂事件和事件存取子。請注意,當您處理來自 C# 或 Visual Basic 程式碼的事件時,事件仍會顯示為一般的 .NET Framework 事件。
後續步驟
在您建立要供自己使用的 Windows 執行階段元件後,可能會發現它封裝的功能對其他開發人員也很有用。您可以選擇兩種方式來包裝元件並發佈給其他開發人員。請參閱散發 Managed Windows 執行階段元件。
如需 Visual Basic 和 C# 語言功能以及 Windows 執行階段之 .NET Framework 支援的詳細資訊,請參閱 Visual Basic 和 C# 語言參考。
請參閱
概念
適用於 Windows 市集應用程式的 .NET 應用程式開發介面
逐步解說:以 C# 或 Visual Basic 建立簡單的元件,然後從 JavaScript 呼叫該元件