From 000c0d3a2b60c5eaafc8f43484f17ec666a7624e Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 11 Oct 2025 18:21:10 -0700 Subject: [PATCH 01/43] Add support for IList --- src/cswinrt/code_writers.h | 63 +++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 07af9bec9..162e2c01c 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2741,6 +2741,14 @@ protected %(WindowsRuntimeActivationFactoryCallback.DerivedSealed activationFact write_static_members(w, type); } + void write_nongeneric_enumerable_members_using_static_abi_methods(writer& w, std::string const& objref_name) + { + w.write(R"( +IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => global::ABI.System.Collections.IEnumerableMethods.GetEnumerator(%); +)", +objref_name); + } + void write_nongeneric_enumerable_members(writer& w, std::string_view target) { w.write(R"( @@ -3025,6 +3033,53 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); element, enumerable_type, target); } + void write_nongeneric_list_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) + { + auto self = emit_explicit ? "global::System.Collections.IList." : ""; + auto icollection = emit_explicit ? "global::System.Collections.ICollection." : ""; + auto visibility = emit_explicit ? "" : "public "; + auto target = "global::ABI.System.Collections.IListMethods"; + + w.write(R"( +%int %Count => %.get_Count(%); +%bool %IsSynchronized => false; +%object %SyncRoot => this; +%void %CopyTo(Array array, int index) => %.CopyTo(%, array, index); +% +%object %this[int index] +{ +get => %.Indexer_Get(%, index); +set => %.Indexer_Set(%, index, value); +} +%bool %IsFixedSize => false; +%bool %IsReadOnly => false; +%int %Add(object value) => %.Add(%, value); +%void %Clear() => %.Clear(%); +%bool %Contains(object value) => %.Contains(%, value); +%int %IndexOf(object value) => %.IndexOf(%, value); +%void %Insert(int index, object value) => %.Insert(%, index, value); +%void %Remove(object value) => %.Remove(%, value); +%void %RemoveAt(int index) => %.RemoveAt(%, index); +)", +visibility, icollection, target, objref_name, +visibility, icollection, +visibility, icollection, +visibility, icollection, target, objref_name, +!emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("NonGenericListItem")])" : "", +visibility, self, +target, objref_name, +target, objref_name, +visibility, self, +visibility, self, +visibility, self, target, objref_name, +visibility, self, target, objref_name, +visibility, self, target, objref_name, +visibility, self, target, objref_name, +visibility, self, target, objref_name, +visibility, self, target, objref_name, +visibility, self, target, objref_name); + } + void write_nongeneric_list_members(writer& w, std::string_view target, bool include_enumerable, bool emit_explicit) { auto self = emit_explicit ? "global::System.Collections.IList." : ""; @@ -3218,7 +3273,7 @@ visibility, self, objref_name, objref_name, visibility, self, objref_name); } - void write_custom_mapped_type_members(writer& w, std::string_view target, mapped_type const& mapping, bool is_private, std::string objref_name) + void write_custom_mapped_type_members(writer& w, mapped_type const& mapping, bool is_private, std::string objref_name) { if (mapping.abi_name == "IIterable`1") { @@ -3246,11 +3301,11 @@ visibility, self, objref_name); } else if (mapping.mapped_namespace == "System.Collections" && mapping.mapped_name == "IEnumerable") { - write_nongeneric_enumerable_members(w, target); + write_nongeneric_enumerable_members_using_static_abi_methods(w, objref_name); } else if (mapping.mapped_namespace == "System.Collections" && mapping.mapped_name == "IList") { - write_nongeneric_list_members(w, target, false, is_private); + write_nongeneric_list_members_using_static_abi_methods(w, is_private, objref_name); } else if (mapping.mapped_namespace == "System" && mapping.mapped_name == "IDisposable") { @@ -3392,7 +3447,7 @@ return %.AsValue(); { bool is_private = is_implemented_as_private_mapped_interface(w, type, interface_type); auto objref_name = w.write_temp("%", bind(semantics)); - write_custom_mapped_type_members(w, target, *mapping, is_private, objref_name); + write_custom_mapped_type_members(w, *mapping, is_private, objref_name); return; } From 2346784acd19a41a74bb520cb9395352c0d92a3f Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 12 Oct 2025 01:36:01 -0700 Subject: [PATCH 02/43] Add generic interface support --- src/cswinrt/code_writers.h | 443 ++++++++++++++++++++++++++++--------- 1 file changed, 341 insertions(+), 102 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 162e2c01c..194be4c00 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2762,20 +2762,27 @@ IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(); auto element = w.write_temp("%", bind(0)); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerable<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IEnumerableMethods<%>", element); + auto interop_method_name_prefix = w.write_temp("IEnumerableMethods_%_", escape_type_name_for_identifier(element, true)); w.write(R"( -%IEnumerator<%> %GetEnumerator() => %.GetEnumerator(%); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetEnumerator")] +static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerableMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); )", -visibility, element, self, abiClass, objref_name); +element, interop_method_name_prefix +); + + w.write(R"( +%IEnumerator<%> %GetEnumerator() => %GetEnumerator(null, %); +)", +visibility, element, self, interop_method_name_prefix, objref_name); if (!include_nongeneric) return; if (emit_explicit) { w.write(R"( -IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(%); -)", abiClass, objref_name); +IEnumerator IEnumerable.GetEnumerator() => %GetEnumerator(null, %); +)", interop_method_name_prefix, objref_name); } else { @@ -2800,7 +2807,7 @@ IEnumerator IEnumerable.GetEnumerator() => ((global::System.Collections.Generic. )", element); } - void write_enumerator_members(writer& w) + void write_enumerator_members_using_idic(writer& w) { auto element = w.write_temp("%", bind(0)); @@ -2822,19 +2829,39 @@ object IEnumerator.Current => Current; auto element = w.write_temp("%", bind(0)); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerator<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IEnumeratorMethods<%>", element); + auto interop_method_name_prefix = w.write_temp("IEnumeratorMethods_%_", escape_type_name_for_identifier(element, true)); w.write(R"( -%bool %MoveNext() => %.MoveNext(%); -%void %Reset() => %.Reset(%); -%void %Dispose() => %.Dispose(%); -%% %Current => %.get_Current(%); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "MoveNext")] +static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Reset")] +static extern void %Reset([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] +static extern void %Dispose([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Current")] +static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +)", +interop_method_name_prefix, +interop_method_name_prefix, +interop_method_name_prefix, +element, +interop_method_name_prefix +); + + w.write(R"( +%bool %MoveNext() => %MoveNext(null, %); +%void %Reset() => %Reset(null, %); +%void %Dispose() => %Dispose(null, %); +%% %Current => %Current(null, %); object IEnumerator.Current => Current; )", -visibility, self, abiClass, objref_name, -visibility, self, abiClass, objref_name, -visibility, self, abiClass, objref_name, -visibility, element, self, abiClass, objref_name); +visibility, self, interop_method_name_prefix, objref_name, +visibility, self, interop_method_name_prefix, objref_name, +visibility, self, interop_method_name_prefix, objref_name, +visibility, element, self, interop_method_name_prefix, objref_name); } void write_readonlydictionary_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) @@ -2844,23 +2871,50 @@ visibility, element, self, abiClass, objref_name); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyDictionary<%, %>.", key, value) : ""; auto ireadonlycollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyCollection>.", key, value) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<%, %>", key, value); auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; + auto interop_method_name_prefix = w.write_temp("IReadOnlyDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); w.write(R"( -%IEnumerable<%> %Keys => %.get_Keys(%); -%IEnumerable<%> %Values => %.get_Values(%); -%int %Count => %.get_Count(%); -%% %this[% key] => %.Indexer_Get(%, key); -%bool %ContainsKey(% key) => %.ContainsKey(%, key); -%bool %TryGetValue(% key, out % value) => %.TryGetValue(%, key, out value); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] +static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); )", -visibility, key, self, abiClass, objref_name, -visibility, value, self, abiClass, objref_name, -visibility, ireadonlycollection, abiClass, objref_name, -visibility, value, self, key, abiClass, objref_name, -visibility, self, key, abiClass, objref_name, -visibility, self, key, value, abiClass, objref_name); +key, interop_method_name_prefix, key, value, // Keys +value, interop_method_name_prefix, key, value, // Values +interop_method_name_prefix, key, value, // Count +value, interop_method_name_prefix, key, value, key, // Indexer_Get +interop_method_name_prefix, key, value, key, // ContainsKey +interop_method_name_prefix, key, value, key, value // TryGetValue +); + + w.write(R"( +%IEnumerable<%> %Keys => %Keys(null, %); +%IEnumerable<%> %Values => %Values(null, %); +%int %Count => %Count(null, %); +%% %this[% key] => %Indexer_Get(null, %, key); +%bool %ContainsKey(% key) => %ContainsKey(null, %, key); +%bool %TryGetValue(% key, out % value) => %TryGetValue(null, %, key, out value); +)", +visibility, key, self, interop_method_name_prefix, objref_name, +visibility, value, self, interop_method_name_prefix, objref_name, +visibility, ireadonlycollection, interop_method_name_prefix, objref_name, +visibility, value, self, key, interop_method_name_prefix, objref_name, +visibility, self, key, interop_method_name_prefix, objref_name, +visibility, self, key, value, interop_method_name_prefix, objref_name); } void write_readonlydictionary_members_using_idic(writer& w, bool include_enumerable) @@ -2901,43 +2955,102 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IDictionary<%, %>.", key, value) : ""; auto icollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.ICollection>.", key, value) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IDictionaryMethods<%, %>", key, value); auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; + auto interop_method_name_prefix = w.write_temp("IDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); w.write(R"( -%ICollection<%> %Keys => %.get_Keys(%); -%ICollection<%> %Values => %.get_Values(%); -%int %Count => %.get_Count(%); -%bool %IsReadOnly => %.get_IsReadOnly(%); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] +static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Set")] +static extern void %Indexer_Set([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +)", +key, interop_method_name_prefix, key, value, // Keys +value, interop_method_name_prefix, key, value, // Values +interop_method_name_prefix, key, value, // Count +value, interop_method_name_prefix, key, value, key, // Indexer_Get +interop_method_name_prefix, key, value, key, value, // Indexer_Set +interop_method_name_prefix, key, value, key, value, // Add +interop_method_name_prefix, key, value, key, // ContainsKey +interop_method_name_prefix, key, value, key, // Remove +interop_method_name_prefix, key, value, key, value, // TryGetValue +interop_method_name_prefix, key, value, key, value, // Add +interop_method_name_prefix, key, value, // Clear +interop_method_name_prefix, key, value, key, value, // Contains +interop_method_name_prefix, key, value, key, value, // CopyTo +interop_method_name_prefix, key, value, key, value // Remove +); + + w.write(R"( +%ICollection<%> %Keys => %Keys(null, %); +%ICollection<%> %Values => %Values(null, %); +%int %Count => %Count(null, %); +%bool %IsReadOnly => false; %% %this[% key] { -get => %.Indexer_Get(%, null, key); -set => %.Indexer_Set(%, key, value); +get => %Indexer_Get(null, %, key); +set => %Indexer_Set(null, %, key, value); } -%void %Add(% key, % value) => %.Add(%, key, value); -%bool %ContainsKey(% key) => %.ContainsKey(%, key); -%bool %Remove(% key) => %.Remove(%, key); -%bool %TryGetValue(% key, out % value) => %.TryGetValue(%, null, key, out value); -%void %Add(KeyValuePair<%, %> item) => %.Add(%, item); -%void %Clear() => %.Clear(%); -%bool %Contains(KeyValuePair<%, %> item) => %.Contains(%, null, item); -%void %CopyTo(KeyValuePair<%, %>[] array, int arrayIndex) => %.CopyTo(%, %, array, arrayIndex); -bool ICollection>.Remove(KeyValuePair<%, %> item) => %.Remove(%, item); +%void %Add(% key, % value) => %Add(null, %, key, value); +%bool %ContainsKey(% key) => %ContainsKey(null, %, key); +%bool %Remove(% key) => %Remove(null, %, key); +%bool %TryGetValue(% key, out % value) => %TryGetValue(null, %, key, out value); +%void %Add(KeyValuePair<%, %> item) => %Add(null, %, item); +%void %Clear() => %Clear(null, %); +%bool %Contains(KeyValuePair<%, %> item) => %Contains(null, %, item); +%void %CopyTo(KeyValuePair<%, %>[] array, int arrayIndex) => %CopyTo(null, %, %, array, arrayIndex); +bool ICollection>.Remove(KeyValuePair<%, %> item) => %Remove(null, %, item); )", -visibility, key, self, abiClass, objref_name, //Keys -visibility, value, self, abiClass, objref_name, // Values -visibility, icollection, abiClass, objref_name, // Count -visibility, icollection, abiClass, objref_name, // IsReadOnly -visibility, value, self, key, abiClass, objref_name, abiClass, objref_name, // Indexer -visibility, self, key, value, abiClass, objref_name, -visibility, self, key, abiClass, objref_name, -visibility, self, key, abiClass, objref_name, -visibility, self, key, value, abiClass, objref_name, -visibility, icollection, key, value, abiClass, objref_name, -visibility, icollection, abiClass, objref_name, -visibility, icollection, key, value, abiClass, objref_name, -visibility, icollection, key, value, abiClass, objref_name, enumerableObjRefName, -key, value, key, value, abiClass, objref_name); +visibility, key, self, interop_method_name_prefix, objref_name, //Keys +visibility, value, self, interop_method_name_prefix, objref_name, // Values +visibility, icollection, interop_method_name_prefix, objref_name, // Count +visibility, icollection, // IsReadOnly +visibility, value, self, key, interop_method_name_prefix, objref_name, interop_method_name_prefix, objref_name, // Indexer +visibility, self, key, value, interop_method_name_prefix, objref_name, +visibility, self, key, interop_method_name_prefix, objref_name, +visibility, self, key, interop_method_name_prefix, objref_name, +visibility, self, key, value, interop_method_name_prefix, objref_name, +visibility, icollection, key, value, interop_method_name_prefix, objref_name, +visibility, icollection, interop_method_name_prefix, objref_name, +visibility, icollection, key, value, interop_method_name_prefix, objref_name, +visibility, icollection, key, value, interop_method_name_prefix, objref_name, enumerableObjRefName, +key, value, key, value, interop_method_name_prefix, objref_name); } void write_dictionary_members_using_idic(writer& w, bool include_enumerable) @@ -2997,17 +3110,28 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyList<%>.", element) : ""; auto ireadonlycollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.IReadOnlyCollection<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IReadOnlyListMethods<%>", element); auto objRefName = w.write_temp("%", objref_name); + auto interop_method_name_prefix = w.write_temp("IReadOnlyListMethods_%_", escape_type_name_for_identifier(element, true)); w.write(R"( -%int %Count => %.get_Count(%); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] +static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +)", +interop_method_name_prefix, element, // Count +element, interop_method_name_prefix, element // Indexer_Get +); + + w.write(R"( +%int %Count => %Count(null, %); % -%% %this[int index] => %.Indexer_Get(%, index); +%% %this[int index] => %Indexer_Get(null, %, index); )", -visibility, ireadonlycollection, abiClass, objRefName, +visibility, ireadonlycollection, interop_method_name_prefix, objRefName, !emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("ReadOnlyListItem")])" : "", -visibility, element, self, abiClass, objRefName); +visibility, element, self, interop_method_name_prefix, objRefName); } @@ -3080,7 +3204,7 @@ visibility, self, target, objref_name, visibility, self, target, objref_name); } - void write_nongeneric_list_members(writer& w, std::string_view target, bool include_enumerable, bool emit_explicit) + void write_nongeneric_list_members_using_idic(writer& w, std::string_view target, bool include_enumerable, bool emit_explicit) { auto self = emit_explicit ? "global::System.Collections.IList." : ""; auto icollection = emit_explicit ? "global::System.Collections.ICollection." : ""; @@ -3182,38 +3306,86 @@ element, enumerable_type, target); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IList<%>.", element) : ""; auto icollection = emit_explicit ? w.write_temp("global::System.Collections.Generic.ICollection<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; - auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IListMethods<%>", element); auto objRefName = w.write_temp("%", objref_name); + auto interop_method_name_prefix = w.write_temp("IListMethods_%_", escape_type_name_for_identifier(element, true)); + w.write(R"( -%int %Count => %.get_Count(%); -%bool %IsReadOnly => %.get_IsReadOnly(%); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] +static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Set")] +static extern void %Indexer_Set([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "IndexOf")] +static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Insert")] +static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RemoveAt")] +static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +)", +interop_method_name_prefix, element, // Count +element, interop_method_name_prefix, element, // Indexer_Get +interop_method_name_prefix, element, element, // Indexer_Set +interop_method_name_prefix, element, element, // IndexOf +interop_method_name_prefix, element, element, // Insert +interop_method_name_prefix, element, // RemoveAt +interop_method_name_prefix, element, element, // Add +interop_method_name_prefix, element, // Clear +interop_method_name_prefix, element, element, // Contains +interop_method_name_prefix, element, element, // CopyTo +interop_method_name_prefix, element, element // Remove +); + + w.write(R"( +%int %Count => %Count(null, %); +%bool %IsReadOnly => false; % %% %this[int index] { -get => %.Indexer_Get(%, index); -set => %.Indexer_Set(%, index, value); +get => %Indexer_Get(null, %, index); +set => %Indexer_Set(null, %, index, value); } -%int %IndexOf(% item) => %.IndexOf(%, item); -%void %Insert(int index, % item) => %.Insert(%, index, item); -%void %RemoveAt(int index) => %.RemoveAt(%, index); -%void %Add(% item) => %.Add(%, item); -%void %Clear() => %.Clear(%); -%bool %Contains(% item) => %.Contains(%, item); -%void %CopyTo(%[] array, int arrayIndex) => %.CopyTo(%, array, arrayIndex); -%bool %Remove(% item) => %.Remove(%, item); +%int %IndexOf(% item) => %IndexOf(null, %, item); +%void %Insert(int index, % item) => %Insert(null, %, index, item); +%void %RemoveAt(int index) => %RemoveAt(null, %, index); +%void %Add(% item) => %Add(null, %, item); +%void %Clear() => %Clear(null, %); +%bool %Contains(% item) => %Contains(null, %, item); +%void %CopyTo(%[] array, int arrayIndex) => %CopyTo(null, %, array, arrayIndex); +%bool %Remove(% item) => %Remove(null, %, item); )", - visibility, icollection, abiClass, objref_name, //Count - visibility, icollection, abiClass, objref_name, //IsReadOnly + visibility, icollection, interop_method_name_prefix, objref_name, //Count + visibility, icollection, //IsReadOnly !emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("ListItem")])" : "", //Indexer - visibility, element, self, abiClass, objref_name, abiClass, objref_name, //Indexer - visibility, self, element, abiClass, objref_name, //IndexOf - visibility, self, element, abiClass, objref_name, //Insert - visibility, self, abiClass, objref_name, //RemoveAt - visibility, icollection, element, abiClass, objref_name, //Add - visibility, icollection, abiClass, objref_name, //Clear - visibility, icollection, element, abiClass, objref_name, //Contains - visibility, icollection, element, abiClass, objref_name, //CopyTo - visibility, icollection, element, abiClass, objref_name); //Remove + visibility, element, self, interop_method_name_prefix, objref_name, interop_method_name_prefix, objref_name, //Indexer + visibility, self, element, interop_method_name_prefix, objref_name, //IndexOf + visibility, self, element, interop_method_name_prefix, objref_name, //Insert + visibility, self, interop_method_name_prefix, objref_name, //RemoveAt + visibility, icollection, element, interop_method_name_prefix, objref_name, //Add + visibility, icollection, interop_method_name_prefix, objref_name, //Clear + visibility, icollection, element, interop_method_name_prefix, objref_name, //Contains + visibility, icollection, element, interop_method_name_prefix, objref_name, //CopyTo + visibility, icollection, element, interop_method_name_prefix, objref_name); //Remove } void write_idisposable_members(writer& w) @@ -3416,9 +3588,9 @@ visibility, self, objref_name); auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type); auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; - // Write IWindowsRuntimeInterface implementation for non-default interface + // Write IWindowsRuntimeInterface implementation for non-default interface and for the default interface if it isn't exclusive. // Otherwise, write the default interface in composable scenarios using its own function. - if (!is_default_interface) + if (!is_exclusive_to(interface_type)) { w.write(R"( WindowsRuntimeObjectReferenceValue IWindowsRuntimeInterface<%>.GetInterface() @@ -3429,7 +3601,7 @@ return %.AsValue(); interface_name, bind(interface_type)); } - else if (!type.Flags().Sealed()) + else if (is_default_interface && !type.Flags().Sealed()) { bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); @@ -5734,7 +5906,7 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference { required_interfaces[std::move(interface_name)] = { - w.write_temp("%", bind()), + w.write_temp("%", bind()), }; } else if (mapping->abi_name == "IMapView`2") // IReadOnlyDictionary`2 @@ -5786,7 +5958,7 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference { required_interfaces[std::move(interface_name)] = { - w.write_temp("%", bind( + w.write_temp("%", bind( "((global::System.Collections.IList)(WindowsRuntimeObject)this)", true, true)) @@ -5848,8 +6020,81 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference }); } } + + void write_guid(writer& w, TypeDef const& type, bool lowerCase) + { + auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); + if (!attribute) + { + throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); + } + + auto args = attribute.Value().FixedArgs(); + + using std::get; + + auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; + + w.write_printf( + lowerCase ? + R"(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)" : + R"(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)", + get(get_arg(0)), + get(get_arg(1)), + get(get_arg(2)), + get(get_arg(3)), + get(get_arg(4)), + get(get_arg(5)), + get(get_arg(6)), + get(get_arg(7)), + get(get_arg(8)), + get(get_arg(9)), + get(get_arg(10))); + } + + void write_guid_attribute(writer& w, TypeDef const& type) + { + auto fully_qualify_guid = (type.TypeNamespace() == "Windows.Foundation.Metadata"); + + w.write(R"([%("%")])", + fully_qualify_guid ? "global::System.Runtime.InteropServices.Guid" : "Guid", + bind(type, false)); + } + + void write_guid_bytes(writer& w, TypeDef const& type) + { + auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); + if (!attribute) + { + throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); + } + + auto args = attribute.Value().FixedArgs(); + + using std::get; + + auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; + + w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", + (get(get_arg(0)) >> 0) & 0xFF, + (get(get_arg(0)) >> 8) & 0xFF, + (get(get_arg(0)) >> 16) & 0xFF, + (get(get_arg(0)) >> 24) & 0xFF, + (get(get_arg(1)) >> 0) & 0xFF, + (get(get_arg(1)) >> 8) & 0xFF, + (get(get_arg(2)) >> 0) & 0xFF, + (get(get_arg(2)) >> 8) & 0xFF, + get(get_arg(3)), + get(get_arg(4)), + get(get_arg(5)), + get(get_arg(6)), + get(get_arg(7)), + get(get_arg(8)), + get(get_arg(9)), + get(get_arg(10))); + } - void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool add_custom_qi, bool include_exclusive_interface, bool includeWindowsRuntimeObject) + void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool include_exclusive_interface, bool includeWindowsRuntimeObject) { auto delimiter{ " : " }; auto write_delimiter = [&]() @@ -5886,12 +6131,6 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference } }); } - - if (add_custom_qi) - { - write_delimiter(); - w.write("global::System.Runtime.InteropServices.ICustomQueryInterface"); - } } std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string) @@ -7245,7 +7484,7 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, bind(type, false), (is_exclusive_to(type) && !settings.public_exclusiveto) ? "internal" : "public", type_name, - bind(type, object_type{}, false, false, false), + bind(type, object_type{}, false, false), bind(type) ); } @@ -7658,7 +7897,7 @@ return MarshalInspectable<%>.FromAbi(thisPtr); bind(type), // class name type_name, - bind(type, base_semantics, false, false, true), + bind(type, base_semantics, false, true), // start of class bind(type, type.Flags().Sealed()), // ObjectReference constructor From 5aa416a0e458e5534caea3397f3758fd4aca7023 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 18 Oct 2025 21:54:31 -0700 Subject: [PATCH 03/43] Update for array support --- src/cswinrt/code_writers.h | 353 ++++++++++++++++++++++++++++++++----- 1 file changed, 304 insertions(+), 49 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 194be4c00..56cf6e15a 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -565,8 +565,10 @@ namespace cswinrt w.write("out %", bind(semantics)); break; case param_category::pass_array: + w.write("ReadOnlySpan<%>", bind(semantics)); + break; case param_category::fill_array: - w.write("%[]", bind(semantics)); + w.write("Span<%>", bind(semantics)); break; case param_category::receive_array: w.write("out %[]", bind(semantics)); @@ -1004,10 +1006,10 @@ namespace cswinrt break; case param_category::pass_array: case param_category::fill_array: - w.write(", int __%Size, IntPtr %", param_name, param_name); + w.write(", uint __%Size, IntPtr %", param_name, param_name); break; case param_category::receive_array: - w.write(settings.netstandard_compat ? ", out int __%Size, out IntPtr %" : ", int* __%Size, IntPtr* %", param_name, param_name); + w.write(settings.netstandard_compat ? ", out uint __%Size, out IntPtr %" : ", uint* __%Size, IntPtr* %", param_name, param_name); break; } } @@ -1028,10 +1030,10 @@ namespace cswinrt break; case param_category::pass_array: case param_category::fill_array: - w.write(", int, IntPtr"); + w.write(", uint, IntPtr"); break; case param_category::receive_array: - w.write(", out int, out IntPtr"); + w.write(", out uint, out IntPtr"); break; } } @@ -1052,10 +1054,10 @@ namespace cswinrt break; case param_category::pass_array: case param_category::fill_array: - w.write(", int, IntPtr"); + w.write(", uint, void*"); break; case param_category::receive_array: - w.write(", int*, IntPtr*"); + w.write(", uint*, void**"); break; } } @@ -1069,13 +1071,13 @@ namespace cswinrt if (settings.netstandard_compat) { return_sig.Type().is_szarray() ? - w.write(", out int __%Size, out IntPtr %", signature.return_param_name(), return_param) : + w.write(", out uint __%Size, out IntPtr %", signature.return_param_name(), return_param) : w.write(", out % %", bind(semantics), return_param); } else { return_sig.Type().is_szarray() ? - w.write(", int* __%Size, IntPtr* %", signature.return_param_name(), return_param) : + w.write(", uint* __%Size, IntPtr* %", signature.return_param_name(), return_param) : w.write(", %* %", bind(semantics), return_param); } } @@ -1087,7 +1089,7 @@ namespace cswinrt { auto semantics = get_type_semantics(return_sig.Type()); return_sig.Type().is_szarray() ? - w.write(", out int, out IntPtr") : + w.write(", out uint, out IntPtr") : w.write(", out %", bind(semantics)); } } @@ -1098,7 +1100,7 @@ namespace cswinrt { auto semantics = get_type_semantics(return_sig.Type()); return_sig.Type().is_szarray() ? - w.write(", int*, IntPtr*") : + w.write(", uint*, void**") : w.write(", %*", bind(semantics)); } } @@ -4773,6 +4775,11 @@ event % %;)", marshaler_type.empty() && local_type == "void*"; } + bool is_pinnable_array_data() const + { + return is_array() && !is_out(); + } + // We pass using in for .NET Standard. Outside of .NET Standard, // we want our function pointers to be blittable and be able to disable // runtime marshaling, so we use ptrs with the managed function calling the @@ -4797,7 +4804,7 @@ event % %;)", if (!is_generic()) { return is_array() ? - w.write_temp("(__%_length, __%_data)", + w.write_temp("__%_length, &__%_data", param_name, param_name) : get_marshaler_local(w); } @@ -4811,13 +4818,55 @@ event % %;)", { if (interop_dll_type != "") { - w.write(R"( + if (is_array()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] +static extern %[] ConvertToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); + +)", +param_type, +param_name, +interop_dll_type); + } + else + { + w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] static extern % ConvertToManaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, void* value); )", param_type, param_name, +interop_dll_type); + } + } + } + + void write_interop_dispose_function(writer& w) const + { + if (interop_dll_type != "" && is_array()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] +static extern void Dispose_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); + +)", +param_name, +interop_dll_type); + } + } + + void write_interop_free_function(writer& w) const + { + if (interop_dll_type != "" && is_array()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Free")] +static extern void Free_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); + +)", +param_name, interop_dll_type); } } @@ -4826,7 +4875,20 @@ interop_dll_type); { if (interop_dll_type != "") { - w.write(R"( + if (is_array()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] +static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); + +)", +param_name, +interop_dll_type, +param_type); + } + else + { + w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, % value); @@ -4834,6 +4896,52 @@ static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAcc param_name, interop_dll_type, param_type); + } + } + } + + void write_copy_to_unmanaged_function(writer& w) const + { + if (is_array() && marshaler_type == "HStringMarshaller") + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanagedUnsafe")] +static extern void CopyToUnmanagedUnsafe_%([UnsafeAccessorType("string_ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan value, uint destinationSize, void* destination, uint headersSize, void* headers); + +)", +param_name); + } + else if (interop_dll_type != "") + { + if (is_array()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] +static extern void CopyToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); + +)", +param_name, +interop_dll_type, +param_type); + } + } + } + + void write_copy_to_managed_function(writer& w) const + { + if (interop_dll_type != "") + { + if (is_ref()) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] +static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); + +)", +param_name, +interop_dll_type, +param_type); + } } } @@ -4847,8 +4955,52 @@ param_type); if (is_array()) { - w.write("int __%_length = default;\n", param_name); - w.write("IntPtr __%_data = default;\n", param_name); + if (is_out()) + { + w.write("uint __%_length = default;\n", param_name); + w.write("void* __%_data = default;\n", param_name); + } + else + { + w.write(R"( +Unsafe.SkipInit(out InlineArray16<%> __%_inlineArray); +%[] __%_arrayFromPool = null; +Span<%> __%_span = %.Length <= 16 + ? __%_inlineArray[..%.Length] + : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent(%.Length)); +)", + local_type, + param_name, + local_type, + param_name, + local_type, + param_name, + param_name, + param_name, + param_name, + param_name, + local_type, + param_name); + + if (!is_ref() && marshaler_type == "HStringMarshaller") + { + w.write(R"( +Unsafe.SkipInit(out InlineArray16 __%_inlineReferenceArray); +HStringReference[] __%_referenceArrayFromPool = null; +Span __%_referenceSpan = %.Length <= 16 + ? __%_inlineReferenceArray[..%.Length] + : (__%_referenceArrayFromPool = global::System.Buffers.ArrayPool.Shared.Rent(%.Length)); +)", + param_name, + param_name, + param_name, + param_name, + param_name, + param_name, + param_name, + param_name); + } + } return; } @@ -4889,7 +5041,7 @@ param_type); void write_assignments(writer& w) const { - if (is_pinnable || is_object_in() || is_out() || local_type.empty()) + if (is_pinnable || is_pinnable_array_data() || is_object_in() || is_out() || local_type.empty()) return; // TODO: Arrays @@ -4914,28 +5066,77 @@ param_type); void write_fixed_expression(writer& w, bool& write_delimiter) const { - if (!is_pinnable) + if (!is_pinnable && !is_pinnable_array_data()) return; if (write_delimiter) { w.write(", "); } - w.write("_% = %", - param_name, - marshaler_type == "global::ABI.System.TypeMarshaller" + + if (!is_pinnable && is_pinnable_array_data()) + { + w.write("_% = __%_span", + param_name, + param_name); + + if (!is_ref() && marshaler_type == "HStringMarshaller") + { + w.write(", _%_reference = __%_referenceSpan", + param_name, + param_name); + } + } + else + { + w.write("_% = %", + param_name, + marshaler_type == "global::ABI.System.TypeMarshaller" ? "__" + param_name : get_escaped_param_name(w)); + } write_delimiter = true; } void write_fixed_marshaler(writer& w) const { - if (!is_pinnable) + if (!is_pinnable && !is_pinnable_array_data()) return; if (marshaler_type == "HStringMarshaller") { - w.write("HStringMarshaller.ConvertToUnmanagedUnsafe((char*)_%, %.Length, out HStringReference __%);", - param_name, get_escaped_param_name(w), param_name); + if (is_pinnable_array_data()) + { + if (is_ref()) + { + return; + } + + write_copy_to_unmanaged_function(w); + w.write(R"( +CopyToUnmanagedUnsafe_%( + null, + value: %, + destinationSize: (uint)__%_span.Length, + destination: _%, + headersSize: (uint)__%_referenceSpan.Length, + headers: _%_reference); +)", + param_name, + param_name, + param_name, + param_name, + param_name, + param_name); + } + else + { + w.write("HStringMarshaller.ConvertToUnmanagedUnsafe((char*)_%, %.Length, out HStringReference __%);", + param_name, get_escaped_param_name(w), param_name); + } + } + else if (!is_pinnable && is_pinnable_array_data() && !is_ref()) + { + write_copy_to_unmanaged_function(w); + w.write("CopyToUnmanaged_%(null, %, (uint)%.Length, &_%);\n", param_name, param_name, param_name, param_name); } } @@ -4945,9 +5146,16 @@ param_type); { if (is_array()) { - w.write("%__%_length, %__%_data", - is_out() ? (settings.netstandard_compat ? "out " : "&") : "", param_name, - is_out() ? (settings.netstandard_compat ? "out " : "&") : "", param_name); + if (is_pinnable || is_pinnable_array_data()) + { + w.write("(uint) %.Length, _%", param_name, param_name); + } + else + { + w.write("%__%_length, %__%_data", + is_out() ? "&" : "", param_name, + is_out() ? "&" : "", param_name); + } return; } @@ -5102,13 +5310,14 @@ param_type); return; if (is_ref()) { - if (!starts_with(marshaler_type, "MarshalBlittable")) + if (!is_pinnable) { - w.write("%.CopyAbiArray(%, (__%_length, __%_data));\n", - marshaler_type, - bind(param_name), + write_copy_to_managed_function(w); + w.write("CopyToManaged_%(null, (uint)__%_span.Length, &_%, %);\n", param_name, - param_name); + param_name, + param_name, + bind(param_name)); } return; } @@ -5140,9 +5349,10 @@ param_type); { if (is_array()) { - w.write("%.DisposeAbi%(%);\n", - marshaler_type, - is_array() ? "Array" : "", + write_interop_free_function(w); + + w.write("Free_%(null, %);\n", + param_name, get_param_local(w)); } else if (is_marshal_by_object_reference_value()) @@ -5165,10 +5375,42 @@ param_type); } else { - // TODO arrays - w.write("%.Dispose(%);\n", - marshaler_type, - get_param_local(w)); + if (is_array()) + { + // HStringReference doesn't need to be freed. + if (is_ref() || marshaler_type != "HStringMarshaller") + { + write_interop_dispose_function(w); + + w.write(R"( +fixed(void* _% = __%_span) +{ +Dispose_%(null, (uint) __%_span.Length, &_%); +} +)", + param_name, + param_name, + param_name, + param_name, + param_name); + } + + w.write(R"( +if (__%_arrayFromPool is not null) +{ +global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); +} +)", + param_name, + local_type, + param_name); + } + else + { + w.write("%.Dispose(%);\n", + marshaler_type, + get_param_local(w)); + } } } }; @@ -5193,9 +5435,10 @@ param_type); { if (m.is_array()) { - m.marshaler_type = is_type_blittable(semantics, true) ? "MarshalBlittable" : "MarshalNonBlittable"; + m.is_pinnable = is_type_blittable(semantics, true) && !m.is_out(); + m.interop_dll_type = w.write_temp("%", bind(semantics)); m.marshaler_type += "<" + m.param_type + ">"; - m.local_type = m.marshaler_type + ".MarshalerArray"; + m.local_type = w.write_temp("%", bind(semantics)); } else if (!is_type_blittable(type)) { @@ -5229,7 +5472,12 @@ param_type); m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); if (m.is_array()) { - m.local_type = w.write_temp("MarshalInterfaceHelper<%>.MarshalerArray", m.param_type); + m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.local_type = w.write_temp("%", bind(semantics)); + if (m.local_type == "void*") + { + m.local_type = "nint"; + } } else { @@ -5247,7 +5495,8 @@ param_type); m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); if (m.is_array()) { - m.local_type = w.write_temp("MarshalInterfaceHelper<%>.MarshalerArray", m.param_type); + m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.local_type = "nint"; } else { @@ -5259,7 +5508,8 @@ param_type); m.marshaler_type = get_abi_type(); if (m.is_array()) { - m.local_type = w.write_temp("MarshalInterfaceHelper<%>.MarshalerArray", m.param_type); + m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.local_type = "nint"; } else { @@ -5282,7 +5532,8 @@ param_type); m.marshaler_type = "WindowsRuntimeObjectMarshaller"; if (m.is_array()) { - m.local_type = "MarshalInterfaceHelper.MarshalerArray"; + m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.local_type = "nint"; } else { @@ -5321,8 +5572,10 @@ param_type); { if (m.is_array()) { - m.marshaler_type = "MarshalString"; - m.local_type = "MarshalString.MarshalerArray"; + m.marshaler_type = "HStringMarshaller"; + m.local_type = "nint"; + m.interop_dll_type = "string"; + // m.is_pinnable = !m.is_out(); } else { @@ -5350,9 +5603,11 @@ param_type); } else { + m.is_pinnable = is_type_blittable(semantics, true) && !m.is_out(); + m.interop_dll_type = w.write_temp("%", bind(semantics)); m.marshaler_type = is_type_blittable(semantics, true) ? "MarshalBlittable" : "MarshalNonBlittable"; m.marshaler_type += "<" + m.param_type + ">"; - m.local_type = m.marshaler_type + ".MarshalerArray"; + m.local_type = w.write_temp("%", bind(semantics)); } } } @@ -5407,7 +5662,7 @@ param_type); bool have_pinnables{}; for (auto&& m : marshalers) { - have_pinnables |= m.is_pinnable; + have_pinnables |= m.is_pinnable || m.is_pinnable_array_data(); m.write_pinnable(w); } From 021ccd91ba0f82f1d12ad110e392e493b0153c7f Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 19 Oct 2025 19:27:57 -0700 Subject: [PATCH 04/43] Array support for ABI side --- src/cswinrt/code_writers.h | 293 ++++++++++++++++++++++++------------- 1 file changed, 193 insertions(+), 100 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 56cf6e15a..4e733199d 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -999,17 +999,17 @@ namespace cswinrt w.write(", % %", bind(semantics), param_name); break; case param_category::ref: - w.write(settings.netstandard_compat ? ", in % %" : ", %* %", bind(semantics), param_name); + w.write(", %* %", bind(semantics), param_name); break; case param_category::out: - w.write(settings.netstandard_compat ? ", out % %" : ", %* %", bind(semantics), param_name); + w.write(", %* %", bind(semantics), param_name); break; case param_category::pass_array: case param_category::fill_array: - w.write(", uint __%Size, IntPtr %", param_name, param_name); + w.write(", uint __%Size, void* %", param_name, param_name); break; case param_category::receive_array: - w.write(settings.netstandard_compat ? ", out uint __%Size, out IntPtr %" : ", uint* __%Size, IntPtr* %", param_name, param_name); + w.write(", uint* __%Size, void** %", param_name, param_name); break; } } @@ -6530,26 +6530,12 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference if (param_cat == param_category::ref) { - if (settings.netstandard_compat) - { - param_prefix = "in "; - } - else - { - param_suffix = "*"; - } + param_suffix = "*"; } if (param_cat == param_category::out) { - if (settings.netstandard_compat) - { - param_prefix = "out "; - } - else - { - param_suffix = "*"; - } + param_suffix = "*"; } w.write(", %%% %", @@ -6591,6 +6577,7 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference bool use_pointers; std::string interop_dll_type; bool marshal_by_object_reference_value; + bool is_blittable; bool is_out() const { @@ -6620,27 +6607,45 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference void write_local(writer& w) const { - if ((category == param_category::in) || (category == param_category::pass_array)) - return; - if (category == param_category::fill_array) - { - w.write("% __% = %.FromAbiArray((__%Size, %));\n", - local_type, - param_name, - marshaler_type, - param_name, bind(param_name)); + if (category == param_category::in || category == param_category::ref) return; - } - if (category == param_category::ref) + + if (category == param_category::pass_array || category == param_category::fill_array) { - if (!use_pointers) + if (is_blittable) { - w.write("var __% = %;\n", + w.write("%<%> __% = new(%, (int)__%Size);\n", + category == param_category::pass_array ? "ReadOnlySpan" : "Span", + param_type, param_name, - bind(param_name)); + bind(param_name), + param_name); + } + else + { + w.write(R"( +Unsafe.SkipInit(out InlineArray16<%> __%_inlineArray); +%[] __%_arrayFromPool = null; +Span<%> __% = __%Size <= 16 + ? __%_inlineArray[..(int)__%Size] + : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent((int)__%Size)); +)", + param_type, + param_name, + param_type, + param_name, + param_type, + param_name, + param_name, + param_name, + param_name, + param_name, + param_type, + param_name); } return; } + std::string_view out_local_type; if (param_type == "bool") { @@ -6662,26 +6667,20 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference void write_out_initialize(writer& w) const { XLANG_ASSERT(is_out()); - if (!use_pointers) - { - w.write("% = default;\n", bind(param_name)); - if (is_array()) - { - w.write("__%Size = default;\n", param_name); - } - } - else + w.write("*% = default;\n", bind(param_name)); + if (is_array()) { - w.write("*% = default;\n", bind(param_name)); - if (is_array()) - { - w.write("*__%Size = default;\n", param_name); - } + w.write("*__%Size = default;\n", param_name); } } void write_convert_to_managed_function(writer& w) const { + if (is_array()) + { + return; + } + if (interop_dll_type != "") { w.write(R"( @@ -6699,7 +6698,25 @@ interop_dll_type); { if (interop_dll_type != "") { - w.write(R"( + if (is_array()) + { + if (!is_out()) + { + return; + } + + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] +static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); + +)", +param_name, +interop_dll_type, +param_type); + } + else + { + w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, % value); @@ -6707,6 +6724,28 @@ static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAcc param_name, interop_dll_type, param_type); + } + } + } + + void write_copy_to_managed(writer& w) const + { + if (!is_blittable && category == param_category::pass_array) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] +static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); + +)", + param_name, + interop_dll_type, + param_type); + + w.write("CopyToManaged_%(null, __%Size, &%, __%);", + param_name, + param_name, + bind(param_name), + param_name); } } @@ -6729,14 +6768,7 @@ param_type); } else if (category == param_category::ref) { - if (!use_pointers) - { - format_string = "in __%"; - } - else - { - format_string = "*%"; - } + format_string = "*%"; } else { @@ -6746,9 +6778,7 @@ param_type); } else if (is_array()) { - w.write("%.FromAbiArray((__%Size, %))", - marshaler_type, - param_name, bind(param_name)); + w.write("__%", param_name); } else if (interop_dll_type != "") { @@ -6773,61 +6803,91 @@ param_type); auto param_local = get_param_local(w); if (is_ref()) { - w.write("%.CopyManagedArray(%, %);\n", - marshaler_type, - param_local, + if (is_blittable) + { + return; + } + + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] +static extern void CopyToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); + +CopyToUnmanaged_%(null, __%, __%Size, (void**)%); + +)", + // CopyToUnmanaged function + param_name, + interop_dll_type, + param_type, + // function call + param_name, + param_name, + param_name, bind(param_name)); + return; } - if (!use_pointers) + + if (is_array()) { - is_array() ? - w.write("(__%Size, %) = ", param_name, bind(param_name)) : - w.write("% = ", bind(param_name)); + w.write("ConvertToUnmanaged_%(null, __%, out *__%Size, out *(void***)%);\n", param_name, param_name, param_name, bind(param_name)); } else { - is_array() ? - w.write("(*__%Size, *%) = ", param_name, bind(param_name)) : - w.write("*% = ", bind(param_name)); - } - if (marshaler_type.empty()) - { - if (local_type == "void*") - { - w.write("%.ConvertToUnmanaged(%);", - param_type, - param_local); - } - else + w.write("*% = ", bind(param_name)); + + if (marshaler_type.empty()) { - if (param_type == "bool") + if (local_type == "void*") { - w.write("(byte)(% ? 1 : 0);", param_local); - } - else if (param_type == "char") - { - w.write("(ushort)%;", param_local); + w.write("%.ConvertToUnmanaged(%);", + param_type, + param_local); } else { - w.write("%;", param_local); + if (param_type == "bool") + { + w.write("(byte)(% ? 1 : 0);", param_local); + } + else if (param_type == "char") + { + w.write("(ushort)%;", param_local); + } + else + { + w.write("%;", param_local); + } } } + else + { + w.write("%%ConvertToUnmanaged%(%%)%;", + abi_boxed ? w.write_temp("(%)", param_type) : "", + interop_dll_type != "" ? "" : marshaler_type + ".", + interop_dll_type != "" ? "_" + param_name : "", + interop_dll_type != "" ? "null, " : "", + param_local, + is_marshal_by_object_reference_value() ? ".DetachThisPtrUnsafe()" : ""); + } } - else + + w.write("\n"); + } + + bool need_dispose() const + { + return is_array() && !is_blittable && (category == param_category::pass_array || category == param_category::fill_array); + } + + void write_dispose(writer& w) const + { + if (!need_dispose()) { - w.write("%%ConvertToUnmanaged%%(%%)%;", - abi_boxed && !is_array() ? - w.write_temp("(%)", param_type) : "", - interop_dll_type != "" ? "" : marshaler_type + ".", - is_array() ? "Array" : "", - interop_dll_type != "" ? "_" + param_name : "", - interop_dll_type != "" ? "null, " : "", - param_local, - is_marshal_by_object_reference_value() ? ".DetachThisPtrUnsafe()" : ""); + return; } - w.write("\n"); + + w.write("global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool);\n", param_type, param_name); } }; @@ -6937,12 +6997,14 @@ param_type); { if (m.marshaler_type.empty()) { + m.is_blittable = is_type_blittable(semantics, true); m.marshaler_type = is_type_blittable(semantics, true) ? "MarshalBlittable" : "MarshalNonBlittable"; m.marshaler_type += "<" + m.param_type + ">"; } m.local_type = (m.local_type.empty() ? m.param_type : m.local_type) + "[]"; + m.interop_dll_type = w.write_temp("%", bind(semantics)); } - m.use_pointers = !settings.netstandard_compat; + m.use_pointers = true; }; for (auto&& param : signature.params()) @@ -6987,18 +7049,20 @@ R"( try { % +% %% return 0; } catch (Exception __exception__) { return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); -} +}% )", [&](writer& w) { w.write(bind_each([](writer& w, managed_marshaler const& m) { m.write_convert_to_managed_function(w); + m.write_convert_to_unmanaged_function(w); }, marshalers)); if (return_sig) @@ -7028,6 +7092,13 @@ return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); }, marshalers)); }, [&](writer& w) + { + w.write(bind_each([](writer& w, managed_marshaler const& m) + { + m.write_copy_to_managed(w); + }, marshalers)); + }, + [&](writer& w) { if (return_sig) { @@ -7051,6 +7122,28 @@ return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); [&](writer& w) { if (!return_sig) return; return_marshaler.write_marshal_from_managed(w); + }, + [&](writer& w) { + bool write_dispose = false; + w.write(bind_each([&](writer&, managed_marshaler const& m) + { + write_dispose |= m.need_dispose(); + }, marshalers)); + + if (write_dispose) + { + w.write(R"( +finally +{ +% +} +)", + bind_each([](writer& w, managed_marshaler const& m) + { + m.write_dispose(w); + }, marshalers) +); + } }); } From 37fc263f7497f6d24904ced5065382dea297f137 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 19 Oct 2025 20:15:58 -0700 Subject: [PATCH 05/43] Fix TypedEventHandler and other delegate issues --- src/cswinrt/code_writers.h | 18 +++++++++--------- src/cswinrt/helpers.h | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 4e733199d..9b162afb4 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -5505,7 +5505,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); } break; case category::delegate_type: - m.marshaler_type = get_abi_type(); + m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); if (m.is_array()) { m.interop_dll_type = w.write_temp("%", bind(semantics)); @@ -6696,15 +6696,15 @@ interop_dll_type); void write_convert_to_unmanaged_function(writer& w) const { + if (!is_out()) + { + return; + } + if (interop_dll_type != "") { if (is_array()) { - if (!is_out()) - { - return; - } - w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); @@ -6923,7 +6923,7 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); } break; case category::interface_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true));; + m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); m.local_type = m.param_type; m.interface_guid = w.write_temp("%", bind(type)); m.marshal_by_object_reference_value = true; @@ -6933,12 +6933,12 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); } break; case category::class_type: - m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true));; + m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); m.local_type = m.param_type; m.marshal_by_object_reference_value = true; break; case category::delegate_type: - m.marshaler_type = get_abi_type(); + m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); m.local_type = m.param_type; m.marshal_by_object_reference_value = true; if (distance(type.GenericParam()) > 0) diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 6384ee228..bc3ef1b86 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -797,6 +797,7 @@ namespace cswinrt { "Rect", "Windows.Foundation", "Rect" }, { "Size", "Windows.Foundation", "Size" }, { "TimeSpan", "System", "TimeSpan", true }, + { "TypedEventHandler`2", "System", "EventHandler", false }, { "Uri", "System", "Uri", true } } }, From 9926aa527ea6d3a02ed044b2f825a3ccb182c5ab Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 19 Oct 2025 20:37:26 -0700 Subject: [PATCH 06/43] More fixes --- src/cswinrt/code_writers.h | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 9b162afb4..1b9e1eb5d 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -616,10 +616,10 @@ namespace cswinrt break; case param_category::pass_array: case param_category::fill_array: - w.write("%[]", bind(param)); + w.write("%", bind(param)); break; case param_category::receive_array: - w.write("out %[]", bind(param)); + w.write("out %", bind(param)); break; } } @@ -1068,18 +1068,9 @@ namespace cswinrt { auto semantics = get_type_semantics(return_sig.Type()); auto return_param = w.write_temp("%", bind(signature.return_param_name())); - if (settings.netstandard_compat) - { - return_sig.Type().is_szarray() ? - w.write(", out uint __%Size, out IntPtr %", signature.return_param_name(), return_param) : - w.write(", out % %", bind(semantics), return_param); - } - else - { - return_sig.Type().is_szarray() ? - w.write(", uint* __%Size, IntPtr* %", signature.return_param_name(), return_param) : - w.write(", %* %", bind(semantics), return_param); - } + return_sig.Type().is_szarray() ? + w.write(", uint* __%Size, void** %", signature.return_param_name(), return_param) : + w.write(", %* %", bind(semantics), return_param); } } @@ -6574,7 +6565,6 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference std::string marshaler_type; std::string interface_guid; bool abi_boxed; - bool use_pointers; std::string interop_dll_type; bool marshal_by_object_reference_value; bool is_blittable; @@ -6676,7 +6666,7 @@ Span<%> __% = __%Size <= 16 void write_convert_to_managed_function(writer& w) const { - if (is_array()) + if (is_array() || is_out()) { return; } @@ -7004,7 +6994,6 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); m.local_type = (m.local_type.empty() ? m.param_type : m.local_type) + "[]"; m.interop_dll_type = w.write_temp("%", bind(semantics)); } - m.use_pointers = true; }; for (auto&& param : signature.params()) From 2525cf015c36341350c45d8d6f4bf93f5fffb467 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 19 Oct 2025 21:38:04 -0700 Subject: [PATCH 07/43] Fix bad merge --- src/cswinrt/code_writers.h | 73 -------------------------------------- 1 file changed, 73 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 1b9e1eb5d..226faa2f9 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -6266,79 +6266,6 @@ public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference }); } } - - void write_guid(writer& w, TypeDef const& type, bool lowerCase) - { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); - } - - auto args = attribute.Value().FixedArgs(); - - using std::get; - - auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - - w.write_printf( - lowerCase ? - R"(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)" : - R"(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)", - get(get_arg(0)), - get(get_arg(1)), - get(get_arg(2)), - get(get_arg(3)), - get(get_arg(4)), - get(get_arg(5)), - get(get_arg(6)), - get(get_arg(7)), - get(get_arg(8)), - get(get_arg(9)), - get(get_arg(10))); - } - - void write_guid_attribute(writer& w, TypeDef const& type) - { - auto fully_qualify_guid = (type.TypeNamespace() == "Windows.Foundation.Metadata"); - - w.write(R"([%("%")])", - fully_qualify_guid ? "global::System.Runtime.InteropServices.Guid" : "Guid", - bind(type, false)); - } - - void write_guid_bytes(writer& w, TypeDef const& type) - { - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); - if (!attribute) - { - throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); - } - - auto args = attribute.Value().FixedArgs(); - - using std::get; - - auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - - w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", - (get(get_arg(0)) >> 0) & 0xFF, - (get(get_arg(0)) >> 8) & 0xFF, - (get(get_arg(0)) >> 16) & 0xFF, - (get(get_arg(0)) >> 24) & 0xFF, - (get(get_arg(1)) >> 0) & 0xFF, - (get(get_arg(1)) >> 8) & 0xFF, - (get(get_arg(2)) >> 0) & 0xFF, - (get(get_arg(2)) >> 8) & 0xFF, - get(get_arg(3)), - get(get_arg(4)), - get(get_arg(5)), - get(get_arg(6)), - get(get_arg(7)), - get(get_arg(8)), - get(get_arg(9)), - get(get_arg(10))); - } void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool include_exclusive_interface, bool includeWindowsRuntimeObject) { From f29f4ea4f4523d69b34ef2d9c0fe30437569ebd2 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 19 Oct 2025 23:47:04 -0700 Subject: [PATCH 08/43] Fix compilation issues --- src/cswinrt/code_writers.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 226faa2f9..007d6f834 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -3428,10 +3428,10 @@ bool %HasErrors {get => %.HasErrors; } %event global::System.EventHandler %ErrorsChanged { -add => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.Get_ErrorsChanged2(%, this).Subscribe(value); -remove => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.Get_ErrorsChanged2(%, this).Unsubscribe(value); +add => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.ErrorsChanged(this, %).Subscribe(value); +remove => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.ErrorsChanged(this, %).Unsubscribe(value); } -%bool %HasErrors {get => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.get_HasErrors(%); } +%bool %HasErrors {get => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.HasErrors(%); } )", visibility, self, objref_name, visibility, self, objref_name, objref_name, @@ -4649,13 +4649,13 @@ evt.Name()); void write_event_source_table(writer& w, Event const& evt) { w.write(R"( -private static ConditionalWeakTable _% +private static ConditionalWeakTable _% { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { [MethodImpl(MethodImplOptions.NoInlining)] - static ConditionalWeakTable MakeTable() + static ConditionalWeakTable MakeTable() { _ = global::System.Threading.Interlocked.CompareExchange(ref field, [], null); @@ -6092,7 +6092,7 @@ public static unsafe void %(WindowsRuntimeObjectReference thisReference, % value w.write(R"( % -public static % %(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference) +public static % %(object thisObject, WindowsRuntimeObjectReference thisReference) { % return _%.GetOrAdd( From 5e3d23e2637b1d11845c18394bc4cbf438694728 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 00:20:50 -0700 Subject: [PATCH 09/43] generic IID fix --- src/cswinrt/code_writers.h | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 007d6f834..59791308d 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -794,6 +794,12 @@ namespace cswinrt static void write_iid_guid(writer& w, TypeDef const& type) { + if (distance(type.GenericParam()) != 0) + { + write_iid_guid_property_name(w, type); + return; + } + if (auto mapping = get_mapped_type(type.TypeNamespace(), type.TypeName())) { std::string name = w.write_temp("%", bind(type, typedef_name_type::NonProjected, true)); @@ -2464,13 +2470,13 @@ public %() private WindowsRuntimeObjectReference % { get - { + {% [MethodImpl(MethodImplOptions.NoInlining)] WindowsRuntimeObjectReference MakeObjectReference() { _ = global::System.Threading.Interlocked.CompareExchange( location1: ref field, - value: NativeObjectReference.As(%), + value: NativeObjectReference.As(%%), comparand: null); return field; @@ -2481,7 +2487,23 @@ private WindowsRuntimeObjectReference % } )", objrefname, - bind(ifaceType)); + [&](writer& w) { + if (distance(ifaceType.GenericParam()) != 0) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] +static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll")] object _); +)", + bind(ifaceType)); + } + }, + bind(ifaceType), + [&](writer& w){ + if (distance(ifaceType.GenericParam()) != 0) + { + w.write("(null)"); + } + }); /* TODO handle fast ABI From 836c892078705272727fa70ce791f378fa75a60c Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 00:39:32 -0700 Subject: [PATCH 10/43] Fix property with array return types --- src/cswinrt/code_writers.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 59791308d..6c473e100 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -7136,10 +7136,11 @@ private static unsafe int Do_Abi_%% bind(setter), bind( setter_sig, - w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).% = %", + w.write_temp("ComInterfaceDispatch.GetInstance<%>((ComInterfaceDispatch*)thisPtr).% = %%", type_name, prop.Name(), - "%"), + "%", + prop.Type().Type().is_szarray() ? ".ToArray()" : ""), false)); } From fe3c5e3ad2a2d25ded9b8b5649995083d428540d Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 01:02:22 -0700 Subject: [PATCH 11/43] Rename item and fix protected interface not projecting --- src/cswinrt/code_writers.h | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 6c473e100..b421f8308 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2899,8 +2899,8 @@ static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] -static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); @@ -2911,7 +2911,7 @@ static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Gene key, interop_method_name_prefix, key, value, // Keys value, interop_method_name_prefix, key, value, // Values interop_method_name_prefix, key, value, // Count -value, interop_method_name_prefix, key, value, key, // Indexer_Get +value, interop_method_name_prefix, key, value, key, // Item interop_method_name_prefix, key, value, key, // ContainsKey interop_method_name_prefix, key, value, key, value // TryGetValue ); @@ -2920,7 +2920,7 @@ interop_method_name_prefix, key, value, key, value // TryGetValue %IEnumerable<%> %Keys => %Keys(null, %); %IEnumerable<%> %Values => %Values(null, %); %int %Count => %Count(null, %); -%% %this[% key] => %Indexer_Get(null, %, key); +%% %this[% key] => %Item(null, %, key); %bool %ContainsKey(% key) => %ContainsKey(null, %, key); %bool %TryGetValue(% key, out % value) => %TryGetValue(null, %, key, out value); )", @@ -2983,11 +2983,11 @@ static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] -static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Set")] -static extern void %Indexer_Set([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); @@ -3019,8 +3019,8 @@ static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.< key, interop_method_name_prefix, key, value, // Keys value, interop_method_name_prefix, key, value, // Values interop_method_name_prefix, key, value, // Count -value, interop_method_name_prefix, key, value, key, // Indexer_Get -interop_method_name_prefix, key, value, key, value, // Indexer_Set +value, interop_method_name_prefix, key, value, key, // Item get +interop_method_name_prefix, key, value, key, value, // Item set interop_method_name_prefix, key, value, key, value, // Add interop_method_name_prefix, key, value, key, // ContainsKey interop_method_name_prefix, key, value, key, // Remove @@ -3039,8 +3039,8 @@ interop_method_name_prefix, key, value, key, value // Remove %bool %IsReadOnly => false; %% %this[% key] { -get => %Indexer_Get(null, %, key); -set => %Indexer_Set(null, %, key, value); +get => %Item(null, %, key); +set => %Item(null, %, key, value); } %void %Add(% key, % value) => %Add(null, %, key, value); %bool %ContainsKey(% key) => %ContainsKey(null, %, key); @@ -3132,17 +3132,17 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] -static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); )", interop_method_name_prefix, element, // Count -element, interop_method_name_prefix, element // Indexer_Get +element, interop_method_name_prefix, element // Item ); w.write(R"( %int %Count => %Count(null, %); % -%% %this[int index] => %Indexer_Get(null, %, index); +%% %this[int index] => %Item(null, %, index); )", visibility, ireadonlycollection, interop_method_name_prefix, objRefName, !emit_explicit ? R"([global::System.Runtime.CompilerServices.IndexerName("ReadOnlyListItem")])" : "", @@ -3180,15 +3180,15 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto target = "global::ABI.System.Collections.IListMethods"; w.write(R"( -%int %Count => %.get_Count(%); +%int %Count => %.Count(%); %bool %IsSynchronized => false; %object %SyncRoot => this; %void %CopyTo(Array array, int index) => %.CopyTo(%, array, index); % %object %this[int index] { -get => %.Indexer_Get(%, index); -set => %.Indexer_Set(%, index, value); +get => %.Item(%, index); +set => %.Item(%, index, value); } %bool %IsFixedSize => false; %bool %IsReadOnly => false; @@ -3328,11 +3328,11 @@ element, enumerable_type, target); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Get")] -static extern % %Indexer_Get([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Indexer_Set")] -static extern void %Indexer_Set([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "IndexOf")] static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); @@ -3359,8 +3359,8 @@ static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.< static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); )", interop_method_name_prefix, element, // Count -element, interop_method_name_prefix, element, // Indexer_Get -interop_method_name_prefix, element, element, // Indexer_Set +element, interop_method_name_prefix, element, // Item get +interop_method_name_prefix, element, element, // Item set interop_method_name_prefix, element, element, // IndexOf interop_method_name_prefix, element, element, // Insert interop_method_name_prefix, element, // RemoveAt @@ -3377,8 +3377,8 @@ interop_method_name_prefix, element, element // Remove % %% %this[int index] { -get => %Indexer_Get(null, %, index); -set => %Indexer_Set(null, %, index, value); +get => %Item(null, %, index); +set => %Item(null, %, index, value); } %int %IndexOf(% item) => %IndexOf(null, %, item); %void %Insert(int index, % item) => %Insert(null, %, index, item); @@ -3605,7 +3605,7 @@ visibility, self, objref_name); // Write IWindowsRuntimeInterface implementation for non-default interface and for the default interface if it isn't exclusive. // Otherwise, write the default interface in composable scenarios using its own function. - if (!is_exclusive_to(interface_type)) + if (!is_exclusive_to(interface_type) || is_overridable_interface || is_protected_interface) { w.write(R"( WindowsRuntimeObjectReferenceValue IWindowsRuntimeInterface<%>.GetInterface() From 1742ea2b33a5fa9bb182fb21adc5b74804a7d40c Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 14:43:46 -0700 Subject: [PATCH 12/43] More fixes to get projections compiling --- .../Marshalling/HStringReference.cs | 2 +- src/cswinrt/code_writers.h | 35 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/HStringReference.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/HStringReference.cs index e63bba164..0476babe6 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/HStringReference.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/HStringReference.cs @@ -13,7 +13,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] -public unsafe ref struct HStringReference +public unsafe struct HStringReference { /// /// The underlying header for the HSTRING reference. diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index b421f8308..1bf55a27b 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -4982,18 +4982,21 @@ Span<%> __%_span = %.Length <= 16 ? __%_inlineArray[..%.Length] : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent(%.Length)); )", + // SkipInit local_type, param_name, + // array local_type, - param_name, - local_type, - param_name, param_name, + // span + local_type, param_name, + get_escaped_param_name(w), param_name, + get_escaped_param_name(w), param_name, local_type, - param_name); + get_escaped_param_name(w)); if (!is_ref() && marshaler_type == "HStringMarshaller") { @@ -5004,14 +5007,17 @@ Span __%_referenceSpan = %.Length <= 16 ? __%_inlineReferenceArray[..%.Length] : (__%_referenceArrayFromPool = global::System.Buffers.ArrayPool.Shared.Rent(%.Length)); )", + // SkipInit param_name, + // array param_name, + // span param_name, + get_escaped_param_name(w), param_name, + get_escaped_param_name(w), param_name, - param_name, - param_name, - param_name); + get_escaped_param_name(w)); } } return; @@ -5025,7 +5031,7 @@ Span __%_referenceSpan = %.Length <= 16 local_type, get_marshaler_local(w), marshaler_type, - param_name); + get_escaped_param_name(w)); return; } @@ -5044,7 +5050,7 @@ Span __%_referenceSpan = %.Length <= 16 interop_dll_type != "" ? "" : marshaler_type + ".", interop_dll_type != "" ? "_" + param_name : "", interop_dll_type != "" ? "null, " : "", - param_name); + get_escaped_param_name(w)); } auto get_escaped_param_name(writer& w) const @@ -5064,7 +5070,7 @@ Span __%_referenceSpan = %.Length <= 16 w.write("% = %.ConvertToUnmanaged(%);\n", get_marshaler_local(w), marshaler_type, - param_name); + get_escaped_param_name(w)); } } @@ -5134,7 +5140,7 @@ CopyToUnmanagedUnsafe_%( headers: _%_reference); )", param_name, - param_name, + get_escaped_param_name(w), param_name, param_name, param_name, @@ -5149,7 +5155,7 @@ CopyToUnmanagedUnsafe_%( else if (!is_pinnable && is_pinnable_array_data() && !is_ref()) { write_copy_to_unmanaged_function(w); - w.write("CopyToUnmanaged_%(null, %, (uint)%.Length, &_%);\n", param_name, param_name, param_name, param_name); + w.write("CopyToUnmanaged_%(null, %, (uint)%.Length, &_%);\n", param_name, get_escaped_param_name(w), get_escaped_param_name(w), param_name); } } @@ -5161,7 +5167,7 @@ CopyToUnmanagedUnsafe_%( { if (is_pinnable || is_pinnable_array_data()) { - w.write("(uint) %.Length, _%", param_name, param_name); + w.write("(uint) %.Length, _%", get_escaped_param_name(w), param_name); } else { @@ -6569,10 +6575,13 @@ Span<%> __% = __%Size <= 16 ? __%_inlineArray[..(int)__%Size] : (__%_arrayFromPool = global::System.Buffers.ArrayPool<%>.Shared.Rent((int)__%Size)); )", + // SkipInit param_type, param_name, + // Array param_type, param_name, + // Span param_type, param_name, param_name, From d20640fcd37d936e390d771763ed93cc47085617 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 14:44:39 -0700 Subject: [PATCH 13/43] Fix spacing --- src/cswinrt/code_writers.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 1bf55a27b..5ce32e3a1 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -5103,7 +5103,7 @@ Span __%_referenceSpan = %.Length <= 16 w.write(", _%_reference = __%_referenceSpan", param_name, param_name); - } + } } else { @@ -5421,7 +5421,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); } )", param_name, - local_type, + local_type, param_name); } else @@ -5496,7 +5496,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); if (m.local_type == "void*") { m.local_type = "nint"; - } + } } else { @@ -6692,7 +6692,7 @@ static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT. w.write("CopyToManaged_%(null, __%Size, &%, __%);", param_name, param_name, - bind(param_name), + bind(param_name), param_name); } } @@ -7071,7 +7071,7 @@ return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); return_marshaler.write_marshal_from_managed(w); }, [&](writer& w) { - bool write_dispose = false; + bool write_dispose = false; w.write(bind_each([&](writer&, managed_marshaler const& m) { write_dispose |= m.need_dispose(); From 2ffe69030727cbaf3ed3cbf4edd37e165a52c578 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 20 Oct 2025 16:17:04 -0700 Subject: [PATCH 14/43] Move couple event sources to be with where the delegates are defined. --- .../Specialized/INotifyCollectionChanged.cs | 8 ++++---- .../NotifyCollectionChangedEventHandlerEventSource.cs} | 10 ++++++---- .../System/ComponentModel/INotifyPropertyChanged.cs | 8 ++++---- .../PropertyChangedEventHandlerEventSource.cs} | 10 ++++++---- 4 files changed, 20 insertions(+), 16 deletions(-) rename src/WinRT.Runtime2/{InteropServices/Events/NotifyCollectionChangedEventSource.cs => ABI/System/Collections/Specialized/NotifyCollectionChangedEventHandlerEventSource.cs} (81%) rename src/WinRT.Runtime2/{InteropServices/Events/PropertyChangedEventSource.cs => ABI/System/ComponentModel/PropertyChangedEventHandlerEventSource.cs} (81%) diff --git a/src/WinRT.Runtime2/ABI/System/Collections/Specialized/INotifyCollectionChanged.cs b/src/WinRT.Runtime2/ABI/System/Collections/Specialized/INotifyCollectionChanged.cs index 978b2f51b..d7f417a33 100644 --- a/src/WinRT.Runtime2/ABI/System/Collections/Specialized/INotifyCollectionChanged.cs +++ b/src/WinRT.Runtime2/ABI/System/Collections/Specialized/INotifyCollectionChanged.cs @@ -53,13 +53,13 @@ public static unsafe class INotifyCollectionChangedMethods /// /// The table for . /// - private static ConditionalWeakTable CollectionChangedTable + private static ConditionalWeakTable CollectionChangedTable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { [MethodImpl(MethodImplOptions.NoInlining)] - static ConditionalWeakTable MakeCollectionChangedTable() + static ConditionalWeakTable MakeCollectionChangedTable() { _ = Interlocked.CompareExchange(ref field, [], null); @@ -71,11 +71,11 @@ static ConditionalWeakTable - public static NotifyCollectionChangedEventSource CollectionChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference) + public static NotifyCollectionChangedEventHandlerEventSource CollectionChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference) { return CollectionChangedTable.GetOrAdd( key: thisObject, - valueFactory: static (_, thisReference) => new NotifyCollectionChangedEventSource(thisReference, 6), + valueFactory: static (_, thisReference) => new NotifyCollectionChangedEventHandlerEventSource(thisReference, 6), factoryArgument: thisReference); } } diff --git a/src/WinRT.Runtime2/InteropServices/Events/NotifyCollectionChangedEventSource.cs b/src/WinRT.Runtime2/ABI/System/Collections/Specialized/NotifyCollectionChangedEventHandlerEventSource.cs similarity index 81% rename from src/WinRT.Runtime2/InteropServices/Events/NotifyCollectionChangedEventSource.cs rename to src/WinRT.Runtime2/ABI/System/Collections/Specialized/NotifyCollectionChangedEventHandlerEventSource.cs index 7c50608fb..5e5cb96d0 100644 --- a/src/WinRT.Runtime2/InteropServices/Events/NotifyCollectionChangedEventSource.cs +++ b/src/WinRT.Runtime2/ABI/System/Collections/Specialized/NotifyCollectionChangedEventHandlerEventSource.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Specialized; using System.ComponentModel; +using WindowsRuntime; +using WindowsRuntime.InteropServices; -namespace WindowsRuntime.InteropServices; +namespace ABI.System.Collections.Specialized; /// /// An implementation for . @@ -14,10 +16,10 @@ namespace WindowsRuntime.InteropServices; DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] -public sealed unsafe class NotifyCollectionChangedEventSource : EventSource +public sealed unsafe class NotifyCollectionChangedEventHandlerEventSource : EventSource { /// - public NotifyCollectionChangedEventSource(WindowsRuntimeObjectReference nativeObjectReference, int index) + public NotifyCollectionChangedEventHandlerEventSource(WindowsRuntimeObjectReference nativeObjectReference, int index) : base(nativeObjectReference, index) { } @@ -35,7 +37,7 @@ protected override EventSourceState CreateE } /// - /// The implementation for . + /// The implementation for . /// private sealed class EventState : EventSourceState { diff --git a/src/WinRT.Runtime2/ABI/System/ComponentModel/INotifyPropertyChanged.cs b/src/WinRT.Runtime2/ABI/System/ComponentModel/INotifyPropertyChanged.cs index 4da81148d..f7a659811 100644 --- a/src/WinRT.Runtime2/ABI/System/ComponentModel/INotifyPropertyChanged.cs +++ b/src/WinRT.Runtime2/ABI/System/ComponentModel/INotifyPropertyChanged.cs @@ -52,13 +52,13 @@ public static unsafe class INotifyPropertyChangedMethods /// /// The table for . /// - private static ConditionalWeakTable PropertyChangedTable + private static ConditionalWeakTable PropertyChangedTable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { [MethodImpl(MethodImplOptions.NoInlining)] - static ConditionalWeakTable MakePropertyChanged() + static ConditionalWeakTable MakePropertyChanged() { _ = Interlocked.CompareExchange(ref field, [], null); @@ -70,11 +70,11 @@ static ConditionalWeakTable Ma } /// - public static PropertyChangedEventSource PropertyChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference) + public static PropertyChangedEventHandlerEventSource PropertyChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference) { return PropertyChangedTable.GetOrAdd( key: thisObject, - valueFactory: static (_, thisReference) => new PropertyChangedEventSource(thisReference, 6), + valueFactory: static (_, thisReference) => new PropertyChangedEventHandlerEventSource(thisReference, 6), factoryArgument: thisReference); } } diff --git a/src/WinRT.Runtime2/InteropServices/Events/PropertyChangedEventSource.cs b/src/WinRT.Runtime2/ABI/System/ComponentModel/PropertyChangedEventHandlerEventSource.cs similarity index 81% rename from src/WinRT.Runtime2/InteropServices/Events/PropertyChangedEventSource.cs rename to src/WinRT.Runtime2/ABI/System/ComponentModel/PropertyChangedEventHandlerEventSource.cs index 129a5360c..0d6fc1da3 100644 --- a/src/WinRT.Runtime2/InteropServices/Events/PropertyChangedEventSource.cs +++ b/src/WinRT.Runtime2/ABI/System/ComponentModel/PropertyChangedEventHandlerEventSource.cs @@ -3,8 +3,10 @@ using System; using System.ComponentModel; +using WindowsRuntime; +using WindowsRuntime.InteropServices; -namespace WindowsRuntime.InteropServices; +namespace ABI.System.ComponentModel; /// /// An implementation for . @@ -13,10 +15,10 @@ namespace WindowsRuntime.InteropServices; DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] -public sealed unsafe class PropertyChangedEventSource : EventSource +public sealed unsafe class PropertyChangedEventHandlerEventSource : EventSource { /// - public PropertyChangedEventSource(WindowsRuntimeObjectReference nativeObjectReference, int index) + public PropertyChangedEventHandlerEventSource(WindowsRuntimeObjectReference nativeObjectReference, int index) : base(nativeObjectReference, index) { } @@ -34,7 +36,7 @@ protected override EventSourceState CreateEventSour } /// - /// The implementation for . + /// The implementation for . /// private sealed class EventState : EventSourceState { From 08a3094fa0e048d36b044837674fc9f9172a54e5 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 21 Oct 2025 01:17:44 -0700 Subject: [PATCH 15/43] Fix issues --- src/cswinrt/code_writers.h | 27 +++++++++++++++++---------- src/cswinrt/helpers.h | 7 +++++++ src/cswinrt/main.cpp | 10 +++++----- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 5ce32e3a1..8f9647f69 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2198,6 +2198,11 @@ remove => %; write_custom_attributes(w, type.CustomAttribute(), enable_platform_attrib); } + void write_constructor_callback_method_name(writer& w, MethodDef const& method) + { + w.write("%_%", method.Name(), size(method.Signature().Params())); + } + struct attributed_type { TypeDef type; @@ -2397,7 +2402,7 @@ private static class _% class_type.TypeName(), bind_list(", ", signature.params()), // base - method.Name(), + bind(method), bind(default_type_semantics), bind_list(", ", signature.params()), [&](writer& w) @@ -2570,7 +2575,7 @@ static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll") else { w.write("%, %, [%]", - method.Name(), + bind(method), bind(default_type_semantics), bind_list(", ", params_without_objects)); } @@ -5827,7 +5832,7 @@ void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); % } )", - method.Name(), + bind(method), cache_object, bind(write_constructor_params_as_variables, signature), bind(invoke_target, "", is_generic, abi_marshalers, is_noexcept(method))); @@ -5891,7 +5896,7 @@ void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); % } )", - method.Name(), + bind(method), cache_object, bind(write_composable_constructor_params_as_variables, signature), bind(invoke_target, "", is_generic, abi_marshalers, is_noexcept(method)) @@ -7245,16 +7250,18 @@ return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] static extern % ConvertToManaged([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, void* value); -var __handler = ConvertToManaged(null, handler); +var __handler = ConvertToManaged(null, %); )", -bind(semantics, typedef_name_type::Projected, false), -bind(semantics) + bind(semantics, typedef_name_type::Projected, false), + bind(semantics), + handler_parameter_name ); } else { - w.write("var __handler = %Marshaller.ConvertToManaged(handler);", - bind(semantics, typedef_name_type::ABI, false)); + w.write("var __handler = %Marshaller.ConvertToManaged(%);", + bind(semantics, typedef_name_type::ABI, false), + handler_parameter_name); } }, add_handler_event_token_name, @@ -8409,7 +8416,7 @@ return false; %%%% delegate % %(%); )", bind(type), - bind(type, true), + bind(type, false), bind(type), internal_accessibility(), bind(signature), diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index bc3ef1b86..e3d717a19 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -784,16 +784,21 @@ namespace cswinrt }, { "Windows.Foundation", { + { "AsyncActionCompletedHandler", "Windows.Foundation", "AsyncActionCompletedHandler" }, + { "AsyncStatus", "Windows.Foundation", "AsyncStatus" }, { "DateTime", "System", "DateTimeOffset", true }, { "EventHandler`1", "System", "EventHandler", false }, { "EventRegistrationToken", "WindowsRuntime.InteropServices", "EventRegistrationToken", false }, { "FoundationContract", "Windows.Foundation", "FoundationContract"}, { "HResult", "System", "Exception", true }, + { "IAsyncAction", "Windows.Foundation", "IAsyncAction" }, + { "IAsyncInfo", "Windows.Foundation", "IAsyncInfo" }, { "IClosable", "System", "IDisposable", true, true }, { "IPropertyValue", "Windows.Foundation", "IPropertyValue", true }, { "IReferenceArray`1", "Windows.Foundation", "IReferenceArray", true }, { "IReference`1", "System", "Nullable", true }, { "Point", "Windows.Foundation", "Point" }, + { "PropertyType", "Windows.Foundation", "PropertyType" }, { "Rect", "Windows.Foundation", "Rect" }, { "Size", "Windows.Foundation", "Size" }, { "TimeSpan", "System", "TimeSpan", true }, @@ -803,11 +808,13 @@ namespace cswinrt }, { "Windows.Foundation.Collections", { + { "CollectionChange", "Windows.Foundation.Collections", "CollectionChange" }, { "IIterable`1", "System.Collections.Generic", "IEnumerable`1", true, true }, { "IIterator`1", "System.Collections.Generic", "IEnumerator`1", true, true }, { "IKeyValuePair`2", "System.Collections.Generic", "KeyValuePair`2", true }, { "IMapView`2", "System.Collections.Generic", "IReadOnlyDictionary`2", true, true }, { "IMap`2", "System.Collections.Generic", "IDictionary`2", true, true }, + { "IVectorChangedEventArgs", "Windows.Foundation.Collections", "IVectorChangedEventArgs" }, { "IVectorView`1", "System.Collections.Generic", "IReadOnlyList`1", true, true }, { "IVector`1", "System.Collections.Generic", "IList`1", true, true }, } diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index de5097e5a..160af8a95 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -202,7 +202,7 @@ Where is one or more of: for (auto&& [name, type] : members.types) { if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name)) + if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) { continue; } @@ -247,7 +247,7 @@ Where is one or more of: { currentType = name; if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name)) + if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) { continue; } @@ -290,7 +290,7 @@ Where is one or more of: { currentType = name; if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name)) + if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) { written = true; continue; @@ -353,7 +353,7 @@ Where is one or more of: { currentType = name; if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name)) continue; + if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) continue; if (is_api_contract_type(type)) { continue; } if (is_attribute_type(type)) { continue; } auto guard{ w.push_generic_params(type.GenericParam()) }; @@ -389,7 +389,7 @@ Where is one or more of: // Custom additions to namespaces for (auto addition : strings::additions) { - if (ns == addition.name && settings.addition_filter.includes(ns)) + if (ns == addition.name && ns == "Windows.UI" && settings.addition_filter.includes(ns)) { w.write(addition.value); } From dbd7fd5a2bf1a211cb5b2851dc7f3d8070e8d102 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Wed, 22 Oct 2025 17:47:58 -0700 Subject: [PATCH 16/43] More fixes while trying to generate the Windows SDK projection --- ...MapChangedEventHandlerEventSource{K, V}.cs | 4 +- .../ABI/Windows.Foundation/TokenizerHelper.cs | 39 ++++ ...IObservableMapMethodsImpl{TKey, TValue}.cs | 1 + .../Windows.Foundation/Point.cs | 25 +-- src/WinRT.Runtime2/Windows.Foundation/Rect.cs | 4 +- src/cswinrt/code_writers.h | 181 ++++++++++++++---- src/cswinrt/helpers.h | 42 +++- .../additions/Windows.UI/Windows.UI.cs | 101 +--------- 8 files changed, 234 insertions(+), 163 deletions(-) rename src/WinRT.Runtime2/{InteropServices/Events => ABI/Windows.Foundation/Collections}/MapChangedEventHandlerEventSource{K, V}.cs (94%) create mode 100644 src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs diff --git a/src/WinRT.Runtime2/InteropServices/Events/MapChangedEventHandlerEventSource{K, V}.cs b/src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/MapChangedEventHandlerEventSource{K, V}.cs similarity index 94% rename from src/WinRT.Runtime2/InteropServices/Events/MapChangedEventHandlerEventSource{K, V}.cs rename to src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/MapChangedEventHandlerEventSource{K, V}.cs index a9493575f..1e9ca86f8 100644 --- a/src/WinRT.Runtime2/InteropServices/Events/MapChangedEventHandlerEventSource{K, V}.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/MapChangedEventHandlerEventSource{K, V}.cs @@ -4,8 +4,10 @@ using System; using System.ComponentModel; using Windows.Foundation.Collections; +using WindowsRuntime; +using WindowsRuntime.InteropServices; -namespace WindowsRuntime.InteropServices; +namespace ABI.Windows.Foundation.Collections; /// /// An implementation for . diff --git a/src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs b/src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs new file mode 100644 index 000000000..4b0c013cf --- /dev/null +++ b/src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Globalization; +using WindowsRuntime; + +namespace ABI.Windows.Foundation; + +/// +/// Helper class for tokenizing used by generated projections. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static class TokenizerHelper +{ + /// + /// Gets the numeric list separator for a given provider. + /// + /// The input provider to use for formatting. + /// The numeric list separator to use. + public static char GetNumericListSeparator(IFormatProvider? provider) + { + const char CommaSeparator = ','; + const char SemicolonSeparator = ';'; + + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + + // If the decimal separator is ',', use ';', otherwise use ',' + return + (numberFormat.NumberDecimalSeparator.Length > 0) && + (numberFormat.NumberDecimalSeparator[0] == CommaSeparator) + ? SemicolonSeparator + : CommaSeparator; + } +} diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs index dab1be7f7..89205737e 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using ABI.Windows.Foundation.Collections; namespace WindowsRuntime.InteropServices; diff --git a/src/WinRT.Runtime2/Windows.Foundation/Point.cs b/src/WinRT.Runtime2/Windows.Foundation/Point.cs index eb2282e37..6823c15d5 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Point.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Point.cs @@ -4,7 +4,6 @@ using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -81,7 +80,7 @@ public override readonly int GetHashCode() /// public override readonly string ToString() { - char separator = GetNumericListSeparator(null); + char separator = TokenizerHelper.GetNumericListSeparator(null); return $"{X}{separator}{Y}"; } @@ -96,7 +95,7 @@ public readonly string ToString(string? format, IFormatProvider? formatProvider) } // We need the separator as a 'string', so we can pass it as a literal - string separator = GetNumericListSeparator(formatProvider) is ',' ? "," : ";"; + string separator = TokenizerHelper.GetNumericListSeparator(formatProvider) is ',' ? "," : ";"; DefaultInterpolatedStringHandler handler = new( literalLength: 1, @@ -111,26 +110,6 @@ public readonly string ToString(string? format, IFormatProvider? formatProvider) return handler.ToStringAndClear(); } - /// - /// Gets the numeric list separator for a given provider. - /// - /// The input provider to use for formatting. - /// The numeric list separator to use. - internal static char GetNumericListSeparator(IFormatProvider? provider) - { - const char CommaSeparator = ','; - const char SemicolonSeparator = ';'; - - NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); - - // If the decimal separator is ',', use ';', otherwise use ',' - return - (numberFormat.NumberDecimalSeparator.Length > 0) && - (numberFormat.NumberDecimalSeparator[0] == CommaSeparator) - ? SemicolonSeparator - : CommaSeparator; - } - /// /// Returns a value that indicates whether each pair of elements in two specified values is equal. /// diff --git a/src/WinRT.Runtime2/Windows.Foundation/Rect.cs b/src/WinRT.Runtime2/Windows.Foundation/Rect.cs index b97ffc65c..552552d2e 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Rect.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Rect.cs @@ -293,7 +293,7 @@ public override readonly string ToString() return "Empty"; } - char separator = Point.GetNumericListSeparator(null); + char separator = TokenizerHelper.GetNumericListSeparator(null); return $"{X}{separator}{Y}{separator}{Width}{separator}{Height}"; } @@ -308,7 +308,7 @@ public readonly string ToString(string? format, IFormatProvider? formatProvider) } // We need the separator as a 'string', so we can pass it as a literal - string separator = Point.GetNumericListSeparator(formatProvider) is ',' ? "," : ";"; + string separator = TokenizerHelper.GetNumericListSeparator(formatProvider) is ',' ? "," : ";"; DefaultInterpolatedStringHandler handler = new( literalLength: 3, diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 8f9647f69..4b8959d26 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -549,6 +549,19 @@ namespace cswinrt write_parameter_name(w, param); } + // This is used to handle scenarios where we do boxing with parameters passed to the constructor. + // This means for pass_array which we project as a ReadOnlySpan, we need to convert it to an array + // to be able to box it. + void write_constructor_parameter_name_with_modifier(writer& w, method_signature::param_t const& param) + { + write_parameter_name_with_modifier(w, param); + + if (get_param_category(param) == param_category::pass_array) + { + w.write(".ToArray()"); + } + } + void write_projection_parameter_type(writer& w, method_signature::param_t const& param) { auto semantics = get_type_semantics(param.second->Type()); @@ -576,6 +589,20 @@ namespace cswinrt } } + // This is similar to write_constructor_parameter_name_with_modifier but used in the callback. + void write_constructor_parameter_type(writer& w, method_signature::param_t const& param) + { + if (get_param_category(param) == param_category::pass_array) + { + auto semantics = get_type_semantics(param.second->Type()); + w.write("%[]", bind(semantics)); + } + else + { + write_projection_parameter_type(w, param); + } + } + void write_projected_signature(writer& w, TypeSig const& type_sig) { write_projection_type(w, get_type_semantics(type_sig)); @@ -797,6 +824,8 @@ namespace cswinrt if (distance(type.GenericParam()) != 0) { write_iid_guid_property_name(w, type); + // This is a unsafe accessor call, so write the function call to it too. + w.write("(null)"); return; } @@ -1384,19 +1413,38 @@ namespace cswinrt void write_abi_event_source_static_method_call(writer& w, type_semantics const& iface, Event const& evt, bool isSubscribeCall, std::string const& targetObjRef, bool is_static_event = false) { - w.write("%.%(%, %).%(value)", - bind(iface, typedef_name_type::StaticAbiClass, true), - evt.Name(), + bool is_unsafe_accessor_call = false; + w.write("%(%, %).%(value)", bind([&](writer& w) { - if (is_static_event) + for_typedef(w, iface, [&](type_definition const& type) + { + if (size(type.GenericParam()) != 0) { - w.write("%", targetObjRef); + is_unsafe_accessor_call = true; + auto interface_name = w.write_temp("%", bind(type, typedef_name_type::Projected, true)); + w.write("%_%", escape_type_name_for_identifier(interface_name, true), evt.Name()); } else { - w.write("(WindowsRuntimeObject)this"); + w.write("%.%", bind(type, typedef_name_type::StaticAbiClass, true), evt.Name()); } - }), + }); + }), + bind([&](writer& w) { + if (is_unsafe_accessor_call) + { + w.write("null, "); + } + + if (is_static_event) + { + w.write("%", targetObjRef); + } + else + { + w.write("(WindowsRuntimeObject)this"); + } + }), targetObjRef, isSubscribeCall ? "Subscribe" : "Unsubscribe"); } @@ -1798,17 +1846,33 @@ private % Make_%() std::optional> paramsForStaticMethodCall = {}) { auto event_type = w.write_temp("%", bind(get_type_semantics(event.EventType()), typedef_name_type::Projected, false)); + auto parent_type = event.Parent(); // Microsoft.UI.Xaml.Input.ICommand has a lower-fidelity type mapping where the type of the event handler doesn't project one-to-one // so we need to hard-code mapping the event handler from the mapped WinRT type to the correct .NET type. if (event.Name() == "CanExecuteChanged" && event_type == "global::System.EventHandler") { - auto parent_type = w.write_temp("%", bind(event.Parent(), typedef_name_type::NonProjected, true)); - if (parent_type == "Microsoft.UI.Xaml.Input.ICommand" || parent_type == "Windows.UI.Xaml.Input.ICommand") + auto parent_type_name = w.write_temp("%", bind(parent_type, typedef_name_type::NonProjected, true)); + if (parent_type_name == "Microsoft.UI.Xaml.Input.ICommand" || parent_type_name == "Windows.UI.Xaml.Input.ICommand") { event_type = "global::System.EventHandler"; } } + + if (paramsForStaticMethodCall.has_value() && size(parent_type.GenericParam()) != 0) + { + auto parent_type_name = w.write_temp("%", bind(parent_type, typedef_name_type::Projected, true)); + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] +static extern % %_%([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, object thisObject, WindowsRuntimeObjectReference thisReference); +)", + external_event_name, + bind(get_type_semantics(event.EventType()), typedef_name_type::EventSource, false), + escape_type_name_for_identifier(parent_type_name, true), + external_event_name, + bind(parent_type)); + } + w.write(R"( %%%event % % { @@ -2404,7 +2468,7 @@ private static class _% // base bind(method), bind(default_type_semantics), - bind_list(", ", signature.params()), + bind_list(", ", signature.params()), [&](writer& w) { if (!gc_pressure_amount) return; @@ -2454,6 +2518,16 @@ public %() w.write("%", escape_type_name_for_identifier(objRefTypeName)); } + void write_unsafe_accessor_for_iid(writer& w, TypeDef ifaceType, bool isInNullableContext = false) + { + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] +static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll")] object% _); +)", + bind(ifaceType), + isInNullableContext ? "?" : ""); + } + void write_class_objrefs_definition(writer& w, TypeDef const& classType, bool replaceDefaultByInner) { for (auto&& ii : classType.InterfaceImpl()) @@ -2467,7 +2541,7 @@ public %() } auto objrefname = bind(semantics); - bool useInner = replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute") && distance(ifaceType.GenericParam()) == 0; + bool useInner = replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); if (!useInner) { @@ -2495,19 +2569,16 @@ private WindowsRuntimeObjectReference % [&](writer& w) { if (distance(ifaceType.GenericParam()) != 0) { - w.write(R"( -[UnsafeAccessor(UnsafeAccessorKind.StaticMethod)] -static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll")] object _); -)", - bind(ifaceType)); + write_unsafe_accessor_for_iid(w, ifaceType); } }, bind(ifaceType), - [&](writer& w){ - if (distance(ifaceType.GenericParam()) != 0) - { - w.write("(null)"); - } + [&](writer&){ + // Unsafe accessor call + //if (distance(ifaceType.GenericParam()) != 0) + //{ + // w.write("(null)"); + //} }); /* @@ -2521,6 +2592,12 @@ static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll") else { w.write(R"(private WindowsRuntimeObjectReference % => NativeObjectReference;)", objrefname); + + // Write it here so that we can pass the default interface IID as part of the constructor. + if (distance(ifaceType.GenericParam()) != 0) + { + write_unsafe_accessor_for_iid(w, ifaceType); + } } }); } @@ -2577,7 +2654,7 @@ static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll") w.write("%, %, [%]", bind(method), bind(default_type_semantics), - bind_list(", ", params_without_objects)); + bind_list(", ", params_without_objects)); } }), gc_pressure); @@ -4011,24 +4088,29 @@ return %.AsValue(); switch (get_category(td)) { case category::interface_type: - w.write("WindowsRuntimeObjectMarshaller.Free(value.%);\n", field_name); - break; case category::class_type: - w.write("WindowsRuntimeObjectMarshaller.Free(value.%);\n", field_name); - break; case category::delegate_type: w.write("WindowsRuntimeObjectMarshaller.Free(value.%);\n", field_name); break; - case category::enum_type: - break; case category::struct_type: if (!is_type_blittable(td)) { - w.write("%Marshaller.Dispose(value.%);\n", td.TypeName(), field_name); + if (td.TypeNamespace() == "Windows.Foundation" && + (td.TypeName() == "DateTime" || + td.TypeName() == "TimeSpan" || + td.TypeName() == "HResult")) + { + // These types require marshaling due to being custom mapped, + // but are still blittable and don't have disposer. + break; + } + + w.write("%Marshaller.Dispose(value.%);\n", + bind(td, typedef_name_type::ABI, true), + field_name); } break; default: - w.write("// Unsupported type_definition for %\n", field_name); break; } }, @@ -5805,9 +5887,10 @@ finally auto const& params = method_sig.params(); for (size_t i = 0; i < params.size(); i++) { - w.write("% = (%)additionalParameters[%];\n", - bind(params[i]), - bind(params[i]), + w.write("% % = (%)additionalParameters[%];\n", + bind(params[i]), + bind(params[i]), + bind(params[i]), i); } }; @@ -5853,9 +5936,10 @@ void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); auto const& params = method_sig.params(); for (size_t i = 0; i < params.size() - 2; i++) { - w.write("% = (%)additionalParameters[%];\n", - bind(params[i]), - bind(params[i]), + w.write("% % = (%)additionalParameters[%];\n", + bind(params[i]), + bind(params[i]), + bind(params[i]), i); } }; @@ -8316,7 +8400,7 @@ return value.GetDefaultInterface(); w.write(R"( file sealed unsafe class %ComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute -{ +{% public override object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) { WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReference( @@ -8329,6 +8413,15 @@ return new %(valueReference); } )", type.TypeName(), + bind([&](writer& w) { + for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) + { + if (size(interface_type.GenericParam()) != 0) + { + write_unsafe_accessor_for_iid(w, interface_type, true); + } + }); + }), bind(default_type_semantics), bind(type, typedef_name_type::Projected, true)); } @@ -8342,7 +8435,7 @@ return new %(valueReference); { w.write(R"( file sealed unsafe class %ComWrappersCallback : IWindowsRuntimeObjectComWrappersCallback -{ +{% public static object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags) { WindowsRuntimeObjectReference valueReference = WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe( @@ -8355,6 +8448,15 @@ return new %(valueReference); } )", type.TypeName(), + bind([&](writer& w) { + for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) + { + if (size(interface_type.GenericParam()) != 0) + { + write_unsafe_accessor_for_iid(w, interface_type, true); + } + }); + }), bind(default_type_semantics), bind(type, typedef_name_type::Projected, true)); } @@ -8709,10 +8811,11 @@ R"( } // struct - w.write("%%%public struct %: IEquatable<%>\n{\n", + w.write("%%%public% struct %: IEquatable<%>\n{\n", bind(type), bind(type), bind(type), + has_addition_to_type(type) ? " partial" : "", type.TypeName(), type.TypeName()); diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index e3d717a19..de5ff356b 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -837,11 +837,6 @@ namespace cswinrt { "Vector4", "System.Numerics", "Vector4" }, } }, - { "Windows.UI", - { - { "Color", "Windows.UI", "Color" }, - } - }, { "Windows.UI.Xaml", { { "CornerRadius", "Windows.UI.Xaml", "CornerRadius" }, @@ -1197,6 +1192,43 @@ namespace cswinrt return versionItr->platform_version; } + bool has_addition_to_type(TypeDef const& type) + { + static const struct + { + std::string_view name_space; + std::vector types; + } addition_types[] = + { + { "Windows.UI", + { + "Color", + } + }, + }; + + auto nsItr = std::lower_bound(std::begin(addition_types), std::end(addition_types), type.TypeNamespace(), [](auto&& v, std::string_view ns) + { + return v.name_space < ns; + }); + + if ((nsItr == std::end(addition_types)) || (nsItr->name_space != type.TypeNamespace())) + { + return false; + } + + auto nameItr = std::lower_bound(nsItr->types.begin(), nsItr->types.end(), type.TypeName(), [](auto&& v, std::string_view name) + { + return v < name; + }); + if ((nameItr == nsItr->types.end()) || (*nameItr != type.TypeName())) + { + return false; + } + + return true; + } + enum class typedef_name_type { Projected, diff --git a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs index 5987fac9a..fb7cde439 100644 --- a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs +++ b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs @@ -4,24 +4,8 @@ namespace Windows.UI using global::System; using global::System.Globalization; - [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Color))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Color : IFormattable + partial struct Color : IFormattable { - private byte _A; - private byte _R; - private byte _G; - private byte _B; - public static Color FromArgb(byte a, byte r, byte g, byte b) { Color c1 = default; @@ -34,30 +18,6 @@ public static Color FromArgb(byte a, byte r, byte g, byte b) return c1; } - public byte A - { - get { return _A; } - set { _A = value; } - } - - public byte R - { - get { return _R; } - set { _R = value; } - } - - public byte G - { - get { return _G; } - set { _G = value; } - } - - public byte B - { - get { return _B; } - set { _B = value; } - } - public override string ToString() { // Delegate to the internal method which implements all ToString calls. @@ -82,67 +42,22 @@ internal string ConvertToString(string format, IFormatProvider provider) if (format == null) { - sb.AppendFormat(provider, "#{0:X2}", _A); - sb.AppendFormat(provider, "{0:X2}", _R); - sb.AppendFormat(provider, "{0:X2}", _G); - sb.AppendFormat(provider, "{0:X2}", _B); + sb.AppendFormat(provider, "#{0:X2}", A); + sb.AppendFormat(provider, "{0:X2}", R); + sb.AppendFormat(provider, "{0:X2}", G); + sb.AppendFormat(provider, "{0:X2}", B); } else { // Helper to get the numeric list separator for a given culture. - char separator = Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); + char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); sb.AppendFormat(provider, "sc#{1:" + format + "}{0} {2:" + format + "}{0} {3:" + format + "}{0} {4:" + format + "}", - separator, _A, _R, _G, _B); + separator, A, R, G, B); } return sb.ToString(); } - - public override int GetHashCode() - { - return _A.GetHashCode() ^ _R.GetHashCode() ^ _G.GetHashCode() ^ _B.GetHashCode(); - } - - public override bool Equals(object o) - { - return o is Color && this == (Color)o; - } - - public bool Equals(Color color) - { - return this == color; - } - - public static bool operator ==(Color color1, Color color2) - { - return - color1.R == color2.R && - color1.G == color2.G && - color1.B == color2.B && - color1.A == color2.A; - } - - public static bool operator !=(Color color1, Color color2) - { - return (!(color1 == color2)); - } - } -} - -namespace ABI.Windows.UI -{ -#if EMBED - internal -#else - public -#endif - static class Color - { - public static string GetGuidSignature() - { - return "struct(Windows.UI.Color;u1;u1;u1;u1)"; - } } -} +} \ No newline at end of file From 925f399bf05ee5cd9ed8acda6721aa4abfc64d8f Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Wed, 22 Oct 2025 18:18:36 -0700 Subject: [PATCH 17/43] Constructor fixes --- src/cswinrt/code_writers.h | 53 +++++++++++++++----------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 4b8959d26..a98082c66 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2555,7 +2555,7 @@ private WindowsRuntimeObjectReference % { _ = global::System.Threading.Interlocked.CompareExchange( location1: ref field, - value: NativeObjectReference.As(%%), + value: NativeObjectReference.As(%), comparand: null); return field; @@ -2572,14 +2572,7 @@ private WindowsRuntimeObjectReference % write_unsafe_accessor_for_iid(w, ifaceType); } }, - bind(ifaceType), - [&](writer&){ - // Unsafe accessor call - //if (distance(ifaceType.GenericParam()) != 0) - //{ - // w.write("(null)"); - //} - }); + bind(ifaceType)); /* TODO handle fast ABI @@ -2605,7 +2598,12 @@ private WindowsRuntimeObjectReference % void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) { - write_static_objref_definition(w, composable_type, class_type); + // Write the factory objref if there are constructors on this type. + if (size(composable_type.MethodList()) != 0) + { + write_static_objref_definition(w, composable_type, class_type); + } + auto cache_object = bind(composable_type); auto gc_pressure_amount = get_gc_pressure_amount(class_type); auto gc_pressure = w.write_temp("%", @@ -2617,8 +2615,6 @@ private WindowsRuntimeObjectReference % auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); - bool has_constructor_without_parameters = false; - bool has_constructor_with_parameters = false; for (auto&& method : composable_type.MethodList()) { method_signature signature{ method }; @@ -2665,14 +2661,9 @@ private WindowsRuntimeObjectReference % { write_static_composing_factory_method(w, composable_type, method); } - - has_constructor_without_parameters |= params_without_objects.empty(); - has_constructor_with_parameters |= !params_without_objects.empty(); } - if (has_constructor_without_parameters) - { - w.write(R"( + w.write(R"( protected %(WindowsRuntimeActivationTypes.DerivedComposed _, WindowsRuntimeObjectReference activationFactoryObjectReference, in Guid iid) :base(_, activationFactoryObjectReference, in iid) { @@ -2682,16 +2673,7 @@ protected %(WindowsRuntimeActivationTypes.DerivedSealed _, WindowsRuntimeObjectR :base(_, activationFactoryObjectReference, in iid) { %} -)", - class_type.TypeName(), - gc_pressure, - class_type.TypeName(), - gc_pressure); - } - if (has_constructor_with_parameters) - { - w.write(R"( protected %(WindowsRuntimeActivationFactoryCallback.DerivedComposed activationFactoryCallback, in Guid iid, params ReadOnlySpan additionalParameters) :base(activationFactoryCallback, in iid, additionalParameters) { @@ -2702,11 +2684,18 @@ protected %(WindowsRuntimeActivationFactoryCallback.DerivedSealed activationFact { %} )", - class_type.TypeName(), - gc_pressure, - class_type.TypeName(), - gc_pressure); - } + // WindowsRuntimeActivationTypes.DerivedComposed + class_type.TypeName(), + gc_pressure, + // WindowsRuntimeActivationTypes.DerivedSealed + class_type.TypeName(), + gc_pressure, + // WindowsRuntimeActivationFactoryCallback.DerivedComposed + class_type.TypeName(), + gc_pressure, + // WindowsRuntimeActivationFactoryCallback.DerivedSealed + class_type.TypeName(), + gc_pressure); } void write_static_factory_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) From 564f2d2d2372508948145e3d4ac038b10d966372 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Thu, 23 Oct 2025 23:50:06 -0700 Subject: [PATCH 18/43] More fixes --- .../DispatcherQueueProxyHandler.cs | 8 +- .../DispatcherQueueSynchronizationContext.cs | 25 +- ...VectorChangedEventHandlerEventSource{T}.cs | 4 +- .../IObservableVectorMethodsImpl{T}.cs | 1 + src/cswinrt/code_writers.h | 190 ++++++++------ src/cswinrt/cswinrt.vcxproj.filters | 24 +- src/cswinrt/helpers.h | 41 ++- src/cswinrt/main.cpp | 30 ++- .../Windows.UI.Xaml.Controls.Primitives.cs | 69 +---- .../Windows.UI.Xaml.Media.Animation.cs | 161 +++--------- .../Windows.UI.Xaml.Media.Media3D.cs | 63 ++--- .../Windows.UI.Xaml.Media.cs | 201 ++------------ .../Windows.UI.Xaml/Windows.UI.Xaml.cs | 246 ++---------------- 13 files changed, 310 insertions(+), 753 deletions(-) rename src/WinRT.Runtime2/{InteropServices/Events => ABI/Windows.Foundation/Collections}/VectorChangedEventHandlerEventSource{T}.cs (94%) diff --git a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs index 04ccef611..0ed08bbfd 100644 --- a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs +++ b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using WindowsRuntime.InteropServices; using WinRT; using WinRT.Interop; @@ -150,7 +151,8 @@ private static ref readonly Guid IID_IDispatcherQueueHandler [UnmanagedCallersOnly] public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, void** ppvObject) { - if (riid->Equals(IID.IID_IUnknown) || + + /* if (riid->Equals(IID.IID_IUnknown) || riid->Equals(IID.IID_IAgileObject) || riid->Equals(IID_IDispatcherQueueHandler)) { @@ -159,7 +161,7 @@ public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, *ppvObject = @this; return S_OK; - } + }*/ return E_NOINTERFACE; } @@ -214,7 +216,7 @@ public static int Invoke(DispatcherQueueProxyHandler* @this) // Register the exception with the global error handler. The failfast behavior // is governed by the state of the 'UnhandledExceptionEventArgs.Handled' property. // If 'Handled' is true the app continues running, else it failfasts. - ExceptionHelpers.ReportUnhandledError(e); + RestrictedErrorInfo.ReportUnhandledError(e); } return S_OK; diff --git a/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs index dbb8f0fd5..4c248b9b3 100644 --- a/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs +++ b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using WinRT; +using WindowsRuntime.InteropServices; #nullable enable @@ -13,9 +13,9 @@ namespace Windows.System; public sealed partial class DispatcherQueueSynchronizationContext : SynchronizationContext { /// - /// The instance for the target dispatcher queue. + /// The instance for the target dispatcher queue. /// - private readonly IObjectReference _objectReference; + private readonly WindowsRuntimeObjectReference _objectReference; /// /// Creates a new instance with the specified parameters. @@ -26,15 +26,18 @@ public DispatcherQueueSynchronizationContext(global::Windows.System.DispatcherQu { ArgumentNullException.ThrowIfNull(dispatcherQueue); - _objectReference = ((IWinRTObject)dispatcherQueue).NativeObject; + if (!WindowsRuntimeMarshal.TryUnwrapObjectReference(dispatcherQueue, out _objectReference!)) + { + throw new ArgumentException(null, nameof(dispatcherQueue)); + } } /// /// Creates a new instance with the specified parameters. /// - /// The instance for the target dispatcher queue. + /// The instance for the target dispatcher queue. /// Thrown if is . - private DispatcherQueueSynchronizationContext(IObjectReference objectReference) + private DispatcherQueueSynchronizationContext(WindowsRuntimeObjectReference objectReference) { ArgumentNullException.ThrowIfNull(objectReference); @@ -51,21 +54,23 @@ public override unsafe void Post(SendOrPostCallback d, object? state) try { - void* thisPtr = (void*)_objectReference.ThisPtr; + _objectReference.AddRefUnsafe(); + + void* thisPtr = _objectReference.GetThisPtrUnsafe(); bool success; // Note: we're intentionally ignoring the retval for 'DispatcherQueue::TryEnqueue'. // This matches the behavior for the equivalent type on WinUI 3 as well. hresult = ((delegate* unmanaged)(*(void***)thisPtr)[7])(thisPtr, dispatcherQueueProxyHandler, (byte*)&success); - - GC.KeepAlive(_objectReference); } finally { dispatcherQueueProxyHandler->Release(); + _objectReference.ReleaseUnsafe(); } - ExceptionHelpers.ThrowExceptionForHR(hresult); + + RestrictedErrorInfo.ThrowExceptionForHR(hresult); } /// diff --git a/src/WinRT.Runtime2/InteropServices/Events/VectorChangedEventHandlerEventSource{T}.cs b/src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/VectorChangedEventHandlerEventSource{T}.cs similarity index 94% rename from src/WinRT.Runtime2/InteropServices/Events/VectorChangedEventHandlerEventSource{T}.cs rename to src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/VectorChangedEventHandlerEventSource{T}.cs index e08190082..85c4be36d 100644 --- a/src/WinRT.Runtime2/InteropServices/Events/VectorChangedEventHandlerEventSource{T}.cs +++ b/src/WinRT.Runtime2/ABI/Windows.Foundation/Collections/VectorChangedEventHandlerEventSource{T}.cs @@ -4,8 +4,10 @@ using System; using System.ComponentModel; using Windows.Foundation.Collections; +using WindowsRuntime; +using WindowsRuntime.InteropServices; -namespace WindowsRuntime.InteropServices; +namespace ABI.Windows.Foundation.Collections; /// /// An implementation for . diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs index 0452486cd..b4c0cca12 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using ABI.Windows.Foundation.Collections; namespace WindowsRuntime.InteropServices; diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index a98082c66..4be1638fb 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -1395,6 +1395,16 @@ namespace cswinrt bind_list(", ", signature.params())); } + void write_unsafe_accessor_static_method_call(writer& w, std::string const& unsafeAccessorMethod, MethodDef const& method, std::string const& targetObjRef) + { + method_signature signature{ method }; + w.write("%(null, %%%)", + unsafeAccessorMethod, + targetObjRef, + signature.has_params() ? ", " : "", + bind_list(", ", signature.params())); + } + void write_abi_get_property_static_method_call(writer& w, type_semantics const& iface, Property const& prop, std::string const& targetObjRef) { w.write("%.%(%)", @@ -1411,6 +1421,14 @@ namespace cswinrt targetObjRef); } + void write_unsafe_accessor_property_static_method_call(writer& w, std::string const& unsafeAccessorMethod, std::string const& targetObjRef, bool get) + { + w.write("%(null, %%)", + unsafeAccessorMethod, + targetObjRef, + get ? "" : ", value"); + } + void write_abi_event_source_static_method_call(writer& w, type_semantics const& iface, Event const& evt, bool isSubscribeCall, std::string const& targetObjRef, bool is_static_event = false) { bool is_unsafe_accessor_call = false; @@ -1455,6 +1473,27 @@ namespace cswinrt std::string_view platform_attribute = ""sv, std::optional> paramsForStaticMethodCall = {}) { + std::optional unsafe_accessor; + if (paramsForStaticMethodCall.has_value()) + { + auto parent = paramsForStaticMethodCall.value().second.Parent(); + if (size(parent.GenericParam()) != 0) + { + auto parent_type_name = w.write_temp("%", bind(parent, typedef_name_type::Projected, true)); + unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), method_name); + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] +static extern % %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference%%); +)", + method_name, + return_type, + unsafe_accessor.value(), + bind(parent), + signature.params().size() != 0 ? ", " : "", + bind_list(", ", signature.params())); + } + } + w.write(R"( %%%% %(%) => %; )", @@ -1465,18 +1504,30 @@ namespace cswinrt method_name, bind_list(", ", signature.params()), bind([&](writer& w) { - if (paramsForStaticMethodCall.has_value()) + if (paramsForStaticMethodCall.has_value()) + { + if (unsafe_accessor.has_value()) { - w.write("%", bind(paramsForStaticMethodCall.value().first, paramsForStaticMethodCall.value().second, + w.write("%", bind( + unsafe_accessor.value(), + paramsForStaticMethodCall.value().second, w.write_temp("%", method_target))); } else { - w.write("%.%(%)", method_target, - method_name, - bind_list(", ", signature.params())); + w.write("%", bind( + paramsForStaticMethodCall.value().first, + paramsForStaticMethodCall.value().second, + w.write_temp("%", method_target))); } - })); + } + else + { + w.write("%.%(%)", method_target, + method_name, + bind_list(", ", signature.params())); + } + })); } void write_explicitly_implemented_method_for_abi(writer& w, MethodDef const& method, @@ -1712,6 +1763,34 @@ namespace cswinrt setter_platform = ""sv; } + std::optional unsafe_accessor; + if (params_for_static_getter.has_value()) + { + auto parent = params_for_static_getter.value().second.Parent(); + if (size(parent.GenericParam()) != 0) + { + auto parent_type_name = w.write_temp("%", bind(params_for_static_getter.value().first, typedef_name_type::Projected, true)); + unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), external_prop_name); + w.write(R"( +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] +static extern % %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference); + +[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] +static extern void %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference, % value); +)", + // get + external_prop_name, + prop_type, + unsafe_accessor.value(), + bind(params_for_static_getter.value().first), + // set + external_prop_name, + unsafe_accessor.value(), + bind(params_for_static_getter.value().first), + prop_type); + } + } + w.write(R"( %%%% % { @@ -1728,8 +1807,20 @@ namespace cswinrt bind([&](writer& w) { if (params_for_static_getter.has_value()) { - w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", getter_target))); + if (unsafe_accessor.has_value()) + { + w.write("%", bind( + unsafe_accessor.value(), + w.write_temp("%", getter_target), + true)); + } + else + { + w.write("%", bind( + params_for_static_getter.value().first, + params_for_static_getter.value().second, + w.write_temp("%", getter_target))); + } } else { @@ -1740,8 +1831,20 @@ namespace cswinrt bind([&](writer& w) { if (params_for_static_setter.has_value()) { - w.write("%", bind(params_for_static_setter.value().first, params_for_static_setter.value().second, - w.write_temp("%", setter_target))); + if (unsafe_accessor.has_value()) + { + w.write("%", bind( + unsafe_accessor.value(), + w.write_temp("%", setter_target), + false)); + } + else + { + w.write("%", bind( + params_for_static_setter.value().first, + params_for_static_setter.value().second, + w.write_temp("%", setter_target))); + } } else { @@ -1755,69 +1858,6 @@ namespace cswinrt return w.write_temp("((%)(WindowsRuntimeObject)this)", bind(iface, typedef_name_type::Projected, false)); } - void write_lazy_interface_type_name(writer& w, type_semantics const& ifaceTypeSemantics) - { - auto interfaceTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); - std::string interfaceTypeName = "_lazy_" + interfaceTypeCode; - w.write("%", escape_type_name_for_identifier(interfaceTypeName)); - } - - void write_lazy_interface_initialization(writer& w, TypeDef const& type) - { - int numLazyInterfaces = 0; - auto lazyInterfaces = w.write_temp("%", [&](writer& w) - { - for (auto&& ii : type.InterfaceImpl()) - { - if (has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute")) - { - continue; - } - - for_typedef(w, get_type_semantics(ii.Interface()), [&](auto interface_type) - { - if (!is_manually_generated_iface(interface_type) && !settings.netstandard_compat) - { - return; - } - - numLazyInterfaces++; - auto lazy_interface_name = w.write_temp("%", bind(interface_type)); - auto interface_name = write_type_name_temp(w, interface_type); - auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); - - auto interface_init_code = settings.netstandard_compat - ? w.write_temp(R"(new %(GetReferenceForQI()))", - interface_abi_name) - : w.write_temp(R"((%)(object)new SingleInterfaceOptimizedObject(typeof(%), _inner ?? ((IWinRTObject)this).NativeObject))", - interface_name, - interface_name); - - w.write(R"( -private volatile % %; -private % Make_%() -{ - global::System.Threading.Interlocked.CompareExchange(ref %, %, null); - return %; -} -)", - interface_name, - lazy_interface_name, - interface_name, - lazy_interface_name, - lazy_interface_name, - interface_init_code, - lazy_interface_name); - }); - } - }); - - if (numLazyInterfaces != 0) - { - w.write(R"(%)", lazyInterfaces); - } - } - std::string write_explicit_name(writer& w, TypeDef const& iface, std::string_view name) { // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. @@ -3674,9 +3714,9 @@ visibility, self, objref_name); auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type); auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; - // Write IWindowsRuntimeInterface implementation for non-default interface and for the default interface if it isn't exclusive. + // Write IWindowsRuntimeInterface implementation for non-default interfaces and for the default interface if it isn't exclusive. // Otherwise, write the default interface in composable scenarios using its own function. - if (!is_exclusive_to(interface_type) || is_overridable_interface || is_protected_interface) + if (!is_exclusive_to(interface_type) || is_overridable_interface) { w.write(R"( WindowsRuntimeObjectReferenceValue IWindowsRuntimeInterface<%>.GetInterface() diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 1274c5cf0..04505a952 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -193,12 +193,24 @@ strings\additions\Windows.Storage.Streams - - - - - - + + strings\additions\Windows.UI.Xaml.Controls.Primitives + + + strings\additions\Windows.UI.Xaml.Media.Animation + + + strings\additions\Windows.UI.Xaml.Media.Media3D + + + strings\additions\Windows.UI.Xaml.Media + + + strings\additions\Windows.UI.Xaml + + + strings\additions\Windows.UI.Xaml + strings diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index de5ff356b..5649bcdee 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -678,6 +678,7 @@ namespace cswinrt std::string_view mapped_name; bool requires_marshaling; bool has_custom_members_output; + bool emit_abi; }; inline const std::initializer_list get_mapped_types_in_namespace(std::string_view typeNamespace) @@ -694,7 +695,7 @@ namespace cswinrt // NOTE: Must keep namespaces sorted (outer) and abi type names sorted (inner) { "Microsoft.UI.Xaml", { - { "CornerRadius", "Microsoft.UI.Xaml", "CornerRadius" }, + { "CornerRadius", "Microsoft.UI.Xaml", "CornerRadius", false, false, true }, { "CornerRadiusHelper" }, { "Duration", "Microsoft.UI.Xaml", "Duration" }, { "DurationHelper" }, @@ -839,14 +840,12 @@ namespace cswinrt }, { "Windows.UI.Xaml", { - { "CornerRadius", "Windows.UI.Xaml", "CornerRadius" }, + { "CornerRadius", "Windows.UI.Xaml", "CornerRadius", false, false, true }, { "CornerRadiusHelper" }, - { "Duration", "Windows.UI.Xaml", "Duration" }, + { "Duration", "Windows.UI.Xaml", "Duration", false, false, true }, { "DurationHelper" }, - { "DurationType", "Windows.UI.Xaml", "DurationType" }, - { "GridLength", "Windows.UI.Xaml", "GridLength" }, + { "GridLength", "Windows.UI.Xaml", "GridLength", false, false, true }, { "GridLengthHelper" }, - { "GridUnitType", "Windows.UI.Xaml", "GridUnitType" }, { "ICornerRadiusHelper" }, { "ICornerRadiusHelperStatics" }, { "IDurationHelper" }, @@ -855,14 +854,12 @@ namespace cswinrt { "IGridLengthHelperStatics" }, { "IThicknessHelper" }, { "IThicknessHelperStatics" }, - { "Thickness", "Windows.UI.Xaml", "Thickness" }, { "ThicknessHelper" }, { "IXamlServiceProvider", "System", "IServiceProvider" }, } }, { "Windows.UI.Xaml.Controls.Primitives", { - { "GeneratorPosition", "Windows.UI.Xaml.Controls.Primitives", "GeneratorPosition" }, { "GeneratorPositionHelper" }, { "IGeneratorPositionHelper" }, { "IGeneratorPositionHelperStatics" }, @@ -898,7 +895,6 @@ namespace cswinrt { { "IMatrixHelper" }, { "IMatrixHelperStatics" }, - { "Matrix", "Windows.UI.Xaml.Media", "Matrix" }, { "MatrixHelper" }, } }, @@ -908,18 +904,17 @@ namespace cswinrt { "IKeyTimeHelperStatics" }, { "IRepeatBehaviorHelper" }, { "IRepeatBehaviorHelperStatics" }, - { "KeyTime", "Windows.UI.Xaml.Media.Animation", "KeyTime" }, + { "KeyTime", "Windows.UI.Xaml.Media.Animation", "KeyTime", false, false, true }, { "KeyTimeHelper" }, - { "RepeatBehavior", "Windows.UI.Xaml.Media.Animation", "RepeatBehavior" }, + { "RepeatBehavior", "Windows.UI.Xaml.Media.Animation", "RepeatBehavior", false, false, true }, { "RepeatBehaviorHelper" }, - { "RepeatBehaviorType", "Windows.UI.Xaml.Media.Animation", "RepeatBehaviorType" } } }, { "Windows.UI.Xaml.Media.Media3D", { { "IMatrix3DHelper" }, { "IMatrix3DHelperStatics" }, - { "Matrix3D", "Windows.UI.Xaml.Media.Media3D", "Matrix3D" }, + { "Matrix3D", "Windows.UI.Xaml.Media.Media3D", "Matrix3D", false, false, true }, { "Matrix3DHelper" }, } }, @@ -1205,6 +1200,26 @@ namespace cswinrt "Color", } }, + { "Windows.UI.Xaml", + { + "Thickness" + } + }, + { "Windows.UI.Xaml.Controls.Primitives", + { + "GeneratorPosition" + } + }, + { "Windows.UI.Xaml.Media", + { + "Matrix" + } + }, + { "Windows.UI.Xaml.Media.Animation", + { + "KeyTime" + } + }, }; auto nsItr = std::lower_bound(std::begin(addition_types), std::end(addition_types), type.TypeNamespace(), [](auto&& v, std::string_view ns) diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index 160af8a95..775df771f 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -202,9 +202,13 @@ Where is one or more of: for (auto&& [name, type] : members.types) { if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) + if (distance(type.GenericParam()) != 0) { continue; } + if (auto mapping = get_mapped_type(ns, name)) { - continue; + if (!mapping->emit_abi) + { + continue; + } } switch (get_category(type)) { @@ -247,9 +251,13 @@ Where is one or more of: { currentType = name; if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) + if (distance(type.GenericParam()) != 0) { continue; } + if (auto mapping = get_mapped_type(ns, name)) { - continue; + if (!mapping->emit_abi) + { + continue; + } } auto guard{ w.push_generic_params(type.GenericParam()) }; auto guard1{ helperWriter.push_generic_params(type.GenericParam()) }; @@ -353,7 +361,15 @@ Where is one or more of: { currentType = name; if (!settings.filter.includes(type)) { continue; } - if (get_mapped_type(ns, name) || distance(type.GenericParam()) != 0) continue; + if (distance(type.GenericParam()) != 0) { continue; } + if (auto mapping = get_mapped_type(ns, name)) + { + if (!mapping->emit_abi) + { + continue; + } + } + if (is_api_contract_type(type)) { continue; } if (is_attribute_type(type)) { continue; } auto guard{ w.push_generic_params(type.GenericParam()) }; @@ -389,7 +405,7 @@ Where is one or more of: // Custom additions to namespaces for (auto addition : strings::additions) { - if (ns == addition.name && ns == "Windows.UI" && settings.addition_filter.includes(ns)) + if (ns == addition.name && (ns != "Windows.Storage" && ns != "Windows.Storage.Streams" && ns != "Windows.Foundation") && settings.addition_filter.includes(ns)) { w.write(addition.value); } @@ -507,7 +523,7 @@ ComWrappersSupport.RegisterAuthoringMetadataTypeLookup(new Func(GetM { for (auto&& string : strings::base) { - if (std::string(string.name) == "ComInteropHelpers" && !settings.filter.includes("Windows")) + if (std::string(string.name) == "ComInteropHelpers" /* && !settings.filter.includes("Windows") */) { continue; } diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs index 194a509a7..99f9a49a3 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs @@ -3,74 +3,11 @@ namespace Windows.UI.Xaml.Controls.Primitives { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Controls.Primitives.GeneratorPosition))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct GeneratorPosition + partial struct GeneratorPosition { - private int _index; - private int _offset; - - public int Index { get { return _index; } set { _index = value; } } - public int Offset { get { return _offset; } set { _offset = value; } } - - public GeneratorPosition(int index, int offset) - { - _index = index; - _offset = offset; - } - - public override int GetHashCode() - { - return _index.GetHashCode() + _offset.GetHashCode(); - } - public override string ToString() { - return string.Concat("GeneratorPosition (", _index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", _offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); - } - - public override bool Equals(object o) - { - if (o is GeneratorPosition) - { - GeneratorPosition that = (GeneratorPosition)o; - return _index == that._index && - _offset == that._offset; - } - return false; + return string.Concat("GeneratorPosition (", Index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", Offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); } - - public static bool operator ==(GeneratorPosition gp1, GeneratorPosition gp2) - { - return gp1._index == gp2._index && - gp1._offset == gp2._offset; - } - - public static bool operator !=(GeneratorPosition gp1, GeneratorPosition gp2) - { - return !(gp1 == gp2); - } - } -} - -namespace ABI.Windows.UI.Xaml.Controls.Primitives -{ -#if EMBED - internal -#else - public -#endif - static class GeneratorPosition - { - public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Controls.Primitives.GeneratorPosition;i4;i4)"; } -} +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs index 3170b4fb7..ae07769f5 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs @@ -3,38 +3,25 @@ namespace Windows.UI.Xaml.Media.Animation { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Animation.KeyTime))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct KeyTime + public struct KeyTime : IEquatable { - private TimeSpan _timeSpan; - public static KeyTime FromTimeSpan(TimeSpan timeSpan) { if (timeSpan < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(timeSpan)); } - - KeyTime keyTime = default; - - keyTime._timeSpan = timeSpan; - - return keyTime; + + return new KeyTime() { TimeSpan = timeSpan }; } public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) { - return (keyTime1._timeSpan == keyTime2._timeSpan); + return (keyTime1.TimeSpan == keyTime2.TimeSpan); } public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) @@ -59,12 +46,12 @@ public override bool Equals(object value) public override int GetHashCode() { - return _timeSpan.GetHashCode(); + return TimeSpan.GetHashCode(); } public override string ToString() { - return _timeSpan.ToString(); + return TimeSpan.ToString(); } public static implicit operator KeyTime(TimeSpan timeSpan) @@ -73,47 +60,17 @@ public static implicit operator KeyTime(TimeSpan timeSpan) } public TimeSpan TimeSpan - { - get - { - return _timeSpan; - } + { + readonly get; private init; } } - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum RepeatBehaviorType - { - Count, - Duration, - Forever - } - - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Animation.RepeatBehavior))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.Media.Animation.RepeatBehaviorComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct RepeatBehavior : IFormattable + public struct RepeatBehavior : IFormattable, IEquatable { - private double _Count; - private TimeSpan _Duration; - private RepeatBehaviorType _Type; - internal static bool IsFinite(double value) { return !(double.IsNaN(value) || double.IsInfinity(value)); @@ -126,9 +83,9 @@ public RepeatBehavior(double count) throw new ArgumentOutOfRangeException(nameof(count)); } - _Duration = new TimeSpan(0); - _Count = count; - _Type = RepeatBehaviorType.Count; + Duration = new TimeSpan(0); + Count = count; + Type = RepeatBehaviorType.Count; } public RepeatBehavior(TimeSpan duration) @@ -138,9 +95,9 @@ public RepeatBehavior(TimeSpan duration) throw new ArgumentOutOfRangeException(nameof(duration)); } - _Duration = duration; - _Count = 0.0; - _Type = RepeatBehaviorType.Duration; + Duration = duration; + Count = 0.0; + Type = RepeatBehaviorType.Duration; } public static RepeatBehavior Forever @@ -171,21 +128,18 @@ public bool HasDuration } public double Count - { - get { return _Count; } - set { _Count = value; } + { + readonly get; set; } public TimeSpan Duration { - get { return _Duration; } - set { _Duration = value; } + readonly get; set; } public RepeatBehaviorType Type - { - get { return _Type; } - set { _Type = value; } + { + readonly get; set; } public override string ToString() @@ -205,7 +159,7 @@ string IFormattable.ToString(string format, IFormatProvider formatProvider) internal string InternalToString(string format, IFormatProvider formatProvider) { - switch (_Type) + switch (Type) { case RepeatBehaviorType.Forever: @@ -218,13 +172,13 @@ internal string InternalToString(string format, IFormatProvider formatProvider) sb.AppendFormat( formatProvider, "{0:" + format + "}x", - _Count); + Count); return sb.ToString(); case RepeatBehaviorType.Duration: - return _Duration.ToString(); + return Duration.ToString(); default: return string.Empty; @@ -245,13 +199,13 @@ public override bool Equals(object value) public bool Equals(RepeatBehavior repeatBehavior) { - if (_Type == repeatBehavior._Type) + if (Type == repeatBehavior.Type) { - return _Type switch + return Type switch { RepeatBehaviorType.Forever => true, - RepeatBehaviorType.Count => _Count == repeatBehavior._Count, - RepeatBehaviorType.Duration => _Duration == repeatBehavior._Duration, + RepeatBehaviorType.Count => Count == repeatBehavior.Count, + RepeatBehaviorType.Duration => Duration == repeatBehavior.Duration, _ => false, }; } @@ -268,10 +222,10 @@ public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatB public override int GetHashCode() { - return _Type switch + return Type switch { - RepeatBehaviorType.Count => _Count.GetHashCode(), - RepeatBehaviorType.Duration => _Duration.GetHashCode(), + RepeatBehaviorType.Count => Count.GetHashCode(), + RepeatBehaviorType.Duration => Duration.GetHashCode(), // We try to choose an unlikely hash code value for Forever. // All Forevers need to return the same hash code value. @@ -291,45 +245,4 @@ public override int GetHashCode() return !repeatBehavior1.Equals(repeatBehavior2); } } -} - -namespace ABI.Windows.UI.Xaml.Media.Animation -{ -#if EMBED - internal -#else - public -#endif - static class KeyTime - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - return $"struct(Windows.UI.Xaml.Media.Animation.KeyTime;{timeSpanSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class RepeatBehavior - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - return $"struct(Windows.UI.Xaml.Media.Animation.RepeatBehavior;f8;{timeSpanSignature};{RepeatBehaviorType.GetGuidSignature()})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class RepeatBehaviorType - { - public static string GetGuidSignature() => "enum(Windows.UI.Xaml.Media.Animation.RepeatBehaviorType;i4)"; - } -} +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs index df02ccd76..c7360aadc 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs @@ -3,18 +3,11 @@ namespace Windows.UI.Xaml.Media.Media3D { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Media3D.Matrix3D))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.Media.Media3D.Matrix3DComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Matrix3D : IFormattable + public struct Matrix3D : IFormattable { // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: // Returns false if HasInverse is false, otherwise inverts the matrix. @@ -183,7 +176,7 @@ public Matrix3D(double m11, double m12, double m13, double m14, public double M11 { - get + readonly get { return _m11; } @@ -195,7 +188,7 @@ public double M11 public double M12 { - get + readonly get { return _m12; } @@ -207,7 +200,7 @@ public double M12 public double M13 { - get + readonly get { return _m13; } @@ -219,7 +212,7 @@ public double M13 public double M14 { - get + readonly get { return _m14; } @@ -231,7 +224,7 @@ public double M14 public double M21 { - get + readonly get { return _m21; } @@ -243,7 +236,7 @@ public double M21 public double M22 { - get + readonly get { return _m22; } @@ -255,7 +248,7 @@ public double M22 public double M23 { - get + readonly get { return _m23; } @@ -267,7 +260,7 @@ public double M23 public double M24 { - get + readonly get { return _m24; } @@ -279,7 +272,7 @@ public double M24 public double M31 { - get + readonly get { return _m31; } @@ -291,7 +284,7 @@ public double M31 public double M32 { - get + readonly get { return _m32; } @@ -303,7 +296,7 @@ public double M32 public double M33 { - get + readonly get { return _m33; } @@ -315,7 +308,7 @@ public double M33 public double M34 { - get + readonly get { return _m34; } @@ -327,7 +320,7 @@ public double M34 public double OffsetX { - get + readonly get { return _offsetX; } @@ -339,7 +332,7 @@ public double OffsetX public double OffsetY { - get + readonly get { return _offsetY; } @@ -351,7 +344,7 @@ public double OffsetY public double OffsetZ { - get + readonly get { return _offsetZ; } @@ -363,7 +356,7 @@ public double OffsetZ public double M44 { - get + readonly get { return _m44; } @@ -418,7 +411,7 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = TokenizerHelper.GetNumericListSeparator(provider); + char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}{0}{7:" + format + "}{0}{8:" + format + "}{0}{9:" + format + "}{0}{10:" + format + @@ -698,17 +691,3 @@ private static bool IsZero(double value) private double _m44; } } - -namespace ABI.Windows.UI.Xaml.Media.Media3D -{ -#if EMBED - internal -#else - public -#endif - static class Matrix3D - { - public static string GetGuidSignature() => - $"struct(Windows.UI.Xaml.Media.Media3D.Matrix3D;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8)"; - } -} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs index 92a20c4d3..f97d049d1 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs @@ -3,106 +3,11 @@ namespace Windows.UI.Xaml.Media { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Matrix))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Matrix : IFormattable + partial struct Matrix : IFormattable { - public Matrix(double m11, double m12, - double m21, double m22, - double offsetX, double offsetY) - { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; - _offsetX = offsetX; - _offsetY = offsetY; - } - // the transform is identity by default private static Matrix s_identity = CreateIdentity(); - public double M11 - { - get - { - return _m11; - } - set - { - _m11 = value; - } - } - - public double M12 - { - get - { - return _m12; - } - set - { - _m12 = value; - } - } - - public double M21 - { - get - { - return _m21; - } - set - { - _m21 = value; - } - } - - public double M22 - { - get - { - return _m22; - } - set - { - _m22 = value; - } - } - - public double OffsetX - { - get - { - return _offsetX; - } - set - { - _offsetX = value; - } - } - - public double OffsetY - { - get - { - return _offsetY; - } - set - { - _offsetY = value; - } - } - public static Matrix Identity { get @@ -115,7 +20,7 @@ public bool IsIdentity { get { - return (_m11 == 1 && _m12 == 0 && _m21 == 0 && _m22 == 1 && _offsetX == 0 && _offsetY == 0); + return (M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0); } } @@ -145,16 +50,16 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = TokenizerHelper.GetNumericListSeparator(provider); + char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", separator, - _m11, - _m12, - _m21, - _m22, - _offsetX, - _offsetY); + M11, + M12, + M21, + M22, + OffsetX, + OffsetY); } public Point Transform(Point point) @@ -166,42 +71,6 @@ public Point Transform(Point point) return point2; } - public override int GetHashCode() - { - // Perform field-by-field XOR of HashCodes - return M11.GetHashCode() ^ - M12.GetHashCode() ^ - M21.GetHashCode() ^ - M22.GetHashCode() ^ - OffsetX.GetHashCode() ^ - OffsetY.GetHashCode(); - } - - public override bool Equals(object o) - { - return o is Matrix && Matrix.Equals(this, (Matrix)o); - } - - public bool Equals(Matrix value) - { - return Matrix.Equals(this, value); - } - - public static bool operator ==(Matrix matrix1, Matrix matrix2) - { - return matrix1.M11 == matrix2.M11 && - matrix1.M12 == matrix2.M12 && - matrix1.M21 == matrix2.M21 && - matrix1.M22 == matrix2.M22 && - matrix1.OffsetX == matrix2.OffsetX && - matrix1.OffsetY == matrix2.OffsetY; - } - - public static bool operator !=(Matrix matrix1, Matrix matrix2) - { - return !(matrix1 == matrix2); - } - private static Matrix CreateIdentity() { Matrix matrix = default; @@ -215,52 +84,22 @@ private void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY) { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; - _offsetX = offsetX; - _offsetY = offsetY; + M11 = m11; + M12 = m12; + M21 = m21; + M22 = m22; + OffsetX = offsetX; + OffsetY = offsetY; } private void MultiplyPoint(ref float x, ref float y) { - double num = (y * _m21) + _offsetX; - double num2 = (x * _m12) + _offsetY; - x *= (float)_m11; + double num = (y * M21) + OffsetX; + double num2 = (x * M12) + OffsetY; + x *= (float)M11; x += (float)num; - y *= (float)_m22; + y *= (float)M22; y += (float)num2; } - - private static bool Equals(Matrix matrix1, Matrix matrix2) - { - return matrix1.M11.Equals(matrix2.M11) && - matrix1.M12.Equals(matrix2.M12) && - matrix1.M21.Equals(matrix2.M21) && - matrix1.M22.Equals(matrix2.M22) && - matrix1.OffsetX.Equals(matrix2.OffsetX) && - matrix1.OffsetY.Equals(matrix2.OffsetY); - } - - private double _m11; - private double _m12; - private double _m21; - private double _m22; - private double _offsetX; - private double _offsetY; - } -} - -namespace ABI.Windows.UI.Xaml.Media -{ -#if EMBED - internal -#else - public -#endif - static class Matrix - { - public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Media.Matrix;f8;f8;f8;f8;f8;f8)"; } -} +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs index 3fb1fabeb..e3de03433 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs @@ -3,18 +3,11 @@ namespace Windows.UI.Xaml { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.CornerRadius))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.CornerRadiusComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct CornerRadius + public struct CornerRadius : IEquatable { private double _TopLeft; private double _TopRight; @@ -59,7 +52,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) @@ -154,34 +147,12 @@ public double BottomLeft } } - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum GridUnitType - { - Auto = 0, - Pixel, - Star, - } - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.GridLength))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct GridLength + public struct GridLength : IEquatable { private readonly double _unitValue; private readonly GridUnitType _unitType; @@ -269,8 +240,6 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); - // Initial capacity [64] is an estimate based on a sum of: // 12 = 1x double (twelve digits is generous for the range of values likely) // 8 = 4x Unit Type string (approx two characters) @@ -291,59 +260,11 @@ internal string ToString(global::System.Globalization.CultureInfo cultureInfo) } } - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Thickness))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Thickness + partial struct Thickness { - private double _Left; - private double _Top; - private double _Right; - private double _Bottom; - public Thickness(double uniformLength) { - _Left = _Top = _Right = _Bottom = uniformLength; - } - - public Thickness(double left, double top, double right, double bottom) - { - _Left = left; - _Top = top; - _Right = right; - _Bottom = bottom; - } - - public double Left - { - get { return _Left; } - set { _Left = value; } - } - - public double Top - { - get { return _Top; } - set { _Top = value; } - } - - public double Right - { - get { return _Right; } - set { _Right = value; } - } - - public double Bottom - { - get { return _Bottom; } - set { _Bottom = value; } + Left = Top = Right = Bottom = uniformLength; } public override string ToString() @@ -353,7 +274,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) @@ -361,13 +282,13 @@ internal string ToString(global::System.Globalization.CultureInfo cultureInfo) // 4 = 4x separator characters global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - sb.Append(InternalToString(_Left, cultureInfo)); + sb.Append(InternalToString(Left, cultureInfo)); sb.Append(listSeparator); - sb.Append(InternalToString(_Top, cultureInfo)); + sb.Append(InternalToString(Top, cultureInfo)); sb.Append(listSeparator); - sb.Append(InternalToString(_Right, cultureInfo)); + sb.Append(InternalToString(Right, cultureInfo)); sb.Append(listSeparator); - sb.Append(InternalToString(_Bottom, cultureInfo)); + sb.Append(InternalToString(Bottom, cultureInfo)); return sb.ToString(); } @@ -376,66 +297,13 @@ internal string InternalToString(double l, global::System.Globalization.CultureI if (double.IsNaN(l)) return "Auto"; return Convert.ToString(l, cultureInfo); } - - public override bool Equals(object obj) - { - if (obj is Thickness) - { - Thickness otherObj = (Thickness)obj; - return (this == otherObj); - } - return (false); - } - - public bool Equals(Thickness thickness) - { - return (this == thickness); - } - - public override int GetHashCode() - { - return _Left.GetHashCode() ^ _Top.GetHashCode() ^ _Right.GetHashCode() ^ _Bottom.GetHashCode(); - } - - public static bool operator ==(Thickness t1, Thickness t2) - { - return t1._Left == t2._Left && t1._Top == t2._Top && t1._Right == t2._Right && t1._Bottom == t2._Bottom; - } - - public static bool operator !=(Thickness t1, Thickness t2) - { - return (!(t1 == t2)); - } } - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum DurationType - { - Automatic, - TimeSpan, - Forever - } - - [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Duration))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Duration + public struct Duration { private readonly TimeSpan _timeSpan; private DurationType _durationType; @@ -717,76 +585,4 @@ public override string ToString() } } } -} - -namespace ABI.Windows.UI.Xaml -{ -#if EMBED - internal -#else - public -#endif - static class CornerRadius - { - public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.CornerRadius;f8;f8;f8;f8)"; - } - -#if EMBED - internal -#else - public -#endif - static class Duration - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - string durationTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Windows.UI.Xaml.DurationType)); - return $"struct(Windows.UI.Xaml.Duration;{timeSpanSignature};{durationTypeSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class DurationType - { - public static string GetGuidSignature() => "enum(Windows.UI.Xaml.DurationType;i4)"; - } - -#if EMBED - internal -#else - public -#endif - static class GridLength - { - public static string GetGuidSignature() - { - string unitTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Windows.UI.Xaml.GridUnitType)); - return $"struct(Windows.UI.Xaml.GridLength;f8;{unitTypeSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class GridUnitType - { - public static string GetGuidSignature() => "enum(Windows.UI.Xaml.GridUnitType;i4)"; - } - -#if EMBED - internal -#else - public -#endif - static class Thickness - { - public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Thickness;f8;f8;f8;f8)"; - } -} +} \ No newline at end of file From 6085423ea34f6db5039b59e076e8578b0a12369e Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Thu, 23 Oct 2025 23:55:56 -0700 Subject: [PATCH 19/43] Restore to original projections --- src/Projections/Test/Test.csproj | 55 +++---------------- .../Windows.UI.Xaml/Windows.UI.Xaml.csproj | 4 +- src/Projections/Windows/Windows.csproj | 8 +-- 3 files changed, 10 insertions(+), 57 deletions(-) diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj index 673b84884..6aadaac37 100644 --- a/src/Projections/Test/Test.csproj +++ b/src/Projections/Test/Test.csproj @@ -22,58 +22,17 @@ - + + TestComponentCSharp.AnotherAssembly; + - TestComponent.Blittable; - TestComponent.NonBlittable; - TestComponent.Nested; - TestComponentCSharp.FlagValue; - TestComponentCSharp.EnumValue; - TestComponentCSharp.FlagStruct; - TestComponentCSharp.EnumStruct; - TestComponentCSharp.BlittableStruct; - TestComponentCSharp.ComposedBlittableStruct; - TestComponentCSharp.NonBlittableStringStruct; - TestComponentCSharp.NonBlittableBoolStruct; - TestComponentCSharp.NonBlittableRefStruct; - TestComponentCSharp.ComposedNonBlittableStruct - TestComponentCSharp.CustomDisposableTest; - TestComponentCSharp.ICustomDisposableTest; - TestComponent.Composable; - TestComponent.IComposableStatics; - TestComponent.IRequiredOne; - TestComponent.IRequiredTwo; - TestComponent.IRequiredThree; - TestComponent.IRequiredFour; - TestComponent.IComposable; - TestComponentCSharp.ComImports; - TestComponentCSharp.IComImports; - TestComponentCSharp.ISingleton; - TestComponentCSharp.Singleton; - TestComponent.Param1Handler; - TestComponent.Param2Handler; - TestComponent.Param3Handler; - TestComponent.Param4Handler; - TestComponent.Param5Handler; - TestComponent.Param6Handler; - TestComponent.Param7Handler; - TestComponent.Param8Handler; - TestComponent.Param9Handler; - TestComponent.Param10Handler; - TestComponent.Param11Handler; - TestComponent.Param12Handler; - TestComponent.Param13Handler; - TestComponent.Param14Handler; - TestComponent.Param15Handler; - TestComponentCSharp.EventWithReturn; - TestComponentCSharp.EventWithGuid; - TestComponentCSharp.ProvideInt; - TestComponentCSharp.ProvideBool; - TestComponentCSharp.ProvideString; + TestComponent; + TestComponentCSharp; test_component_base; test_component_derived; + test_component_fast; - + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk', '$(MicrosoftWinAppSDKVersion)')) diff --git a/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj index 0a212ebab..ef9cb089d 100644 --- a/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj +++ b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj @@ -8,7 +8,7 @@ - + @@ -19,8 +19,6 @@ -exclude Windows -include Windows.UI.Xaml --exclude Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute --addition_exclude Windows.UI.Xaml.Media.Animation diff --git a/src/Projections/Windows/Windows.csproj b/src/Projections/Windows/Windows.csproj index 563050c64..1fca80cd6 100644 --- a/src/Projections/Windows/Windows.csproj +++ b/src/Projections/Windows/Windows.csproj @@ -17,16 +17,13 @@ --include Windows.Foundation.Metadata --include Windows.Foundation.UniversalApiContract +-include Windows # Exclude Windows.UI, Windows.UI.Text, Windows.UI.Xaml per Microsoft.Windows.SDK.WinUI.Contracts NuGet -# TODO: -include Windows.UI.Popups +-include Windows.UI.Popups -exclude Windows.UI.Colors -exclude Windows.UI.IColors -exclude Windows.UI.ColorHelper -exclude Windows.UI.IColorHelper --exclude Windows.UI.ColorHelper --exclude Windows.UI.IColorHelper -exclude Windows.UI.IColorHelperStatics -exclude Windows.UI.IColorHelperStatics2 #-exclude Windows.UI.Text (must include Windows.UI.Text to work around WinUI nuget issues) @@ -38,7 +35,6 @@ -include Windows.UI.Text.FontWeight -include Windows.UI.Text.IFontWeights -include Windows.UI.Text.UnderlineType --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute From 97297cd8df56493a86e98bb1596ef644b74438a9 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Fri, 24 Oct 2025 00:17:26 -0700 Subject: [PATCH 20/43] Fix --- .../DispatcherQueueProxyHandler.cs | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs index 0ed08bbfd..2c0739b78 100644 --- a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs +++ b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs @@ -3,8 +3,6 @@ using System.Runtime.InteropServices; using System.Threading; using WindowsRuntime.InteropServices; -using WinRT; -using WinRT.Interop; #nullable enable @@ -145,15 +143,64 @@ private static ref readonly Guid IID_IDispatcherQueueHandler } } + /// The IID for IUnknown (00000000-0000-0000-C000-000000000046). + private static ref readonly Guid IID_IUnknown + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IAgileObject (94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90). + private static ref readonly Guid IID_IAgileObject + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x94, 0x2B, 0xEA, 0x94, + 0xCC, 0xE9, + 0xE0, 0x49, + 0xC0, + 0xFF, + 0xEE, + 0x64, + 0xCA, + 0x8F, + 0x5B, + 0x90 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + /// /// Implements IUnknown.QueryInterface(REFIID, void**). /// [UnmanagedCallersOnly] public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, void** ppvObject) { - - /* if (riid->Equals(IID.IID_IUnknown) || - riid->Equals(IID.IID_IAgileObject) || + if (riid->Equals(IID_IUnknown) || + riid->Equals(IID_IAgileObject) || riid->Equals(IID_IDispatcherQueueHandler)) { Interlocked.Increment(ref @this->referenceCount); @@ -161,7 +208,7 @@ public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, *ppvObject = @this; return S_OK; - }*/ + } return E_NOINTERFACE; } From c87f58b4e6fb7783e787974f10486db4743a3e38 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Fri, 24 Oct 2025 08:19:01 -0700 Subject: [PATCH 21/43] Fix IIDs --- src/cswinrt/code_writers.h | 51 +++++++++++++++++++++++++++++++++++--- src/cswinrt/main.cpp | 4 +++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 4be1638fb..c3646fef9 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -906,6 +906,40 @@ namespace cswinrt )"); } + static void write_iid_guid_property_for_class_interfaces(writer& w, TypeDef const& type, std::set& interfacesEmitted) + { + for (auto& iface : type.InterfaceImpl()) + { + auto semantics = get_type_semantics(iface.Interface()); + for_typedef(w, semantics, [&](TypeDef ifaceType) + { + // IIDs for custom mapped interfaces and generic interfaces are handled differently. + if (auto mapping = get_mapped_type(ifaceType.TypeNamespace(), ifaceType.TypeName())) + { + return; + } + + if (size(ifaceType.GenericParam()) != 0) + { + return; + } + + if (interfacesEmitted.find(ifaceType) != interfacesEmitted.end()) + { + return; + } + + // If the IID for the interface is not already going to be written due to + // being projected in this projection, then write it so that we can use it + // for our objref. + if (!settings.filter.includes(ifaceType)) + { + write_iid_guid_property_from_type(w, ifaceType); + interfacesEmitted.insert(ifaceType); + } + }); + } + } void write_event_source_type_name(writer& w, type_semantics const& eventTypeSemantics) { @@ -2585,11 +2619,11 @@ static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll") if (!useInner) { - w.write(R"( + w.write(R"(% private WindowsRuntimeObjectReference % { get - {% + { [MethodImpl(MethodImplOptions.NoInlining)] WindowsRuntimeObjectReference MakeObjectReference() { @@ -2605,13 +2639,13 @@ private WindowsRuntimeObjectReference % } } )", - objrefname, [&](writer& w) { if (distance(ifaceType.GenericParam()) != 0) { write_unsafe_accessor_for_iid(w, ifaceType); } }, + objrefname, bind(ifaceType)); /* @@ -8493,7 +8527,7 @@ return new %(valueReference); { w.write(R"( file sealed unsafe class %ComWrappersCallback : IWindowsRuntimeUnsealedObjectComWrappersCallback -{ +{% public static unsafe bool TryCreateObject( void* value, ReadOnlySpan runtimeClassName, @@ -8518,6 +8552,15 @@ return false; } )", type.TypeName(), + bind([&](writer& w) { + for_typedef(w, default_type_semantics, [&](TypeDef const& interface_type) + { + if (size(interface_type.GenericParam()) != 0) + { + write_unsafe_accessor_for_iid(w, interface_type, true); + } + }); + }), bind(type, typedef_name_type::ABI, false), bind(default_type_semantics), bind(type, typedef_name_type::Projected, true)); diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index 775df771f..c66871253 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -194,6 +194,7 @@ Where is one or more of: w.flush_to_console(); // Write GUID properties out to InterfaceIIDs static class + std::set interfacesFromClassesEmitted; writer guidWriter("ABI"); guidWriter.write_begin_interface_iids(); for (auto&& ns_members : c.namespaces()) @@ -225,6 +226,9 @@ Where is one or more of: case category::struct_type: write_iid_guid_property_from_signature(guidWriter, type); break; + case category::class_type: + write_iid_guid_property_for_class_interfaces(guidWriter, type, interfacesFromClassesEmitted); + break; } } } From 0e655e2db3bcd89a283f351891ccf1ba0cd8b237 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Fri, 24 Oct 2025 19:43:05 -0700 Subject: [PATCH 22/43] Renames --- src/cswinrt/cswinrt.vcxproj | 16 +- src/cswinrt/cswinrt.vcxproj.filters | 24 +- ....Controls.Primitives.GeneratorPosition.cs} | 0 ...Windows.UI.Xaml.Media.Animation.KeyTime.cs | 67 ++ ...UI.Xaml.Media.Animation.RepeatBehavior.cs} | 68 +- ...Windows.UI.Xaml.Media.Media3D.Matrix3D.cs} | 0 ...dia.cs => Windows.UI.Xaml.Media.Matrix.cs} | 0 .../Windows.UI.Xaml.CornerRadius.cs | 149 +++++ .../Windows.UI.Xaml.Duration.cs | 292 +++++++++ .../Windows.UI.Xaml.GridLength.cs | 117 ++++ .../Windows.UI.Xaml.Thickness.cs | 44 ++ .../Windows.UI.Xaml/Windows.UI.Xaml.cs | 588 ------------------ .../{Windows.UI.cs => Windows.UI.Color.cs} | 0 13 files changed, 700 insertions(+), 665 deletions(-) rename src/cswinrt/strings/additions/{Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs => Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs} (100%) create mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs rename src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/{Windows.UI.Xaml.Media.Animation.cs => Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs} (74%) rename src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/{Windows.UI.Xaml.Media.Media3D.cs => Windows.UI.Xaml.Media.Media3D.Matrix3D.cs} (100%) rename src/cswinrt/strings/additions/Windows.UI.Xaml.Media/{Windows.UI.Xaml.Media.cs => Windows.UI.Xaml.Media.Matrix.cs} (100%) create mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs create mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs create mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs create mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs delete mode 100644 src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs rename src/cswinrt/strings/additions/Windows.UI/{Windows.UI.cs => Windows.UI.Color.cs} (100%) diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index c2d4b1d5c..e273d9cc4 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -113,13 +113,17 @@ - - - - - + + + + + + + + + - + diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 04505a952..37db48b14 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -160,7 +160,7 @@ strings\additions\Windows.Foundation - + strings\additions\Windows.UI @@ -193,19 +193,19 @@ strings\additions\Windows.Storage.Streams - + strings\additions\Windows.UI.Xaml.Controls.Primitives - + strings\additions\Windows.UI.Xaml.Media.Animation - + strings\additions\Windows.UI.Xaml.Media.Media3D - + strings\additions\Windows.UI.Xaml.Media - + strings\additions\Windows.UI.Xaml @@ -217,6 +217,18 @@ strings + + strings\additions\Windows.UI.Xaml + + + strings\additions\Windows.UI.Xaml + + + strings\additions\Windows.UI.Xaml + + + strings\additions\Windows.UI.Xaml.Media.Animation + diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs similarity index 100% rename from src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs rename to src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs new file mode 100644 index 000000000..31a9da6c2 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs @@ -0,0 +1,67 @@ + +namespace Windows.UI.Xaml.Media.Animation +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct KeyTime : IEquatable + { + public static KeyTime FromTimeSpan(TimeSpan timeSpan) + { + if (timeSpan < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan)); + } + + return new KeyTime() { TimeSpan = timeSpan }; + } + + public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) + { + return (keyTime1.TimeSpan == keyTime2.TimeSpan); + } + + public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) + { + return KeyTime.Equals(keyTime1, keyTime2); + } + + public static bool operator !=(KeyTime keyTime1, KeyTime keyTime2) + { + return !KeyTime.Equals(keyTime1, keyTime2); + } + + public bool Equals(KeyTime value) + { + return KeyTime.Equals(this, value); + } + + public override bool Equals(object value) + { + return value is KeyTime && this == (KeyTime)value; + } + + public override int GetHashCode() + { + return TimeSpan.GetHashCode(); + } + + public override string ToString() + { + return TimeSpan.ToString(); + } + + public static implicit operator KeyTime(TimeSpan timeSpan) + { + return KeyTime.FromTimeSpan(timeSpan); + } + + public TimeSpan TimeSpan + { + readonly get; private init; + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs similarity index 74% rename from src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs rename to src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs index ae07769f5..e69a3e458 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -3,68 +3,6 @@ namespace Windows.UI.Xaml.Media.Animation { using global::Windows.Foundation; - [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] - [WindowsRuntimeClassName("Windows.Foundation.IReference")] - [ABI.Windows.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] - [StructLayout(LayoutKind.Sequential)] - public struct KeyTime : IEquatable - { - public static KeyTime FromTimeSpan(TimeSpan timeSpan) - { - if (timeSpan < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(timeSpan)); - } - - return new KeyTime() { TimeSpan = timeSpan }; - } - - public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) - { - return (keyTime1.TimeSpan == keyTime2.TimeSpan); - } - - public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) - { - return KeyTime.Equals(keyTime1, keyTime2); - } - - public static bool operator !=(KeyTime keyTime1, KeyTime keyTime2) - { - return !KeyTime.Equals(keyTime1, keyTime2); - } - - public bool Equals(KeyTime value) - { - return KeyTime.Equals(this, value); - } - - public override bool Equals(object value) - { - return value is KeyTime && this == (KeyTime)value; - } - - public override int GetHashCode() - { - return TimeSpan.GetHashCode(); - } - - public override string ToString() - { - return TimeSpan.ToString(); - } - - public static implicit operator KeyTime(TimeSpan timeSpan) - { - return KeyTime.FromTimeSpan(timeSpan); - } - - public TimeSpan TimeSpan - { - readonly get; private init; - } - } - [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.Media.Animation.RepeatBehaviorComWrappersMarshaller] @@ -128,7 +66,7 @@ public bool HasDuration } public double Count - { + { readonly get; set; } @@ -138,8 +76,8 @@ public TimeSpan Duration } public RepeatBehaviorType Type - { - readonly get; set; + { + readonly get; set; } public override string ToString() diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs similarity index 100% rename from src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs rename to src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs similarity index 100% rename from src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs rename to src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs new file mode 100644 index 000000000..505a5e339 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs @@ -0,0 +1,149 @@ + +namespace Windows.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.CornerRadiusComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct CornerRadius : IEquatable + { + private double _TopLeft; + private double _TopRight; + private double _BottomRight; + private double _BottomLeft; + + public CornerRadius(double uniformRadius) + { + Validate(uniformRadius, uniformRadius, uniformRadius, uniformRadius); + _TopLeft = _TopRight = _BottomRight = _BottomLeft = uniformRadius; + } + + public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + Validate(topLeft, topRight, bottomRight, bottomLeft); + + _TopLeft = topLeft; + _TopRight = topRight; + _BottomRight = bottomRight; + _BottomLeft = bottomLeft; + } + + private static void Validate(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + if (topLeft < 0.0 || double.IsNaN(topLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopLeft")); + + if (topRight < 0.0 || double.IsNaN(topRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopRight")); + + if (bottomRight < 0.0 || double.IsNaN(bottomRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomRight")); + + if (bottomLeft < 0.0 || double.IsNaN(bottomLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(_TopLeft, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_TopRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomLeft, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + + public override bool Equals(object obj) + { + if (obj is CornerRadius) + { + CornerRadius otherObj = (CornerRadius)obj; + return (this == otherObj); + } + return (false); + } + + public bool Equals(CornerRadius cornerRadius) + { + return (this == cornerRadius); + } + + public override int GetHashCode() + { + return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); + } + + public static bool operator ==(CornerRadius cr1, CornerRadius cr2) + { + return cr1._TopLeft == cr2._TopLeft && cr1._TopRight == cr2._TopRight && cr1._BottomRight == cr2._BottomRight && cr1._BottomLeft == cr2._BottomLeft; + } + + public static bool operator !=(CornerRadius cr1, CornerRadius cr2) + { + return (!(cr1 == cr2)); + } + + public double TopLeft + { + get { return _TopLeft; } + set + { + Validate(value, 0, 0, 0); + _TopLeft = value; + } + } + + public double TopRight + { + get { return _TopRight; } + set + { + Validate(0, value, 0, 0); + _TopRight = value; + } + } + + public double BottomRight + { + get { return _BottomRight; } + set + { + Validate(0, 0, value, 0); + _BottomRight = value; + } + } + + public double BottomLeft + { + get { return _BottomLeft; } + set + { + Validate(0, 0, 0, value); + _BottomLeft = value; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs new file mode 100644 index 000000000..f536aa665 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs @@ -0,0 +1,292 @@ + +namespace Windows.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct Duration + { + private readonly TimeSpan _timeSpan; + private DurationType _durationType; + + public Duration(TimeSpan timeSpan) + { + _durationType = DurationType.TimeSpan; + _timeSpan = timeSpan; + } + + public static implicit operator Duration(TimeSpan timeSpan) + { + return new Duration(timeSpan); + } + + public static Duration operator +(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan + t2._timeSpan); + } + else if (t1._durationType != DurationType.Automatic && t2._durationType != DurationType.Automatic) + { + return Duration.Forever; + } + else + { + // Automatic + anything is Automatic + return Duration.Automatic; + } + } + + public static Duration operator -(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan - t2._timeSpan); + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return Duration.Forever; + } + else + { + return Duration.Automatic; + } + } + + public static bool operator ==(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public static bool operator !=(Duration t1, Duration t2) + { + return !(t1.Equals(t2)); + } + + public static bool operator >(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan > t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return false; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return true; + } + else + { + return false; + } + } + + public static bool operator >=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 < t2); + } + } + + public static bool operator <(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan < t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return true; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return false; + } + else + { + return false; + } + } + + public static bool operator <=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 > t2); + } + } + + public static int Compare(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic) + { + if (t2._durationType == DurationType.Automatic) + { + return 0; + } + else + { + return -1; + } + } + else if (t2._durationType == DurationType.Automatic) + { + return 1; + } + else + { + if (t1 < t2) + { + return -1; + } + else if (t1 > t2) + { + return 1; + } + else + { + return 0; + } + } + } + + public static Duration operator +(Duration duration) + { + return duration; + } + + public bool HasTimeSpan + { + get + { + return _durationType == DurationType.TimeSpan; + } + } + + public static Duration Automatic + { + get + { + Duration duration = default; + duration._durationType = DurationType.Automatic; + + return duration; + } + } + + public static Duration Forever + { + get + { + Duration duration = default; + duration._durationType = DurationType.Forever; + + return duration; + } + } + + public TimeSpan TimeSpan + { + get + { + if (HasTimeSpan) + { + return _timeSpan; + } + else + { + throw new InvalidOperationException(); + } + } + } + + public Duration Add(Duration duration) + { + return this + duration; + } + + public override bool Equals(object value) + { + return value is Duration && Equals((Duration)value); + } + + public bool Equals(Duration duration) + { + if (HasTimeSpan) + { + if (duration.HasTimeSpan) + { + return _timeSpan == duration._timeSpan; + } + else + { + return false; + } + } + else + { + return _durationType == duration._durationType; + } + } + + public static bool Equals(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public override int GetHashCode() + { + if (HasTimeSpan) + { + return _timeSpan.GetHashCode(); + } + else + { + return _durationType.GetHashCode() + 17; + } + } + + public Duration Subtract(Duration duration) + { + return this - duration; + } + + public override string ToString() + { + if (HasTimeSpan) + { + return _timeSpan.ToString(); // "00"; //TypeDescriptor.GetConverter(_timeSpan).ConvertToString(_timeSpan); + } + else if (_durationType == DurationType.Forever) + { + return "Forever"; + } + else // IsAutomatic + { + return "Automatic"; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs new file mode 100644 index 000000000..96a9d933b --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs @@ -0,0 +1,117 @@ + +namespace Windows.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct GridLength : IEquatable + { + private readonly double _unitValue; + private readonly GridUnitType _unitType; + + private const double Default = 1.0; + private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); + + public GridLength(double pixels) + : this(pixels, GridUnitType.Pixel) + { + } + + internal static bool IsFinite(double value) + { + return !(double.IsNaN(value) || double.IsInfinity(value)); + } + + public GridLength(double value, GridUnitType type) + { + if (!IsFinite(value) || value < 0.0) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(value)); + } + if (type != GridUnitType.Auto && type != GridUnitType.Pixel && type != GridUnitType.Star) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(type)); + } + + _unitValue = (type == GridUnitType.Auto) ? Default : value; + _unitType = type; + } + + + public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } + public GridUnitType GridUnitType { get { return (_unitType); } } + + + public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } + public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } + public bool IsStar { get { return (_unitType == GridUnitType.Star); } } + + public static GridLength Auto + { + get { return (s_auto); } + } + + + public static bool operator ==(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType == gl2.GridUnitType + && gl1.Value == gl2.Value); + } + + public static bool operator !=(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType != gl2.GridUnitType + || gl1.Value != gl2.Value); + } + + public override bool Equals(object oCompare) + { + if (oCompare is GridLength) + { + GridLength l = (GridLength)oCompare; + return (this == l); + } + else + return false; + } + + public bool Equals(GridLength gridLength) + { + return (this == gridLength); + } + + public override int GetHashCode() + { + return ((int)_unitValue + (int)_unitType); + } + + public override string ToString() + { + return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + // Initial capacity [64] is an estimate based on a sum of: + // 12 = 1x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 2 = 2x separator characters + + if (_unitType == GridUnitType.Auto) + { + return "Auto"; + } + else if (_unitType == GridUnitType.Pixel) + { + return Convert.ToString(_unitValue, cultureInfo); + } + else + { + return Convert.ToString(_unitValue, cultureInfo) + "*"; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs new file mode 100644 index 000000000..225d369f3 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs @@ -0,0 +1,44 @@ + +namespace Windows.UI.Xaml +{ + using global::Windows.Foundation; + + partial struct Thickness + { + public Thickness(double uniformLength) + { + Left = Top = Right = Bottom = uniformLength; + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(Left, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Top, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Right, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Bottom, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs deleted file mode 100644 index e3de03433..000000000 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs +++ /dev/null @@ -1,588 +0,0 @@ - -namespace Windows.UI.Xaml -{ - using global::Windows.Foundation; - - [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] - [WindowsRuntimeClassName("Windows.Foundation.IReference")] - [ABI.Windows.UI.Xaml.CornerRadiusComWrappersMarshaller] - [StructLayout(LayoutKind.Sequential)] - public struct CornerRadius : IEquatable - { - private double _TopLeft; - private double _TopRight; - private double _BottomRight; - private double _BottomLeft; - - public CornerRadius(double uniformRadius) - { - Validate(uniformRadius, uniformRadius, uniformRadius, uniformRadius); - _TopLeft = _TopRight = _BottomRight = _BottomLeft = uniformRadius; - } - - public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) - { - Validate(topLeft, topRight, bottomRight, bottomLeft); - - _TopLeft = topLeft; - _TopRight = topRight; - _BottomRight = bottomRight; - _BottomLeft = bottomLeft; - } - - private static void Validate(double topLeft, double topRight, double bottomRight, double bottomLeft) - { - if (topLeft < 0.0 || double.IsNaN(topLeft)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopLeft")); - - if (topRight < 0.0 || double.IsNaN(topRight)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopRight")); - - if (bottomRight < 0.0 || double.IsNaN(bottomRight)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomRight")); - - if (bottomLeft < 0.0 || double.IsNaN(bottomLeft)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); - } - - public override string ToString() - { - return ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); - - // Initial capacity [64] is an estimate based on a sum of: - // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(_TopLeft, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_TopRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomLeft, cultureInfo)); - return sb.ToString(); - } - - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) - { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); - } - - public override bool Equals(object obj) - { - if (obj is CornerRadius) - { - CornerRadius otherObj = (CornerRadius)obj; - return (this == otherObj); - } - return (false); - } - - public bool Equals(CornerRadius cornerRadius) - { - return (this == cornerRadius); - } - - public override int GetHashCode() - { - return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); - } - - public static bool operator ==(CornerRadius cr1, CornerRadius cr2) - { - return cr1._TopLeft == cr2._TopLeft && cr1._TopRight == cr2._TopRight && cr1._BottomRight == cr2._BottomRight && cr1._BottomLeft == cr2._BottomLeft; - } - - public static bool operator !=(CornerRadius cr1, CornerRadius cr2) - { - return (!(cr1 == cr2)); - } - - public double TopLeft - { - get { return _TopLeft; } - set - { - Validate(value, 0, 0, 0); - _TopLeft = value; - } - } - - public double TopRight - { - get { return _TopRight; } - set - { - Validate(0, value, 0, 0); - _TopRight = value; - } - } - - public double BottomRight - { - get { return _BottomRight; } - set - { - Validate(0, 0, value, 0); - _BottomRight = value; - } - } - - public double BottomLeft - { - get { return _BottomLeft; } - set - { - Validate(0, 0, 0, value); - _BottomLeft = value; - } - } - } - - - [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] - [WindowsRuntimeClassName("Windows.Foundation.IReference")] - [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] - [StructLayout(LayoutKind.Sequential)] - public struct GridLength : IEquatable - { - private readonly double _unitValue; - private readonly GridUnitType _unitType; - - private const double Default = 1.0; - private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); - - public GridLength(double pixels) - : this(pixels, GridUnitType.Pixel) - { - } - - internal static bool IsFinite(double value) - { - return !(double.IsNaN(value) || double.IsInfinity(value)); - } - - public GridLength(double value, GridUnitType type) - { - if (!IsFinite(value) || value < 0.0) - { - throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(value)); - } - if (type != GridUnitType.Auto && type != GridUnitType.Pixel && type != GridUnitType.Star) - { - throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(type)); - } - - _unitValue = (type == GridUnitType.Auto) ? Default : value; - _unitType = type; - } - - - public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } - public GridUnitType GridUnitType { get { return (_unitType); } } - - - public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } - public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } - public bool IsStar { get { return (_unitType == GridUnitType.Star); } } - - public static GridLength Auto - { - get { return (s_auto); } - } - - - public static bool operator ==(GridLength gl1, GridLength gl2) - { - return (gl1.GridUnitType == gl2.GridUnitType - && gl1.Value == gl2.Value); - } - - public static bool operator !=(GridLength gl1, GridLength gl2) - { - return (gl1.GridUnitType != gl2.GridUnitType - || gl1.Value != gl2.Value); - } - - public override bool Equals(object oCompare) - { - if (oCompare is GridLength) - { - GridLength l = (GridLength)oCompare; - return (this == l); - } - else - return false; - } - - public bool Equals(GridLength gridLength) - { - return (this == gridLength); - } - - public override int GetHashCode() - { - return ((int)_unitValue + (int)_unitType); - } - - public override string ToString() - { - return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - // Initial capacity [64] is an estimate based on a sum of: - // 12 = 1x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 2 = 2x separator characters - - if (_unitType == GridUnitType.Auto) - { - return "Auto"; - } - else if (_unitType == GridUnitType.Pixel) - { - return Convert.ToString(_unitValue, cultureInfo); - } - else - { - return Convert.ToString(_unitValue, cultureInfo) + "*"; - } - } - } - - partial struct Thickness - { - public Thickness(double uniformLength) - { - Left = Top = Right = Bottom = uniformLength; - } - - public override string ToString() - { - return ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); - - // Initial capacity [64] is an estimate based on a sum of: - // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(Left, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Top, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Right, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Bottom, cultureInfo)); - return sb.ToString(); - } - - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) - { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); - } - } - - [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] - [WindowsRuntimeClassName("Windows.Foundation.IReference")] - [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] - [StructLayout(LayoutKind.Sequential)] - public struct Duration - { - private readonly TimeSpan _timeSpan; - private DurationType _durationType; - - public Duration(TimeSpan timeSpan) - { - _durationType = DurationType.TimeSpan; - _timeSpan = timeSpan; - } - - public static implicit operator Duration(TimeSpan timeSpan) - { - return new Duration(timeSpan); - } - - public static Duration operator +(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return new Duration(t1._timeSpan + t2._timeSpan); - } - else if (t1._durationType != DurationType.Automatic && t2._durationType != DurationType.Automatic) - { - return Duration.Forever; - } - else - { - // Automatic + anything is Automatic - return Duration.Automatic; - } - } - - public static Duration operator -(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return new Duration(t1._timeSpan - t2._timeSpan); - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return Duration.Forever; - } - else - { - return Duration.Automatic; - } - } - - public static bool operator ==(Duration t1, Duration t2) - { - return t1.Equals(t2); - } - - public static bool operator !=(Duration t1, Duration t2) - { - return !(t1.Equals(t2)); - } - - public static bool operator >(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return t1._timeSpan > t2._timeSpan; - } - else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) - { - return false; - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return true; - } - else - { - return false; - } - } - - public static bool operator >=(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) - { - return true; - } - else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) - { - return false; - } - else - { - return !(t1 < t2); - } - } - - public static bool operator <(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return t1._timeSpan < t2._timeSpan; - } - else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) - { - return true; - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return false; - } - else - { - return false; - } - } - - public static bool operator <=(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) - { - return true; - } - else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) - { - return false; - } - else - { - return !(t1 > t2); - } - } - - public static int Compare(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic) - { - if (t2._durationType == DurationType.Automatic) - { - return 0; - } - else - { - return -1; - } - } - else if (t2._durationType == DurationType.Automatic) - { - return 1; - } - else - { - if (t1 < t2) - { - return -1; - } - else if (t1 > t2) - { - return 1; - } - else - { - return 0; - } - } - } - - public static Duration operator +(Duration duration) - { - return duration; - } - - public bool HasTimeSpan - { - get - { - return _durationType == DurationType.TimeSpan; - } - } - - public static Duration Automatic - { - get - { - Duration duration = default; - duration._durationType = DurationType.Automatic; - - return duration; - } - } - - public static Duration Forever - { - get - { - Duration duration = default; - duration._durationType = DurationType.Forever; - - return duration; - } - } - - public TimeSpan TimeSpan - { - get - { - if (HasTimeSpan) - { - return _timeSpan; - } - else - { - throw new InvalidOperationException(); - } - } - } - - public Duration Add(Duration duration) - { - return this + duration; - } - - public override bool Equals(object value) - { - return value is Duration && Equals((Duration)value); - } - - public bool Equals(Duration duration) - { - if (HasTimeSpan) - { - if (duration.HasTimeSpan) - { - return _timeSpan == duration._timeSpan; - } - else - { - return false; - } - } - else - { - return _durationType == duration._durationType; - } - } - - public static bool Equals(Duration t1, Duration t2) - { - return t1.Equals(t2); - } - - public override int GetHashCode() - { - if (HasTimeSpan) - { - return _timeSpan.GetHashCode(); - } - else - { - return _durationType.GetHashCode() + 17; - } - } - - public Duration Subtract(Duration duration) - { - return this - duration; - } - - public override string ToString() - { - if (HasTimeSpan) - { - return _timeSpan.ToString(); // "00"; //TypeDescriptor.GetConverter(_timeSpan).ConvertToString(_timeSpan); - } - else if (_durationType == DurationType.Forever) - { - return "Forever"; - } - else // IsAutomatic - { - return "Automatic"; - } - } - } -} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs similarity index 100% rename from src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs rename to src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs From f5ccbd0cae9534849015b7a534bf23663d40c23e Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 25 Oct 2025 00:22:10 -0700 Subject: [PATCH 23/43] Get WinUI projection building --- src/Projections/WinUI/WinUI.csproj | 16 +- src/cswinrt/code_writers.h | 17 +- src/cswinrt/cswinrt.vcxproj | 14 +- src/cswinrt/cswinrt.vcxproj.filters | 25 +- src/cswinrt/helpers.h | 36 +- ...l.Controls.Primitives.GeneratorPosition.cs | 13 + .../Microsoft.UI.Xaml.Controls.Primitives.cs | 76 -- ...crosoft.UI.Xaml.Media.Animation.KeyTime.cs | 67 ++ ....UI.Xaml.Media.Animation.RepeatBehavior.cs | 186 ++++ .../Microsoft.UI.Xaml.Media.Animation.cs | 335 -------- ...crosoft.UI.Xaml.Media.Media3D.Matrix3D.cs} | 65 +- .../Microsoft.UI.Xaml.Media.Matrix.cs | 105 +++ .../Microsoft.UI.Xaml.Media.cs | 266 ------ .../Microsoft.UI.Xaml.CornerRadius.cs | 149 ++++ .../Microsoft.UI.Xaml.Duration.cs | 292 +++++++ .../Microsoft.UI.Xaml.GridLength.cs | 117 +++ .../Microsoft.UI.Xaml.Thickness.cs | 44 + .../Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs | 792 ------------------ .../Windows.UI.Xaml.Duration.cs | 2 +- 19 files changed, 1067 insertions(+), 1550 deletions(-) create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs delete mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs delete mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs rename src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/{Microsoft.UI.Xaml.Media.Media3D.cs => Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs} (95%) create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs delete mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs create mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs delete mode 100644 src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs diff --git a/src/Projections/WinUI/WinUI.csproj b/src/Projections/WinUI/WinUI.csproj index 4335af0c2..d4e974e7a 100644 --- a/src/Projections/WinUI/WinUI.csproj +++ b/src/Projections/WinUI/WinUI.csproj @@ -10,7 +10,7 @@ - + @@ -22,22 +22,8 @@ -exclude Windows -include Microsoft -# The current WinUI nuget incorrectly references several Windows.* types that should be -# Microsoft.* types instead. Temporarily include these to enable the build --include Windows.UI.Xaml.Interop.Type --include Windows.UI.Xaml.Interop.NotifyCollectionChangedAction -include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.StyleTypedPropertyAttribute --include Windows.UI.Xaml.TemplatePartAttribute --include Windows.UI.Xaml.TemplateVisualStateAttribute -include Windows.UI.Xaml.Data.BindableAttribute --include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.Markup.FullXamlMetadataProviderAttribute --include Windows.UI.Xaml.Markup.MarkupExtensionReturnTypeAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.IndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute --addition_exclude Windows.UI.Xaml.Media.Animation $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index c3646fef9..e2a8c5283 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -8443,12 +8443,27 @@ return objectReference.AsValue(); } else { - w.write(R"( + auto default_type_semantics = get_type_semantics(get_default_interface(type)); + auto default_interface_typedef = for_typedef(w, default_type_semantics, [&](auto&& iface) { return iface; }); + if (!is_exclusive_to(default_interface_typedef)) + { + w.write(R"( +if (value is IWindowsRuntimeInterface<%> windowsRuntimeInterface) +{ +return windowsRuntimeInterface.GetInterface(); +} +)", + bind(default_type_semantics, typedef_name_type::Projected, false)); + } + else + { + w.write(R"( if (value is not null) { return value.GetDefaultInterface(); } )"); + } } }), projected_type_name, diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index e273d9cc4..0b66b8165 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -107,11 +107,15 @@ - - - - - + + + + + + + + + diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 37db48b14..38c896d7e 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -73,7 +73,7 @@ strings\additions\Windows.Foundation - + strings\additions\Microsoft.UI.Xaml @@ -163,16 +163,19 @@ strings\additions\Windows.UI - + strings\additions\Microsoft.UI.Xaml.Media.Media3D - + strings\additions\Microsoft.UI.Xaml.Media.Animation - + strings\additions\Microsoft.UI.Xaml.Media - + + strings\additions\Microsoft.UI.Xaml + + strings\additions\Microsoft.UI.Xaml.Controls.Primitives @@ -229,6 +232,18 @@ strings\additions\Windows.UI.Xaml.Media.Animation + + strings\additions\Microsoft.UI.Xaml + + + strings\additions\Microsoft.UI.Xaml + + + strings\additions\Microsoft.UI.Xaml + + + strings\additions\Microsoft.UI.Xaml.Media.Animation + diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 5649bcdee..4aef9f7b8 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -697,12 +697,10 @@ namespace cswinrt { { "CornerRadius", "Microsoft.UI.Xaml", "CornerRadius", false, false, true }, { "CornerRadiusHelper" }, - { "Duration", "Microsoft.UI.Xaml", "Duration" }, + { "Duration", "Microsoft.UI.Xaml", "Duration", false, false, true }, { "DurationHelper" }, - { "DurationType", "Microsoft.UI.Xaml", "DurationType" }, - { "GridLength", "Microsoft.UI.Xaml", "GridLength" }, + { "GridLength", "Microsoft.UI.Xaml", "GridLength", false, false, true }, { "GridLengthHelper" }, - { "GridUnitType", "Microsoft.UI.Xaml", "GridUnitType" }, { "ICornerRadiusHelper" }, { "ICornerRadiusHelperStatics" }, { "IDurationHelper" }, @@ -711,14 +709,12 @@ namespace cswinrt { "IGridLengthHelperStatics" }, { "IThicknessHelper" }, { "IThicknessHelperStatics" }, - { "Thickness", "Microsoft.UI.Xaml", "Thickness" }, { "ThicknessHelper" }, { "IXamlServiceProvider", "System", "IServiceProvider" }, } }, { "Microsoft.UI.Xaml.Controls.Primitives", { - { "GeneratorPosition", "Microsoft.UI.Xaml.Controls.Primitives", "GeneratorPosition" }, { "GeneratorPositionHelper" }, { "IGeneratorPositionHelper" }, { "IGeneratorPositionHelperStatics" }, @@ -752,7 +748,6 @@ namespace cswinrt { { "IMatrixHelper" }, { "IMatrixHelperStatics" }, - { "Matrix", "Microsoft.UI.Xaml.Media", "Matrix" }, { "MatrixHelper" }, } }, @@ -762,18 +757,17 @@ namespace cswinrt { "IKeyTimeHelperStatics" }, { "IRepeatBehaviorHelper" }, { "IRepeatBehaviorHelperStatics" }, - { "KeyTime", "Microsoft.UI.Xaml.Media.Animation", "KeyTime" }, + { "KeyTime", "Microsoft.UI.Xaml.Media.Animation", "KeyTime", false, false, true }, { "KeyTimeHelper" }, - { "RepeatBehavior", "Microsoft.UI.Xaml.Media.Animation", "RepeatBehavior" }, + { "RepeatBehavior", "Microsoft.UI.Xaml.Media.Animation", "RepeatBehavior", false, false, true }, { "RepeatBehaviorHelper" }, - { "RepeatBehaviorType", "Microsoft.UI.Xaml.Media.Animation", "RepeatBehaviorType" } } }, { "Microsoft.UI.Xaml.Media.Media3D", { { "IMatrix3DHelper" }, { "IMatrix3DHelperStatics" }, - { "Matrix3D", "Microsoft.UI.Xaml.Media.Media3D", "Matrix3D" }, + { "Matrix3D", "Microsoft.UI.Xaml.Media.Media3D", "Matrix3D", false, false, true }, { "Matrix3DHelper" }, } }, @@ -1195,6 +1189,26 @@ namespace cswinrt std::vector types; } addition_types[] = { + { "Microsoft.UI.Xaml", + { + "Thickness" + } + }, + { "Microsoft.UI.Xaml.Controls.Primitives", + { + "GeneratorPosition" + } + }, + { "Microsoft.UI.Xaml.Media", + { + "Matrix" + } + }, + { "Microsoft.UI.Xaml.Media.Animation", + { + "KeyTime" + } + }, { "Windows.UI", { "Color", diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs new file mode 100644 index 000000000..fd5f26790 --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs @@ -0,0 +1,13 @@ + +namespace Microsoft.UI.Xaml.Controls.Primitives +{ + using global::Windows.Foundation; + + partial struct GeneratorPosition + { + public override string ToString() + { + return string.Concat("GeneratorPosition (", Index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", Offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs deleted file mode 100644 index f0ecbcf61..000000000 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs +++ /dev/null @@ -1,76 +0,0 @@ - -namespace Microsoft.UI.Xaml.Controls.Primitives -{ - using global::Windows.Foundation; - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct GeneratorPosition - { - private int _index; - private int _offset; - - public int Index { get { return _index; } set { _index = value; } } - public int Offset { get { return _offset; } set { _offset = value; } } - - public GeneratorPosition(int index, int offset) - { - _index = index; - _offset = offset; - } - - public override int GetHashCode() - { - return _index.GetHashCode() + _offset.GetHashCode(); - } - - public override string ToString() - { - return string.Concat("GeneratorPosition (", _index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", _offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); - } - - public override bool Equals(object o) - { - if (o is GeneratorPosition) - { - GeneratorPosition that = (GeneratorPosition)o; - return _index == that._index && - _offset == that._offset; - } - return false; - } - - public static bool operator ==(GeneratorPosition gp1, GeneratorPosition gp2) - { - return gp1._index == gp2._index && - gp1._offset == gp2._offset; - } - - public static bool operator !=(GeneratorPosition gp1, GeneratorPosition gp2) - { - return !(gp1 == gp2); - } - } -} - -namespace ABI.Microsoft.UI.Xaml.Controls.Primitives -{ -#if EMBED - internal -#else - public -#endif - static class GeneratorPosition - { - public static string GetGuidSignature() => $"struct(Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition;i4;i4)"; - } -} diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs new file mode 100644 index 000000000..b6a92832b --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs @@ -0,0 +1,67 @@ + +namespace Microsoft.UI.Xaml.Media.Animation +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct KeyTime : IEquatable + { + public static KeyTime FromTimeSpan(TimeSpan timeSpan) + { + if (timeSpan < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan)); + } + + return new KeyTime() { TimeSpan = timeSpan }; + } + + public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) + { + return (keyTime1.TimeSpan == keyTime2.TimeSpan); + } + + public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) + { + return KeyTime.Equals(keyTime1, keyTime2); + } + + public static bool operator !=(KeyTime keyTime1, KeyTime keyTime2) + { + return !KeyTime.Equals(keyTime1, keyTime2); + } + + public bool Equals(KeyTime value) + { + return KeyTime.Equals(this, value); + } + + public override bool Equals(object value) + { + return value is KeyTime && this == (KeyTime)value; + } + + public override int GetHashCode() + { + return TimeSpan.GetHashCode(); + } + + public override string ToString() + { + return TimeSpan.ToString(); + } + + public static implicit operator KeyTime(TimeSpan timeSpan) + { + return KeyTime.FromTimeSpan(timeSpan); + } + + public TimeSpan TimeSpan + { + readonly get; private init; + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs new file mode 100644 index 000000000..26cbcec3a --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -0,0 +1,186 @@ + +namespace Microsoft.UI.Xaml.Media.Animation +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.Media.Animation.RepeatBehaviorComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct RepeatBehavior : IFormattable, IEquatable + { + internal static bool IsFinite(double value) + { + return !(double.IsNaN(value) || double.IsInfinity(value)); + } + + public RepeatBehavior(double count) + { + if (!IsFinite(count) || count < 0.0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + Duration = new TimeSpan(0); + Count = count; + Type = RepeatBehaviorType.Count; + } + + public RepeatBehavior(TimeSpan duration) + { + if (duration < new TimeSpan(0)) + { + throw new ArgumentOutOfRangeException(nameof(duration)); + } + + Duration = duration; + Count = 0.0; + Type = RepeatBehaviorType.Duration; + } + + public static RepeatBehavior Forever + { + get + { + RepeatBehavior forever = default; + forever.Type = RepeatBehaviorType.Forever; + + return forever; + } + } + + public bool HasCount + { + get + { + return Type == RepeatBehaviorType.Count; + } + } + + public bool HasDuration + { + get + { + return Type == RepeatBehaviorType.Duration; + } + } + + public double Count + { + readonly get; set; + } + + public TimeSpan Duration + { + readonly get; set; + } + + public RepeatBehaviorType Type + { + readonly get; set; + } + + public override string ToString() + { + return InternalToString(null, null); + } + + public string ToString(IFormatProvider formatProvider) + { + return InternalToString(null, formatProvider); + } + + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + return InternalToString(format, formatProvider); + } + + internal string InternalToString(string format, IFormatProvider formatProvider) + { + switch (Type) + { + case RepeatBehaviorType.Forever: + + return "Forever"; + + case RepeatBehaviorType.Count: + + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); + + sb.AppendFormat( + formatProvider, + "{0:" + format + "}x", + Count); + + return sb.ToString(); + + case RepeatBehaviorType.Duration: + + return Duration.ToString(); + + default: + return string.Empty; + } + } + + public override bool Equals(object value) + { + if (value is RepeatBehavior) + { + return this.Equals((RepeatBehavior)value); + } + else + { + return false; + } + } + + public bool Equals(RepeatBehavior repeatBehavior) + { + if (Type == repeatBehavior.Type) + { + return Type switch + { + RepeatBehaviorType.Forever => true, + RepeatBehaviorType.Count => Count == repeatBehavior.Count, + RepeatBehaviorType.Duration => Duration == repeatBehavior.Duration, + _ => false, + }; + } + else + { + return false; + } + } + + public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return repeatBehavior1.Equals(repeatBehavior2); + } + + public override int GetHashCode() + { + return Type switch + { + RepeatBehaviorType.Count => Count.GetHashCode(), + RepeatBehaviorType.Duration => Duration.GetHashCode(), + + // We try to choose an unlikely hash code value for Forever. + // All Forevers need to return the same hash code value. + RepeatBehaviorType.Forever => int.MaxValue - 42, + + _ => base.GetHashCode(), + }; + } + + public static bool operator ==(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return repeatBehavior1.Equals(repeatBehavior2); + } + + public static bool operator !=(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return !repeatBehavior1.Equals(repeatBehavior2); + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs deleted file mode 100644 index 1ad2d3305..000000000 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs +++ /dev/null @@ -1,335 +0,0 @@ - -namespace Microsoft.UI.Xaml.Media.Animation -{ - using global::Windows.Foundation; - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.KeyTime))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct KeyTime - { - private TimeSpan _timeSpan; - - public static KeyTime FromTimeSpan(TimeSpan timeSpan) - { - if (timeSpan < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(timeSpan)); - } - - KeyTime keyTime = default; - - keyTime._timeSpan = timeSpan; - - return keyTime; - } - - public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) - { - return (keyTime1._timeSpan == keyTime2._timeSpan); - } - - public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) - { - return KeyTime.Equals(keyTime1, keyTime2); - } - - public static bool operator !=(KeyTime keyTime1, KeyTime keyTime2) - { - return !KeyTime.Equals(keyTime1, keyTime2); - } - - public bool Equals(KeyTime value) - { - return KeyTime.Equals(this, value); - } - - public override bool Equals(object value) - { - return value is KeyTime && this == (KeyTime)value; - } - - public override int GetHashCode() - { - return _timeSpan.GetHashCode(); - } - - public override string ToString() - { - return _timeSpan.ToString(); - } - - public static implicit operator KeyTime(TimeSpan timeSpan) - { - return KeyTime.FromTimeSpan(timeSpan); - } - - public TimeSpan TimeSpan - { - get - { - return _timeSpan; - } - } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum RepeatBehaviorType - { - Count, - Duration, - Forever - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.RepeatBehavior))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct RepeatBehavior : IFormattable - { - private double _Count; - private TimeSpan _Duration; - private RepeatBehaviorType _Type; - - internal static bool IsFinite(double value) - { - return !(double.IsNaN(value) || double.IsInfinity(value)); - } - - public RepeatBehavior(double count) - { - if (!IsFinite(count) || count < 0.0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - _Duration = new TimeSpan(0); - _Count = count; - _Type = RepeatBehaviorType.Count; - } - - public RepeatBehavior(TimeSpan duration) - { - if (duration < new TimeSpan(0)) - { - throw new ArgumentOutOfRangeException(nameof(duration)); - } - - _Duration = duration; - _Count = 0.0; - _Type = RepeatBehaviorType.Duration; - } - - public static RepeatBehavior Forever - { - get - { - RepeatBehavior forever = default; - forever.Type = RepeatBehaviorType.Forever; - - return forever; - } - } - - public bool HasCount - { - get - { - return Type == RepeatBehaviorType.Count; - } - } - - public bool HasDuration - { - get - { - return Type == RepeatBehaviorType.Duration; - } - } - - public double Count - { - get { return _Count; } - set { _Count = value; } - } - - public TimeSpan Duration - { - get { return _Duration; } - set { _Duration = value; } - } - - public RepeatBehaviorType Type - { - get { return _Type; } - set { _Type = value; } - } - - public override string ToString() - { - return InternalToString(null, null); - } - - public string ToString(IFormatProvider formatProvider) - { - return InternalToString(null, formatProvider); - } - - string IFormattable.ToString(string format, IFormatProvider formatProvider) - { - return InternalToString(format, formatProvider); - } - - internal string InternalToString(string format, IFormatProvider formatProvider) - { - switch (_Type) - { - case RepeatBehaviorType.Forever: - - return "Forever"; - - case RepeatBehaviorType.Count: - - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); - - sb.AppendFormat( - formatProvider, - "{0:" + format + "}x", - _Count); - - return sb.ToString(); - - case RepeatBehaviorType.Duration: - - return _Duration.ToString(); - - default: - return string.Empty; - } - } - - public override bool Equals(object value) - { - if (value is RepeatBehavior) - { - return this.Equals((RepeatBehavior)value); - } - else - { - return false; - } - } - - public bool Equals(RepeatBehavior repeatBehavior) - { - if (_Type == repeatBehavior._Type) - { - return _Type switch - { - RepeatBehaviorType.Forever => true, - RepeatBehaviorType.Count => _Count == repeatBehavior._Count, - RepeatBehaviorType.Duration => _Duration == repeatBehavior._Duration, - _ => false, - }; - } - else - { - return false; - } - } - - public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) - { - return repeatBehavior1.Equals(repeatBehavior2); - } - - public override int GetHashCode() - { - return _Type switch - { - RepeatBehaviorType.Count => _Count.GetHashCode(), - RepeatBehaviorType.Duration => _Duration.GetHashCode(), - - // We try to choose an unlikely hash code value for Forever. - // All Forevers need to return the same hash code value. - RepeatBehaviorType.Forever => int.MaxValue - 42, - - _ => base.GetHashCode(), - }; - } - - public static bool operator ==(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) - { - return repeatBehavior1.Equals(repeatBehavior2); - } - - public static bool operator !=(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) - { - return !repeatBehavior1.Equals(repeatBehavior2); - } - } -} - -namespace ABI.Microsoft.UI.Xaml.Media.Animation -{ -#if EMBED - internal -#else - public -#endif - static class KeyTime - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - return $"struct(Microsoft.UI.Xaml.Media.Animation.KeyTime;{timeSpanSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class RepeatBehavior - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - return $"struct(Microsoft.UI.Xaml.Media.Animation.RepeatBehavior;f8;{timeSpanSignature};{RepeatBehaviorType.GetGuidSignature()})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class RepeatBehaviorType - { - public static string GetGuidSignature() => "enum(Microsoft.UI.Xaml.Media.Animation.RepeatBehaviorType;i4)"; - } -} diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs similarity index 95% rename from src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs rename to src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs index fdf1a9974..f661c89a3 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -3,18 +3,11 @@ namespace Microsoft.UI.Xaml.Media.Media3D { using global::Windows.Foundation; - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Media3D.Matrix3D))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.Media.Media3D.Matrix3DComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Matrix3D : IFormattable + public struct Matrix3D : IFormattable { // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: // Returns false if HasInverse is false, otherwise inverts the matrix. @@ -183,7 +176,7 @@ public Matrix3D(double m11, double m12, double m13, double m14, public double M11 { - get + readonly get { return _m11; } @@ -195,7 +188,7 @@ public double M11 public double M12 { - get + readonly get { return _m12; } @@ -207,7 +200,7 @@ public double M12 public double M13 { - get + readonly get { return _m13; } @@ -219,7 +212,7 @@ public double M13 public double M14 { - get + readonly get { return _m14; } @@ -231,7 +224,7 @@ public double M14 public double M21 { - get + readonly get { return _m21; } @@ -243,7 +236,7 @@ public double M21 public double M22 { - get + readonly get { return _m22; } @@ -255,7 +248,7 @@ public double M22 public double M23 { - get + readonly get { return _m23; } @@ -267,7 +260,7 @@ public double M23 public double M24 { - get + readonly get { return _m24; } @@ -279,7 +272,7 @@ public double M24 public double M31 { - get + readonly get { return _m31; } @@ -291,7 +284,7 @@ public double M31 public double M32 { - get + readonly get { return _m32; } @@ -303,7 +296,7 @@ public double M32 public double M33 { - get + readonly get { return _m33; } @@ -315,7 +308,7 @@ public double M33 public double M34 { - get + readonly get { return _m34; } @@ -327,7 +320,7 @@ public double M34 public double OffsetX { - get + readonly get { return _offsetX; } @@ -339,7 +332,7 @@ public double OffsetX public double OffsetY { - get + readonly get { return _offsetY; } @@ -351,7 +344,7 @@ public double OffsetY public double OffsetZ { - get + readonly get { return _offsetZ; } @@ -363,7 +356,7 @@ public double OffsetZ public double M44 { - get + readonly get { return _m44; } @@ -418,7 +411,7 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = TokenizerHelper.GetNumericListSeparator(provider); + char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}{0}{7:" + format + "}{0}{8:" + format + "}{0}{9:" + format + "}{0}{10:" + format + @@ -697,18 +690,4 @@ private static bool IsZero(double value) private double _offsetZ; private double _m44; } -} - -namespace ABI.Microsoft.UI.Xaml.Media.Media3D -{ -#if EMBED - internal -#else - public -#endif - static class Matrix3D - { - public static string GetGuidSignature() => - $"struct(Microsoft.UI.Xaml.Media.Media3D.Matrix3D;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8)"; - } -} +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs new file mode 100644 index 000000000..6656640b0 --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs @@ -0,0 +1,105 @@ + +namespace Microsoft.UI.Xaml.Media +{ + using global::Windows.Foundation; + + partial struct Matrix : IFormattable + { + // the transform is identity by default + private static Matrix s_identity = CreateIdentity(); + + public static Matrix Identity + { + get + { + return s_identity; + } + } + + public bool IsIdentity + { + get + { + return (M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0); + } + } + + public override string ToString() + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, null /* format provider */); + } + + public string ToString(IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(format, provider); + } + + private string ConvertToString(string format, IFormatProvider provider) + { + if (IsIdentity) + { + return "Identity"; + } + + // Helper to get the numeric list separator for a given culture. + char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); + return string.Format(provider, + "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", + separator, + M11, + M12, + M21, + M22, + OffsetX, + OffsetY); + } + + public Point Transform(Point point) + { + float x = (float)point.X; + float y = (float)point.Y; + this.MultiplyPoint(ref x, ref y); + Point point2 = new Point(x, y); + return point2; + } + + private static Matrix CreateIdentity() + { + Matrix matrix = default; + matrix.SetMatrix(1, 0, + 0, 1, + 0, 0); + return matrix; + } + + private void SetMatrix(double m11, double m12, + double m21, double m22, + double offsetX, double offsetY) + { + M11 = m11; + M12 = m12; + M21 = m21; + M22 = m22; + OffsetX = offsetX; + OffsetY = offsetY; + } + + private void MultiplyPoint(ref float x, ref float y) + { + double num = (y * M21) + OffsetX; + double num2 = (x * M12) + OffsetY; + x *= (float)M11; + x += (float)num; + y *= (float)M22; + y += (float)num2; + } + } +} diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs deleted file mode 100644 index b361bcbab..000000000 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs +++ /dev/null @@ -1,266 +0,0 @@ - -namespace Microsoft.UI.Xaml.Media -{ - using global::Windows.Foundation; - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Matrix))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Matrix : IFormattable - { - public Matrix(double m11, double m12, - double m21, double m22, - double offsetX, double offsetY) - { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; - _offsetX = offsetX; - _offsetY = offsetY; - } - - // the transform is identity by default - private static Matrix s_identity = CreateIdentity(); - - public double M11 - { - get - { - return _m11; - } - set - { - _m11 = value; - } - } - - public double M12 - { - get - { - return _m12; - } - set - { - _m12 = value; - } - } - - public double M21 - { - get - { - return _m21; - } - set - { - _m21 = value; - } - } - - public double M22 - { - get - { - return _m22; - } - set - { - _m22 = value; - } - } - - public double OffsetX - { - get - { - return _offsetX; - } - set - { - _offsetX = value; - } - } - - public double OffsetY - { - get - { - return _offsetY; - } - set - { - _offsetY = value; - } - } - - public static Matrix Identity - { - get - { - return s_identity; - } - } - - public bool IsIdentity - { - get - { - return (_m11 == 1 && _m12 == 0 && _m21 == 0 && _m22 == 1 && _offsetX == 0 && _offsetY == 0); - } - } - - public override string ToString() - { - // Delegate to the internal method which implements all ToString calls. - return ConvertToString(null /* format string */, null /* format provider */); - } - - public string ToString(IFormatProvider provider) - { - // Delegate to the internal method which implements all ToString calls. - return ConvertToString(null /* format string */, provider); - } - - string IFormattable.ToString(string format, IFormatProvider provider) - { - // Delegate to the internal method which implements all ToString calls. - return ConvertToString(format, provider); - } - - private string ConvertToString(string format, IFormatProvider provider) - { - if (IsIdentity) - { - return "Identity"; - } - - // Helper to get the numeric list separator for a given culture. - char separator = TokenizerHelper.GetNumericListSeparator(provider); - return string.Format(provider, - "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", - separator, - _m11, - _m12, - _m21, - _m22, - _offsetX, - _offsetY); - } - - public Point Transform(Point point) - { - float x = (float)point.X; - float y = (float)point.Y; - this.MultiplyPoint(ref x, ref y); - Point point2 = new Point(x, y); - return point2; - } - - public override int GetHashCode() - { - // Perform field-by-field XOR of HashCodes - return M11.GetHashCode() ^ - M12.GetHashCode() ^ - M21.GetHashCode() ^ - M22.GetHashCode() ^ - OffsetX.GetHashCode() ^ - OffsetY.GetHashCode(); - } - - public override bool Equals(object o) - { - return o is Matrix && Matrix.Equals(this, (Matrix)o); - } - - public bool Equals(Matrix value) - { - return Matrix.Equals(this, value); - } - - public static bool operator ==(Matrix matrix1, Matrix matrix2) - { - return matrix1.M11 == matrix2.M11 && - matrix1.M12 == matrix2.M12 && - matrix1.M21 == matrix2.M21 && - matrix1.M22 == matrix2.M22 && - matrix1.OffsetX == matrix2.OffsetX && - matrix1.OffsetY == matrix2.OffsetY; - } - - public static bool operator !=(Matrix matrix1, Matrix matrix2) - { - return !(matrix1 == matrix2); - } - - private static Matrix CreateIdentity() - { - Matrix matrix = default; - matrix.SetMatrix(1, 0, - 0, 1, - 0, 0); - return matrix; - } - - private void SetMatrix(double m11, double m12, - double m21, double m22, - double offsetX, double offsetY) - { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; - _offsetX = offsetX; - _offsetY = offsetY; - } - - private void MultiplyPoint(ref float x, ref float y) - { - double num = (y * _m21) + _offsetX; - double num2 = (x * _m12) + _offsetY; - x *= (float)_m11; - x += (float)num; - y *= (float)_m22; - y += (float)num2; - } - - private static bool Equals(Matrix matrix1, Matrix matrix2) - { - return matrix1.M11.Equals(matrix2.M11) && - matrix1.M12.Equals(matrix2.M12) && - matrix1.M21.Equals(matrix2.M21) && - matrix1.M22.Equals(matrix2.M22) && - matrix1.OffsetX.Equals(matrix2.OffsetX) && - matrix1.OffsetY.Equals(matrix2.OffsetY); - } - - private double _m11; - private double _m12; - private double _m21; - private double _m22; - private double _offsetX; - private double _offsetY; - } -} - -namespace ABI.Microsoft.UI.Xaml.Media -{ -#if EMBED - internal -#else - public -#endif - static class Matrix - { - public static string GetGuidSignature() => $"struct(Microsoft.UI.Xaml.Media.Matrix;f8;f8;f8;f8;f8;f8)"; - } -} diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs new file mode 100644 index 000000000..7699e7d7e --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs @@ -0,0 +1,149 @@ + +namespace Microsoft.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.CornerRadiusComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct CornerRadius : IEquatable + { + private double _TopLeft; + private double _TopRight; + private double _BottomRight; + private double _BottomLeft; + + public CornerRadius(double uniformRadius) + { + Validate(uniformRadius, uniformRadius, uniformRadius, uniformRadius); + _TopLeft = _TopRight = _BottomRight = _BottomLeft = uniformRadius; + } + + public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + Validate(topLeft, topRight, bottomRight, bottomLeft); + + _TopLeft = topLeft; + _TopRight = topRight; + _BottomRight = bottomRight; + _BottomLeft = bottomLeft; + } + + private static void Validate(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + if (topLeft < 0.0 || double.IsNaN(topLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopLeft")); + + if (topRight < 0.0 || double.IsNaN(topRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopRight")); + + if (bottomRight < 0.0 || double.IsNaN(bottomRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomRight")); + + if (bottomLeft < 0.0 || double.IsNaN(bottomLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(_TopLeft, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_TopRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomLeft, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + + public override bool Equals(object obj) + { + if (obj is CornerRadius) + { + CornerRadius otherObj = (CornerRadius)obj; + return (this == otherObj); + } + return (false); + } + + public bool Equals(CornerRadius cornerRadius) + { + return (this == cornerRadius); + } + + public override int GetHashCode() + { + return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); + } + + public static bool operator ==(CornerRadius cr1, CornerRadius cr2) + { + return cr1._TopLeft == cr2._TopLeft && cr1._TopRight == cr2._TopRight && cr1._BottomRight == cr2._BottomRight && cr1._BottomLeft == cr2._BottomLeft; + } + + public static bool operator !=(CornerRadius cr1, CornerRadius cr2) + { + return (!(cr1 == cr2)); + } + + public double TopLeft + { + get { return _TopLeft; } + set + { + Validate(value, 0, 0, 0); + _TopLeft = value; + } + } + + public double TopRight + { + get { return _TopRight; } + set + { + Validate(0, value, 0, 0); + _TopRight = value; + } + } + + public double BottomRight + { + get { return _BottomRight; } + set + { + Validate(0, 0, value, 0); + _BottomRight = value; + } + } + + public double BottomLeft + { + get { return _BottomLeft; } + set + { + Validate(0, 0, 0, value); + _BottomLeft = value; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs new file mode 100644 index 000000000..52cffb763 --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs @@ -0,0 +1,292 @@ + +namespace Microsoft.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.DurationComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct Duration + { + private readonly TimeSpan _timeSpan; + private DurationType _durationType; + + public Duration(TimeSpan timeSpan) + { + _durationType = DurationType.TimeSpan; + _timeSpan = timeSpan; + } + + public static implicit operator Duration(TimeSpan timeSpan) + { + return new Duration(timeSpan); + } + + public static Duration operator +(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan + t2._timeSpan); + } + else if (t1._durationType != DurationType.Automatic && t2._durationType != DurationType.Automatic) + { + return Duration.Forever; + } + else + { + // Automatic + anything is Automatic + return Duration.Automatic; + } + } + + public static Duration operator -(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan - t2._timeSpan); + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return Duration.Forever; + } + else + { + return Duration.Automatic; + } + } + + public static bool operator ==(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public static bool operator !=(Duration t1, Duration t2) + { + return !(t1.Equals(t2)); + } + + public static bool operator >(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan > t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return false; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return true; + } + else + { + return false; + } + } + + public static bool operator >=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 < t2); + } + } + + public static bool operator <(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan < t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return true; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return false; + } + else + { + return false; + } + } + + public static bool operator <=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 > t2); + } + } + + public static int Compare(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic) + { + if (t2._durationType == DurationType.Automatic) + { + return 0; + } + else + { + return -1; + } + } + else if (t2._durationType == DurationType.Automatic) + { + return 1; + } + else + { + if (t1 < t2) + { + return -1; + } + else if (t1 > t2) + { + return 1; + } + else + { + return 0; + } + } + } + + public static Duration operator +(Duration duration) + { + return duration; + } + + public bool HasTimeSpan + { + get + { + return _durationType == DurationType.TimeSpan; + } + } + + public static Duration Automatic + { + get + { + Duration duration = default; + duration._durationType = DurationType.Automatic; + + return duration; + } + } + + public static Duration Forever + { + get + { + Duration duration = default; + duration._durationType = DurationType.Forever; + + return duration; + } + } + + public TimeSpan TimeSpan + { + get + { + if (HasTimeSpan) + { + return _timeSpan; + } + else + { + throw new InvalidOperationException(); + } + } + } + + public Duration Add(Duration duration) + { + return this + duration; + } + + public override bool Equals(object value) + { + return value is Duration && Equals((Duration)value); + } + + public bool Equals(Duration duration) + { + if (HasTimeSpan) + { + if (duration.HasTimeSpan) + { + return _timeSpan == duration._timeSpan; + } + else + { + return false; + } + } + else + { + return _durationType == duration._durationType; + } + } + + public static bool Equals(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public override int GetHashCode() + { + if (HasTimeSpan) + { + return _timeSpan.GetHashCode(); + } + else + { + return _durationType.GetHashCode() + 17; + } + } + + public Duration Subtract(Duration duration) + { + return this - duration; + } + + public override string ToString() + { + if (HasTimeSpan) + { + return _timeSpan.ToString(); // "00"; //TypeDescriptor.GetConverter(_timeSpan).ConvertToString(_timeSpan); + } + else if (_durationType == DurationType.Forever) + { + return "Forever"; + } + else // IsAutomatic + { + return "Automatic"; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs new file mode 100644 index 000000000..104aaf4f2 --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs @@ -0,0 +1,117 @@ + +namespace Microsoft.UI.Xaml +{ + using global::Windows.Foundation; + + [WindowsRuntimeMetadata("Microsoft.UI")] + [WindowsRuntimeClassName("Windows.Foundation.IReference")] + [ABI.Microsoft.UI.Xaml.GridLengthComWrappersMarshaller] + [StructLayout(LayoutKind.Sequential)] + public struct GridLength : IEquatable + { + private readonly double _unitValue; + private readonly GridUnitType _unitType; + + private const double Default = 1.0; + private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); + + public GridLength(double pixels) + : this(pixels, GridUnitType.Pixel) + { + } + + internal static bool IsFinite(double value) + { + return !(double.IsNaN(value) || double.IsInfinity(value)); + } + + public GridLength(double value, GridUnitType type) + { + if (!IsFinite(value) || value < 0.0) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(value)); + } + if (type != GridUnitType.Auto && type != GridUnitType.Pixel && type != GridUnitType.Star) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(type)); + } + + _unitValue = (type == GridUnitType.Auto) ? Default : value; + _unitType = type; + } + + + public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } + public GridUnitType GridUnitType { get { return (_unitType); } } + + + public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } + public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } + public bool IsStar { get { return (_unitType == GridUnitType.Star); } } + + public static GridLength Auto + { + get { return (s_auto); } + } + + + public static bool operator ==(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType == gl2.GridUnitType + && gl1.Value == gl2.Value); + } + + public static bool operator !=(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType != gl2.GridUnitType + || gl1.Value != gl2.Value); + } + + public override bool Equals(object oCompare) + { + if (oCompare is GridLength) + { + GridLength l = (GridLength)oCompare; + return (this == l); + } + else + return false; + } + + public bool Equals(GridLength gridLength) + { + return (this == gridLength); + } + + public override int GetHashCode() + { + return ((int)_unitValue + (int)_unitType); + } + + public override string ToString() + { + return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + // Initial capacity [64] is an estimate based on a sum of: + // 12 = 1x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 2 = 2x separator characters + + if (_unitType == GridUnitType.Auto) + { + return "Auto"; + } + else if (_unitType == GridUnitType.Pixel) + { + return Convert.ToString(_unitValue, cultureInfo); + } + else + { + return Convert.ToString(_unitValue, cultureInfo) + "*"; + } + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs new file mode 100644 index 000000000..e4add31a4 --- /dev/null +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs @@ -0,0 +1,44 @@ + +namespace Microsoft.UI.Xaml +{ + using global::Windows.Foundation; + + partial struct Thickness + { + public Thickness(double uniformLength) + { + Left = Top = Right = Bottom = uniformLength; + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(Left, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Top, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Right, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(Bottom, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + } +} \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs deleted file mode 100644 index d9388a8f1..000000000 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs +++ /dev/null @@ -1,792 +0,0 @@ - -namespace Microsoft.UI.Xaml -{ - using global::Windows.Foundation; - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.CornerRadius))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct CornerRadius - { - private double _TopLeft; - private double _TopRight; - private double _BottomRight; - private double _BottomLeft; - - public CornerRadius(double uniformRadius) - { - Validate(uniformRadius, uniformRadius, uniformRadius, uniformRadius); - _TopLeft = _TopRight = _BottomRight = _BottomLeft = uniformRadius; - } - - public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) - { - Validate(topLeft, topRight, bottomRight, bottomLeft); - - _TopLeft = topLeft; - _TopRight = topRight; - _BottomRight = bottomRight; - _BottomLeft = bottomLeft; - } - - private static void Validate(double topLeft, double topRight, double bottomRight, double bottomLeft) - { - if (topLeft < 0.0 || double.IsNaN(topLeft)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopLeft")); - - if (topRight < 0.0 || double.IsNaN(topRight)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopRight")); - - if (bottomRight < 0.0 || double.IsNaN(bottomRight)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomRight")); - - if (bottomLeft < 0.0 || double.IsNaN(bottomLeft)) - throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); - } - - public override string ToString() - { - return ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); - - // Initial capacity [64] is an estimate based on a sum of: - // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(_TopLeft, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_TopRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomLeft, cultureInfo)); - return sb.ToString(); - } - - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) - { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); - } - - public override bool Equals(object obj) - { - if (obj is CornerRadius) - { - CornerRadius otherObj = (CornerRadius)obj; - return (this == otherObj); - } - return (false); - } - - public bool Equals(CornerRadius cornerRadius) - { - return (this == cornerRadius); - } - - public override int GetHashCode() - { - return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); - } - - public static bool operator ==(CornerRadius cr1, CornerRadius cr2) - { - return cr1._TopLeft == cr2._TopLeft && cr1._TopRight == cr2._TopRight && cr1._BottomRight == cr2._BottomRight && cr1._BottomLeft == cr2._BottomLeft; - } - - public static bool operator !=(CornerRadius cr1, CornerRadius cr2) - { - return (!(cr1 == cr2)); - } - - public double TopLeft - { - get { return _TopLeft; } - set - { - Validate(value, 0, 0, 0); - _TopLeft = value; - } - } - - public double TopRight - { - get { return _TopRight; } - set - { - Validate(0, value, 0, 0); - _TopRight = value; - } - } - - public double BottomRight - { - get { return _BottomRight; } - set - { - Validate(0, 0, value, 0); - _BottomRight = value; - } - } - - public double BottomLeft - { - get { return _BottomLeft; } - set - { - Validate(0, 0, 0, value); - _BottomLeft = value; - } - } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum GridUnitType - { - Auto = 0, - Pixel, - Star, - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.GridLength))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct GridLength - { - private readonly double _unitValue; - private readonly GridUnitType _unitType; - - private const double Default = 1.0; - private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); - - public GridLength(double pixels) - : this(pixels, GridUnitType.Pixel) - { - } - - internal static bool IsFinite(double value) - { - return !(double.IsNaN(value) || double.IsInfinity(value)); - } - - public GridLength(double value, GridUnitType type) - { - if (!IsFinite(value) || value < 0.0) - { - throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(value)); - } - if (type != GridUnitType.Auto && type != GridUnitType.Pixel && type != GridUnitType.Star) - { - throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(type)); - } - - _unitValue = (type == GridUnitType.Auto) ? Default : value; - _unitType = type; - } - - - public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } - public GridUnitType GridUnitType { get { return (_unitType); } } - - - public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } - public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } - public bool IsStar { get { return (_unitType == GridUnitType.Star); } } - - public static GridLength Auto - { - get { return (s_auto); } - } - - - public static bool operator ==(GridLength gl1, GridLength gl2) - { - return (gl1.GridUnitType == gl2.GridUnitType - && gl1.Value == gl2.Value); - } - - public static bool operator !=(GridLength gl1, GridLength gl2) - { - return (gl1.GridUnitType != gl2.GridUnitType - || gl1.Value != gl2.Value); - } - - public override bool Equals(object oCompare) - { - if (oCompare is GridLength) - { - GridLength l = (GridLength)oCompare; - return (this == l); - } - else - return false; - } - - public bool Equals(GridLength gridLength) - { - return (this == gridLength); - } - - public override int GetHashCode() - { - return ((int)_unitValue + (int)_unitType); - } - - public override string ToString() - { - return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); - - // Initial capacity [64] is an estimate based on a sum of: - // 12 = 1x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 2 = 2x separator characters - - if (_unitType == GridUnitType.Auto) - { - return "Auto"; - } - else if (_unitType == GridUnitType.Pixel) - { - return Convert.ToString(_unitValue, cultureInfo); - } - else - { - return Convert.ToString(_unitValue, cultureInfo) + "*"; - } - } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Thickness))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Thickness - { - private double _Left; - private double _Top; - private double _Right; - private double _Bottom; - - public Thickness(double uniformLength) - { - _Left = _Top = _Right = _Bottom = uniformLength; - } - - public Thickness(double left, double top, double right, double bottom) - { - _Left = left; - _Top = top; - _Right = right; - _Bottom = bottom; - } - - public double Left - { - get { return _Left; } - set { _Left = value; } - } - - public double Top - { - get { return _Top; } - set { _Top = value; } - } - - public double Right - { - get { return _Right; } - set { _Right = value; } - } - - public double Bottom - { - get { return _Bottom; } - set { _Bottom = value; } - } - - public override string ToString() - { - return ToString(global::System.Globalization.CultureInfo.InvariantCulture); - } - - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) - { - char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); - - // Initial capacity [64] is an estimate based on a sum of: - // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(_Left, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_Top, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_Right, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_Bottom, cultureInfo)); - return sb.ToString(); - } - - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) - { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); - } - - public override bool Equals(object obj) - { - if (obj is Thickness) - { - Thickness otherObj = (Thickness)obj; - return (this == otherObj); - } - return (false); - } - - public bool Equals(Thickness thickness) - { - return (this == thickness); - } - - public override int GetHashCode() - { - return _Left.GetHashCode() ^ _Top.GetHashCode() ^ _Right.GetHashCode() ^ _Bottom.GetHashCode(); - } - - public static bool operator ==(Thickness t1, Thickness t2) - { - return t1._Left == t2._Left && t1._Top == t2._Top && t1._Right == t2._Right && t1._Bottom == t2._Bottom; - } - - public static bool operator !=(Thickness t1, Thickness t2) - { - return (!(t1 == t2)); - } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - enum DurationType - { - Automatic, - TimeSpan, - Forever - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Duration))] -#if NET - [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] -#endif - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct Duration - { - private readonly TimeSpan _timeSpan; - private DurationType _durationType; - - public Duration(TimeSpan timeSpan) - { - _durationType = DurationType.TimeSpan; - _timeSpan = timeSpan; - } - - public static implicit operator Duration(TimeSpan timeSpan) - { - return new Duration(timeSpan); - } - - public static Duration operator +(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return new Duration(t1._timeSpan + t2._timeSpan); - } - else if (t1._durationType != DurationType.Automatic && t2._durationType != DurationType.Automatic) - { - return Duration.Forever; - } - else - { - // Automatic + anything is Automatic - return Duration.Automatic; - } - } - - public static Duration operator -(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return new Duration(t1._timeSpan - t2._timeSpan); - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return Duration.Forever; - } - else - { - return Duration.Automatic; - } - } - - public static bool operator ==(Duration t1, Duration t2) - { - return t1.Equals(t2); - } - - public static bool operator !=(Duration t1, Duration t2) - { - return !(t1.Equals(t2)); - } - - public static bool operator >(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return t1._timeSpan > t2._timeSpan; - } - else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) - { - return false; - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return true; - } - else - { - return false; - } - } - - public static bool operator >=(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) - { - return true; - } - else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) - { - return false; - } - else - { - return !(t1 < t2); - } - } - - public static bool operator <(Duration t1, Duration t2) - { - if (t1.HasTimeSpan && t2.HasTimeSpan) - { - return t1._timeSpan < t2._timeSpan; - } - else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) - { - return true; - } - else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) - { - return false; - } - else - { - return false; - } - } - - public static bool operator <=(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) - { - return true; - } - else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) - { - return false; - } - else - { - return !(t1 > t2); - } - } - - public static int Compare(Duration t1, Duration t2) - { - if (t1._durationType == DurationType.Automatic) - { - if (t2._durationType == DurationType.Automatic) - { - return 0; - } - else - { - return -1; - } - } - else if (t2._durationType == DurationType.Automatic) - { - return 1; - } - else - { - if (t1 < t2) - { - return -1; - } - else if (t1 > t2) - { - return 1; - } - else - { - return 0; - } - } - } - - public static Duration operator +(Duration duration) - { - return duration; - } - - public bool HasTimeSpan - { - get - { - return _durationType == DurationType.TimeSpan; - } - } - - public static Duration Automatic - { - get - { - Duration duration = default; - duration._durationType = DurationType.Automatic; - - return duration; - } - } - - public static Duration Forever - { - get - { - Duration duration = default; - duration._durationType = DurationType.Forever; - - return duration; - } - } - - public TimeSpan TimeSpan - { - get - { - if (HasTimeSpan) - { - return _timeSpan; - } - else - { - throw new InvalidOperationException(); - } - } - } - - public Duration Add(Duration duration) - { - return this + duration; - } - - public override bool Equals(object value) - { - return value is Duration && Equals((Duration)value); - } - - public bool Equals(Duration duration) - { - if (HasTimeSpan) - { - if (duration.HasTimeSpan) - { - return _timeSpan == duration._timeSpan; - } - else - { - return false; - } - } - else - { - return _durationType == duration._durationType; - } - } - - public static bool Equals(Duration t1, Duration t2) - { - return t1.Equals(t2); - } - - public override int GetHashCode() - { - if (HasTimeSpan) - { - return _timeSpan.GetHashCode(); - } - else - { - return _durationType.GetHashCode() + 17; - } - } - - public Duration Subtract(Duration duration) - { - return this - duration; - } - - public override string ToString() - { - if (HasTimeSpan) - { - return _timeSpan.ToString(); // "00"; //TypeDescriptor.GetConverter(_timeSpan).ConvertToString(_timeSpan); - } - else if (_durationType == DurationType.Forever) - { - return "Forever"; - } - else // IsAutomatic - { - return "Automatic"; - } - } - } -} - -namespace ABI.Microsoft.UI.Xaml -{ -#if EMBED - internal -#else - public -#endif - static class CornerRadius - { - public static string GetGuidSignature() => $"struct(Microsoft.UI.Xaml.CornerRadius;f8;f8;f8;f8)"; - } - -#if EMBED - internal -#else - public -#endif - static class Duration - { - public static string GetGuidSignature() - { - string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); - string durationTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Microsoft.UI.Xaml.DurationType)); - return $"struct(Microsoft.UI.Xaml.Duration;{timeSpanSignature};{durationTypeSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class DurationType - { - public static string GetGuidSignature() => "enum(Microsoft.UI.Xaml.DurationType;i4)"; - } - -#if EMBED - internal -#else - public -#endif - static class GridLength - { - public static string GetGuidSignature() - { - string unitTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Microsoft.UI.Xaml.GridUnitType)); - return $"struct(Microsoft.UI.Xaml.GridLength;f8;{unitTypeSignature})"; - } - } - -#if EMBED - internal -#else - public -#endif - static class GridUnitType - { - public static string GetGuidSignature() => "enum(Microsoft.UI.Xaml.GridUnitType;i4)"; - } - -#if EMBED - internal -#else - public -#endif - static class Thickness - { - public static string GetGuidSignature() => $"struct(Microsoft.UI.Xaml.Thickness;f8;f8;f8;f8)"; - } -} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs index f536aa665..29f24f282 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs @@ -5,7 +5,7 @@ namespace Windows.UI.Xaml [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] [WindowsRuntimeClassName("Windows.Foundation.IReference")] - [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] + [ABI.Windows.UI.Xaml.DurationComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] public struct Duration { From eb2a0327e44c198c56439414291fd1cc0b26b61b Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 25 Oct 2025 00:46:09 -0700 Subject: [PATCH 24/43] Get other projections building --- src/Projections/Benchmark/Benchmark.csproj | 2 +- src/Projections/Test/Test.csproj | 3 --- .../TestHost.ProbeByClass/TestHost.ProbeByClass.csproj | 2 +- .../TestPublicExclusiveTo/TestPublicExclusiveTo.csproj | 2 +- src/Projections/TestSubset/TestSubset.csproj | 2 +- src/Projections/WinAppSDK/WinAppSDK.csproj | 2 +- .../Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs | 2 +- .../Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs | 8 ++++---- .../Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs | 2 +- .../Windows.UI.Xaml.Media.Media3D.Matrix3D.cs | 2 +- .../Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs | 8 ++++---- .../additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs | 2 +- 12 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Projections/Benchmark/Benchmark.csproj b/src/Projections/Benchmark/Benchmark.csproj index 5a51a7bea..98eb73b9d 100644 --- a/src/Projections/Benchmark/Benchmark.csproj +++ b/src/Projections/Benchmark/Benchmark.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj index 6aadaac37..77e452072 100644 --- a/src/Projections/Test/Test.csproj +++ b/src/Projections/Test/Test.csproj @@ -14,10 +14,7 @@ - - diff --git a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj index c0ce44fb7..7ff120ddf 100644 --- a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj +++ b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj index 373ce54de..2f73e1922 100644 --- a/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj +++ b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Projections/TestSubset/TestSubset.csproj b/src/Projections/TestSubset/TestSubset.csproj index a95cc18cf..1c5100bb8 100644 --- a/src/Projections/TestSubset/TestSubset.csproj +++ b/src/Projections/TestSubset/TestSubset.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Projections/WinAppSDK/WinAppSDK.csproj b/src/Projections/WinAppSDK/WinAppSDK.csproj index c8967af31..b072cde2b 100644 --- a/src/Projections/WinAppSDK/WinAppSDK.csproj +++ b/src/Projections/WinAppSDK/WinAppSDK.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs index f661c89a3..84c8ec206 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -7,7 +7,7 @@ namespace Microsoft.UI.Xaml.Media.Media3D [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Microsoft.UI.Xaml.Media.Media3D.Matrix3DComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Matrix3D : IFormattable + public struct Matrix3D : IFormattable, IEquatable { // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: // Returns false if HasInverse is false, otherwise inverts the matrix. diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs index 7699e7d7e..d9f2cf2e2 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs @@ -108,7 +108,7 @@ public override int GetHashCode() public double TopLeft { - get { return _TopLeft; } + readonly get { return _TopLeft; } set { Validate(value, 0, 0, 0); @@ -118,7 +118,7 @@ public double TopLeft public double TopRight { - get { return _TopRight; } + readonly get { return _TopRight; } set { Validate(0, value, 0, 0); @@ -128,7 +128,7 @@ public double TopRight public double BottomRight { - get { return _BottomRight; } + readonly get { return _BottomRight; } set { Validate(0, 0, value, 0); @@ -138,7 +138,7 @@ public double BottomRight public double BottomLeft { - get { return _BottomLeft; } + readonly get { return _BottomLeft; } set { Validate(0, 0, 0, value); diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs index 52cffb763..e04bab35f 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs @@ -7,7 +7,7 @@ namespace Microsoft.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Microsoft.UI.Xaml.DurationComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Duration + public struct Duration : IEquatable { private readonly TimeSpan _timeSpan; private DurationType _durationType; diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs index c7360aadc..c80c8bf89 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -7,7 +7,7 @@ namespace Windows.UI.Xaml.Media.Media3D [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.Media.Media3D.Matrix3DComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Matrix3D : IFormattable + public struct Matrix3D : IFormattable, IEquatable { // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: // Returns false if HasInverse is false, otherwise inverts the matrix. diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs index 505a5e339..35c93ac8f 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs @@ -108,7 +108,7 @@ public override int GetHashCode() public double TopLeft { - get { return _TopLeft; } + readonly get { return _TopLeft; } set { Validate(value, 0, 0, 0); @@ -118,7 +118,7 @@ public double TopLeft public double TopRight { - get { return _TopRight; } + readonly get { return _TopRight; } set { Validate(0, value, 0, 0); @@ -128,7 +128,7 @@ public double TopRight public double BottomRight { - get { return _BottomRight; } + readonly get { return _BottomRight; } set { Validate(0, 0, value, 0); @@ -138,7 +138,7 @@ public double BottomRight public double BottomLeft { - get { return _BottomLeft; } + readonly get { return _BottomLeft; } set { Validate(0, 0, 0, value); diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs index 29f24f282..eeee7b095 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs @@ -7,7 +7,7 @@ namespace Windows.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.DurationComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Duration + public struct Duration : IEquatable { private readonly TimeSpan _timeSpan; private DurationType _durationType; From 1c1740a72eca53a982ff36ed3dbc2a201253172a Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 25 Oct 2025 00:48:22 -0700 Subject: [PATCH 25/43] Enable in slnx --- src/cswinrt.slnx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cswinrt.slnx b/src/cswinrt.slnx index 121993490..b9b827804 100644 --- a/src/cswinrt.slnx +++ b/src/cswinrt.slnx @@ -55,7 +55,8 @@ - + + @@ -77,28 +78,32 @@ - + + - + + - + + - + + @@ -113,7 +118,8 @@ - + + From 7afa14961e278ab693ccb83cce011608e81b5cc6 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 25 Oct 2025 01:23:08 -0700 Subject: [PATCH 26/43] More fixes --- src/cswinrt/main.cpp | 2 +- .../additions/Windows.Foundation/AsyncInfo.cs | 472 ++-- .../Windows.Foundation/ITaskAwareAsyncInfo.cs | 4 +- .../TaskToAsyncInfoAdapter.cs | 2020 ++++++++--------- .../Windows.Foundation/Windows.Foundation.cs | 794 +++---- 5 files changed, 1646 insertions(+), 1646 deletions(-) diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index c66871253..74564d5c9 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -409,7 +409,7 @@ Where is one or more of: // Custom additions to namespaces for (auto addition : strings::additions) { - if (ns == addition.name && (ns != "Windows.Storage" && ns != "Windows.Storage.Streams" && ns != "Windows.Foundation") && settings.addition_filter.includes(ns)) + if (ns == addition.name && (ns != "Windows.Storage" && ns != "Windows.Storage.Streams") && settings.addition_filter.includes(ns)) { w.write(addition.value); } diff --git a/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs b/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs index 81378030f..13e70df63 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs @@ -1,244 +1,244 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Runtime.InteropServices.WindowsRuntime -{ - using System.Diagnostics; - using System.Threading; - using System.Threading.Tasks; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System.Diagnostics; + using System.Threading; + using System.Threading.Tasks; using global::Windows.Foundation; - ///

Provides factory methods to construct WinRT-compatible representations of asynchronous operations.

- ///

The factory methods take as inputs functions (delegates) that provide managed Task objects; - /// Different factory methods return different sub-interfaces of Windows.Foundation.IAyncInfo. - /// When an asynchronous operation created by this factory is actually started (by calling Start()), - /// the specified Task-provider delegate will be invoked to create the Task that will - /// be wrapped by the to-WinRT adapter.

-#if NET + ///

Provides factory methods to construct WinRT-compatible representations of asynchronous operations.

+ ///

The factory methods take as inputs functions (delegates) that provide managed Task objects; + /// Different factory methods return different sub-interfaces of Windows.Foundation.IAyncInfo. + /// When an asynchronous operation created by this factory is actually started (by calling Start()), + /// the specified Task-provider delegate will be invoked to create the Task that will + /// be wrapped by the to-WinRT adapter.

+#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif +#endif #if EMBED internal #else public #endif - static class AsyncInfo - { - #region Factory methods for creating "normal" IAsyncInfo instances backed by a Task created by a pastProvider delegate - - /// - /// Creates and starts an instance from a function that generates - /// a . - /// Use this overload if your task supports cancellation in order to hook-up the Cancel - /// mechanism exposed by the created asynchronous action and the cancellation of your task. - /// The function to invoke to create the task when the IAsyncInfo is started. - /// The function is passed a that the task may monitor - /// to be notified of a cancellation request; - /// you may ignore the CancellationToken if your task does not support cancellation. - /// An unstarted instance. - public static IAsyncAction Run(Func taskProvider) - { - if (taskProvider == null) - throw new ArgumentNullException(nameof(taskProvider)); - - return new TaskToAsyncActionAdapter(taskProvider); - } - - - /// - /// Creates and starts an instance from a function - /// that generates a . - /// Use this overload if your task supports cancellation and progress monitoring is order to: - /// (1) hook-up the Cancel mechanism of the created asynchronous action and the cancellation of your task, - /// and (2) hook-up the Progress update delegate exposed by the created async action and the progress updates - /// published by your task. - /// The function to invoke to create the task when the IAsyncInfo is started. - /// The function is passed a that the task may monitor - /// to be notified of a cancellation request; - /// you may ignore the CancellationToken if your task does not support cancellation. - /// It is also passed a instance to which progress updates may be published; - /// you may ignore the IProgress if your task does not support progress reporting. - /// An unstarted instance. - public static IAsyncActionWithProgress Run(Func, Task> taskProvider) - { - if (taskProvider == null) - throw new ArgumentNullException(nameof(taskProvider)); - - return new TaskToAsyncActionWithProgressAdapter(taskProvider); - } - - - /// - /// Creates and starts an instance from a function - /// that generates a . - /// Use this overload if your task supports cancellation in order to hook-up the Cancel - /// mechanism exposed by the created asynchronous operation and the cancellation of your task. - /// The function to invoke to create the task when the IAsyncInfo is started. - /// The function is passed a that the task may monitor - /// to be notified of a cancellation request; - /// you may ignore the CancellationToken if your task does not support cancellation. - /// An unstarted instance. - public static IAsyncOperation Run(Func> taskProvider) - { - // This is only internal to reduce the number of public overloads. - // Code execution flows through this method when the method above is called. We can always make this public. - - if (taskProvider == null) - throw new ArgumentNullException(nameof(taskProvider)); - - return new TaskToAsyncOperationAdapter(taskProvider); - } - - - /// - /// Creates and starts an instance - /// from a function that generates a .
- /// Use this overload if your task supports cancellation and progress monitoring is order to: - /// (1) hook-up the Cancel mechanism of the created asynchronous operation and the cancellation of your task, - /// and (2) hook-up the Progress update delegate exposed by the created async operation and the progress - /// updates published by your task.
- /// The result type of the task. - /// The type used for progress notifications. - /// The function to invoke to create the task when the IAsyncOperationWithProgress is started.
- /// The function is passed a that the task may monitor - /// to be notified of a cancellation request; - /// you may ignore the CancellationToken if your task does not support cancellation. - /// It is also passed a instance to which progress updates may be published; - /// you may ignore the IProgress if your task does not support progress reporting. - /// An unstarted instance. - public static IAsyncOperationWithProgress Run( - Func, Task> taskProvider) - { - if (taskProvider == null) - throw new ArgumentNullException(nameof(taskProvider)); - - return new TaskToAsyncOperationWithProgressAdapter(taskProvider); - } - - #endregion Factory methods for creating "normal" IAsyncInfo instances backed by a Task created by a pastProvider delegate - - - #region Factory methods for creating IAsyncInfo instances that have already completed synchronously - - public static IAsyncAction CompletedAction() - { - var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: false); - return asyncInfo; - } - - - public static IAsyncActionWithProgress CompletedActionWithProgress() - { - var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: false); - return asyncInfo; - } - - - public static IAsyncOperation FromResult(TResult synchronousResult) - { - var asyncInfo = new TaskToAsyncOperationAdapter(synchronousResult); - return asyncInfo; - } - - - public static IAsyncOperationWithProgress FromResultWithProgress(TResult synchronousResult) - { - var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(synchronousResult); - return asyncInfo; - } - - #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously - - #region Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error - - public static IAsyncAction FromException(Exception error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - - var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: false); - - asyncInfo.DangerousSetError(error); - Debug.Assert(asyncInfo.Status == AsyncStatus.Error); - - return asyncInfo; - } - - - public static IAsyncActionWithProgress FromExceptionWithProgress(Exception error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - - var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: false); - - asyncInfo.DangerousSetError(error); - Debug.Assert(asyncInfo.Status == AsyncStatus.Error); - - return asyncInfo; - } - - - public static IAsyncOperation FromException(Exception error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - - var asyncInfo = new TaskToAsyncOperationAdapter(default(TResult)); - - asyncInfo.DangerousSetError(error); - Debug.Assert(asyncInfo.Status == AsyncStatus.Error); - - return asyncInfo; - } - - - public static IAsyncOperationWithProgress FromExceptionWithProgress(Exception error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - - var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(default(TResult)); - - asyncInfo.DangerousSetError(error); - Debug.Assert(asyncInfo.Status == AsyncStatus.Error); - - return asyncInfo; - } - - #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error - - #region Factory methods for creating IAsyncInfo instances that have already been canceled synchronously - - public static IAsyncAction CanceledAction() - { - var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: true); - return asyncInfo; - } - - - public static IAsyncActionWithProgress CanceledActionWithProgress() - { - var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: true); - return asyncInfo; - } - - - public static IAsyncOperation CanceledOperation() - { - var asyncInfo = new TaskToAsyncOperationAdapter(isCanceled: true); - return asyncInfo; - } - - - public static IAsyncOperationWithProgress CanceledOperationWithProgress() - { - var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(isCanceled: true); - return asyncInfo; - } - - #endregion Factory methods for creating IAsyncInfo instances that have already been canceled synchronously - - } // class AsyncInfo -} // namespace + static class AsyncInfo + { + #region Factory methods for creating "normal" IAsyncInfo instances backed by a Task created by a pastProvider delegate + + /// + /// Creates and starts an instance from a function that generates + /// a . + /// Use this overload if your task supports cancellation in order to hook-up the Cancel + /// mechanism exposed by the created asynchronous action and the cancellation of your task. + /// The function to invoke to create the task when the IAsyncInfo is started. + /// The function is passed a that the task may monitor + /// to be notified of a cancellation request; + /// you may ignore the CancellationToken if your task does not support cancellation. + /// An unstarted instance. + public static IAsyncAction Run(Func taskProvider) + { + if (taskProvider == null) + throw new ArgumentNullException(nameof(taskProvider)); + + return new TaskToAsyncActionAdapter(taskProvider); + } + + + /// + /// Creates and starts an instance from a function + /// that generates a . + /// Use this overload if your task supports cancellation and progress monitoring is order to: + /// (1) hook-up the Cancel mechanism of the created asynchronous action and the cancellation of your task, + /// and (2) hook-up the Progress update delegate exposed by the created async action and the progress updates + /// published by your task. + /// The function to invoke to create the task when the IAsyncInfo is started. + /// The function is passed a that the task may monitor + /// to be notified of a cancellation request; + /// you may ignore the CancellationToken if your task does not support cancellation. + /// It is also passed a instance to which progress updates may be published; + /// you may ignore the IProgress if your task does not support progress reporting. + /// An unstarted instance. + public static IAsyncActionWithProgress Run(Func, Task> taskProvider) + { + if (taskProvider == null) + throw new ArgumentNullException(nameof(taskProvider)); + + return new TaskToAsyncActionWithProgressAdapter(taskProvider); + } + + + /// + /// Creates and starts an instance from a function + /// that generates a . + /// Use this overload if your task supports cancellation in order to hook-up the Cancel + /// mechanism exposed by the created asynchronous operation and the cancellation of your task. + /// The function to invoke to create the task when the IAsyncInfo is started. + /// The function is passed a that the task may monitor + /// to be notified of a cancellation request; + /// you may ignore the CancellationToken if your task does not support cancellation. + /// An unstarted instance. + public static IAsyncOperation Run(Func> taskProvider) + { + // This is only internal to reduce the number of public overloads. + // Code execution flows through this method when the method above is called. We can always make this public. + + if (taskProvider == null) + throw new ArgumentNullException(nameof(taskProvider)); + + return new TaskToAsyncOperationAdapter(taskProvider); + } + + + /// + /// Creates and starts an instance + /// from a function that generates a .
+ /// Use this overload if your task supports cancellation and progress monitoring is order to: + /// (1) hook-up the Cancel mechanism of the created asynchronous operation and the cancellation of your task, + /// and (2) hook-up the Progress update delegate exposed by the created async operation and the progress + /// updates published by your task.
+ /// The result type of the task. + /// The type used for progress notifications. + /// The function to invoke to create the task when the IAsyncOperationWithProgress is started.
+ /// The function is passed a that the task may monitor + /// to be notified of a cancellation request; + /// you may ignore the CancellationToken if your task does not support cancellation. + /// It is also passed a instance to which progress updates may be published; + /// you may ignore the IProgress if your task does not support progress reporting. + /// An unstarted instance. + public static IAsyncOperationWithProgress Run( + Func, Task> taskProvider) + { + if (taskProvider == null) + throw new ArgumentNullException(nameof(taskProvider)); + + return new TaskToAsyncOperationWithProgressAdapter(taskProvider); + } + + #endregion Factory methods for creating "normal" IAsyncInfo instances backed by a Task created by a pastProvider delegate + + + #region Factory methods for creating IAsyncInfo instances that have already completed synchronously + + public static IAsyncAction CompletedAction() + { + var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: false); + return asyncInfo; + } + + + public static IAsyncActionWithProgress CompletedActionWithProgress() + { + var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: false); + return asyncInfo; + } + + + public static IAsyncOperation FromResult(TResult synchronousResult) + { + var asyncInfo = new TaskToAsyncOperationAdapter(synchronousResult); + return asyncInfo; + } + + + public static IAsyncOperationWithProgress FromResultWithProgress(TResult synchronousResult) + { + var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(synchronousResult); + return asyncInfo; + } + + #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously + + #region Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error + + public static IAsyncAction FromException(Exception error) + { + if (error == null) + throw new ArgumentNullException(nameof(error)); + + var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: false); + + asyncInfo.DangerousSetError(error); + Debug.Assert(asyncInfo.Status == AsyncStatus.Error); + + return asyncInfo; + } + + + public static IAsyncActionWithProgress FromExceptionWithProgress(Exception error) + { + if (error == null) + throw new ArgumentNullException(nameof(error)); + + var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: false); + + asyncInfo.DangerousSetError(error); + Debug.Assert(asyncInfo.Status == AsyncStatus.Error); + + return asyncInfo; + } + + + public static IAsyncOperation FromException(Exception error) + { + if (error == null) + throw new ArgumentNullException(nameof(error)); + + var asyncInfo = new TaskToAsyncOperationAdapter(default(TResult)); + + asyncInfo.DangerousSetError(error); + Debug.Assert(asyncInfo.Status == AsyncStatus.Error); + + return asyncInfo; + } + + + public static IAsyncOperationWithProgress FromExceptionWithProgress(Exception error) + { + if (error == null) + throw new ArgumentNullException(nameof(error)); + + var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(default(TResult)); + + asyncInfo.DangerousSetError(error); + Debug.Assert(asyncInfo.Status == AsyncStatus.Error); + + return asyncInfo; + } + + #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error + + #region Factory methods for creating IAsyncInfo instances that have already been canceled synchronously + + public static IAsyncAction CanceledAction() + { + var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncActionWithProgress CanceledActionWithProgress() + { + var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncOperation CanceledOperation() + { + var asyncInfo = new TaskToAsyncOperationAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncOperationWithProgress CanceledOperationWithProgress() + { + var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(isCanceled: true); + return asyncInfo; + } + + #endregion Factory methods for creating IAsyncInfo instances that have already been canceled synchronously + + } // class AsyncInfo +} // namespace diff --git a/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs b/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs index 3f4e184a3..ced43bd39 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace System.Threading.Tasks -{ +namespace System.Threading.Tasks +{ internal interface ITaskAwareAsyncInfo { Task Task { get; } diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs index 7b7373e1f..9626a1404 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs @@ -1,140 +1,140 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace System.Threading.Tasks -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Diagnostics.Contracts; - using System.Runtime.ExceptionServices; - using System.Runtime.InteropServices; - using System.Threading; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.Threading.Tasks +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Runtime.ExceptionServices; + using System.Runtime.InteropServices; + using System.Threading; using global::Windows.Foundation; - /// - /// Implements a wrapper that allows to expose managed System.Threading.Tasks.Task objects as - /// through the WinRT Windows.Foundation.IAsyncInfo interface. - /// -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - internal partial class TaskToAsyncInfoAdapter - : IAsyncInfo, IProgress, ITaskAwareAsyncInfo - where TCompletedHandler : class - where TProgressHandler : class - { - private const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000D); - private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); - private const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018); - private const int E_FAIL = unchecked((int)0x80004005); - - #region Private Types, Statics and Constants - - // ! THIS DIAGRAM ILLUSTRATES THE CONSTANTS BELOW. UPDATE THIS IF UPDATING THE CONSTANTS BELOW!: - // 3 2 1 0 - // 10987654321098765432109876543210 - // X............................... Reserved such that we can use Int32 and not worry about negative-valued state constants - // ..X............................. STATEFLAG_COMPLETED_SYNCHRONOUSLY - // ...X............................ STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET - // ....X........................... STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED - // ................................ STATE_NOT_INITIALIZED - // ...............................X STATE_STARTED - // ..............................X. STATE_RUN_TO_COMPLETION - // .............................X.. STATE_CANCELLATION_REQUESTED - // ............................X... STATE_CANCELLATION_COMPLETED - // ...........................X.... STATE_ERROR - // ..........................X..... STATE_CLOSED - // ..........................XXXXXX STATEMASK_SELECT_ANY_ASYNC_STATE - // XXXXXXXXXXXXXXXXXXXXXXXXXX...... STATEMASK_CLEAR_ALL_ASYNC_STATES - // 3 2 1 0 - // 10987654321098765432109876543210 - - // These STATE_XXXX constants describe the async state of this object. - // Objects of this type are in exactly in one of these states at any given time: - private const int STATE_NOT_INITIALIZED = 0; // 0x00 - private const int STATE_STARTED = 1; // 0x01 - private const int STATE_RUN_TO_COMPLETION = 2; // 0x02 - private const int STATE_CANCELLATION_REQUESTED = 4; // 0x04 - private const int STATE_CANCELLATION_COMPLETED = 8; // 0x08 - private const int STATE_ERROR = 16; // 0x10 - private const int STATE_CLOSED = 32; // 0x20 - - // The STATEFLAG_XXXX constants can be bitmasked with the states to describe additional - // state info that cannot be easily inferred from the async state: - private const int STATEFLAG_COMPLETED_SYNCHRONOUSLY = 0x20000000; - private const int STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET = 0x10000000; - private const int STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED = 0x8000000; - - // These two masks are used to select any STATE_XXXX bits and clear all other (i.e. STATEFLAG_XXXX) bits. - // It is set to (next power of 2 after the largest STATE_XXXX value) - 1. - // !!! Make sure to update this if a new STATE_XXXX value is added above !! - private const int STATEMASK_SELECT_ANY_ASYNC_STATE = (64 - 1); - - // These two masks are used to clear all STATE_XXXX bits and leave any STATEFLAG_XXXX bits. - private const int STATEMASK_CLEAR_ALL_ASYNC_STATES = ~STATEMASK_SELECT_ANY_ASYNC_STATE; - - private static InvalidOperationException CreateCannotGetResultsFromIncompleteOperationException(Exception cause) - { - InvalidOperationException ex = (cause == null) - ? new InvalidOperationException(SR.InvalidOperation_CannotGetResultsFromIncompleteOperation) - : new InvalidOperationException(SR.InvalidOperation_CannotGetResultsFromIncompleteOperation, cause); - ex.SetHResult(E_ILLEGAL_METHOD_CALL); - return ex; - } - - #endregion Private Types, Statics and Constants - - - #region Instance variables - - /// The token source used to cancel running operations. - private CancellationTokenSource _cancelTokenSource = null; - - /// The async info's ID. InvalidAsyncId stands for not yet been initialised. - private uint _id = AsyncInfoIdGenerator.InvalidId; - - /// The cached error code used to avoid creating several exception objects if the ErrorCode - /// property is accessed several times. null indicates either no error or that ErrorCode - /// has not yet been called. - private Exception _error = null; - - /// The state of the async info. Interlocked operations are used to manipulate this field. - private volatile int _state = STATE_NOT_INITIALIZED; - - /// For IAsyncInfo instances that completed synchronously (at creation time) this field holds the result; - /// for instances backed by an actual Task, this field holds a reference to the task generated by the task generator. - /// Since we always know which of the above is the case, we can always cast this field to TResult in the former case - /// or to one of Task or Task{TResult} in the latter case. This approach allows us to save a field on all IAsyncInfos. - /// Notably, this makes us pay the added cost of boxing for synchronously completing IAsyncInfos where TResult is a - /// value type, however, this is expected to occur rather rare compared to non-synchronously completed user-IAsyncInfos. - private object _dataContainer; - - /// Registered completed handler. - private TCompletedHandler _completedHandler; - - /// Registered progress handler. - private TProgressHandler _progressHandler; - - /// The synchronization context on which this instance was created/started. Used to callback invocations. - private SynchronizationContext _startingContext; - - #endregion Instance variables - - - #region Constructors and Destructor - - /// Creates an IAsyncInfo from the specified delegate. The delegate will be called to construct a task that will - /// represent the future encapsulated by this IAsyncInfo. - /// The task generator to use for creating the task. - internal TaskToAsyncInfoAdapter(Delegate taskProvider) - { - Debug.Assert(taskProvider != null); - Debug.Assert((null != (taskProvider as Func)) - || (null != (taskProvider as Func)) - || (null != (taskProvider as Func, Task>)) + /// + /// Implements a wrapper that allows to expose managed System.Threading.Tasks.Task objects as + /// through the WinRT Windows.Foundation.IAsyncInfo interface. + /// +#if NET + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] +#endif + internal partial class TaskToAsyncInfoAdapter + : IAsyncInfo, IProgress, ITaskAwareAsyncInfo + where TCompletedHandler : class + where TProgressHandler : class + { + private const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000D); + private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); + private const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018); + private const int E_FAIL = unchecked((int)0x80004005); + + #region Private Types, Statics and Constants + + // ! THIS DIAGRAM ILLUSTRATES THE CONSTANTS BELOW. UPDATE THIS IF UPDATING THE CONSTANTS BELOW!: + // 3 2 1 0 + // 10987654321098765432109876543210 + // X............................... Reserved such that we can use Int32 and not worry about negative-valued state constants + // ..X............................. STATEFLAG_COMPLETED_SYNCHRONOUSLY + // ...X............................ STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET + // ....X........................... STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED + // ................................ STATE_NOT_INITIALIZED + // ...............................X STATE_STARTED + // ..............................X. STATE_RUN_TO_COMPLETION + // .............................X.. STATE_CANCELLATION_REQUESTED + // ............................X... STATE_CANCELLATION_COMPLETED + // ...........................X.... STATE_ERROR + // ..........................X..... STATE_CLOSED + // ..........................XXXXXX STATEMASK_SELECT_ANY_ASYNC_STATE + // XXXXXXXXXXXXXXXXXXXXXXXXXX...... STATEMASK_CLEAR_ALL_ASYNC_STATES + // 3 2 1 0 + // 10987654321098765432109876543210 + + // These STATE_XXXX constants describe the async state of this object. + // Objects of this type are in exactly in one of these states at any given time: + private const int STATE_NOT_INITIALIZED = 0; // 0x00 + private const int STATE_STARTED = 1; // 0x01 + private const int STATE_RUN_TO_COMPLETION = 2; // 0x02 + private const int STATE_CANCELLATION_REQUESTED = 4; // 0x04 + private const int STATE_CANCELLATION_COMPLETED = 8; // 0x08 + private const int STATE_ERROR = 16; // 0x10 + private const int STATE_CLOSED = 32; // 0x20 + + // The STATEFLAG_XXXX constants can be bitmasked with the states to describe additional + // state info that cannot be easily inferred from the async state: + private const int STATEFLAG_COMPLETED_SYNCHRONOUSLY = 0x20000000; + private const int STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET = 0x10000000; + private const int STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED = 0x8000000; + + // These two masks are used to select any STATE_XXXX bits and clear all other (i.e. STATEFLAG_XXXX) bits. + // It is set to (next power of 2 after the largest STATE_XXXX value) - 1. + // !!! Make sure to update this if a new STATE_XXXX value is added above !! + private const int STATEMASK_SELECT_ANY_ASYNC_STATE = (64 - 1); + + // These two masks are used to clear all STATE_XXXX bits and leave any STATEFLAG_XXXX bits. + private const int STATEMASK_CLEAR_ALL_ASYNC_STATES = ~STATEMASK_SELECT_ANY_ASYNC_STATE; + + private static InvalidOperationException CreateCannotGetResultsFromIncompleteOperationException(Exception cause) + { + InvalidOperationException ex = (cause == null) + ? new InvalidOperationException(SR.InvalidOperation_CannotGetResultsFromIncompleteOperation) + : new InvalidOperationException(SR.InvalidOperation_CannotGetResultsFromIncompleteOperation, cause); + ex.HResult = E_ILLEGAL_METHOD_CALL; + return ex; + } + + #endregion Private Types, Statics and Constants + + + #region Instance variables + + /// The token source used to cancel running operations. + private CancellationTokenSource _cancelTokenSource = null; + + /// The async info's ID. InvalidAsyncId stands for not yet been initialised. + private uint _id = AsyncInfoIdGenerator.InvalidId; + + /// The cached error code used to avoid creating several exception objects if the ErrorCode + /// property is accessed several times. null indicates either no error or that ErrorCode + /// has not yet been called. + private Exception _error = null; + + /// The state of the async info. Interlocked operations are used to manipulate this field. + private volatile int _state = STATE_NOT_INITIALIZED; + + /// For IAsyncInfo instances that completed synchronously (at creation time) this field holds the result; + /// for instances backed by an actual Task, this field holds a reference to the task generated by the task generator. + /// Since we always know which of the above is the case, we can always cast this field to TResult in the former case + /// or to one of Task or Task{TResult} in the latter case. This approach allows us to save a field on all IAsyncInfos. + /// Notably, this makes us pay the added cost of boxing for synchronously completing IAsyncInfos where TResult is a + /// value type, however, this is expected to occur rather rare compared to non-synchronously completed user-IAsyncInfos. + private object _dataContainer; + + /// Registered completed handler. + private TCompletedHandler _completedHandler; + + /// Registered progress handler. + private TProgressHandler _progressHandler; + + /// The synchronization context on which this instance was created/started. Used to callback invocations. + private SynchronizationContext _startingContext; + + #endregion Instance variables + + + #region Constructors and Destructor + + /// Creates an IAsyncInfo from the specified delegate. The delegate will be called to construct a task that will + /// represent the future encapsulated by this IAsyncInfo. + /// The task generator to use for creating the task. + internal TaskToAsyncInfoAdapter(Delegate taskProvider) + { + Debug.Assert(taskProvider != null); + Debug.Assert((null != (taskProvider as Func)) + || (null != (taskProvider as Func)) + || (null != (taskProvider as Func, Task>)) || (null != (taskProvider as Func, Task>))); // The IAsyncInfo is not expected to trigger the Completed and Progress handlers on the same context as the async operation. @@ -142,883 +142,883 @@ internal TaskToAsyncInfoAdapter(Delegate taskProvider) // wants it to based on how it was configured. For instance, in C#, by default the await runs on the same context, but a caller // can use ConfigureAwait to say it doesn't want to run on the same context and that should be respected which it is // by allowing the awaiter to decide the context. - _startingContext = null; - - // Construct task from the specified provider: - Task task = InvokeTaskProvider(taskProvider); - - if (task == null) - throw new NullReferenceException(SR.NullReference_TaskProviderReturnedNull); - - if (task.Status == TaskStatus.Created) - throw new InvalidOperationException(SR.InvalidOperation_TaskProviderReturnedUnstartedTask); - - _dataContainer = task; - _state = (STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED | STATE_STARTED); - - // Set the completion routine and let the task running: - task.ContinueWith( - (_, this_) => ((TaskToAsyncInfoAdapter)this_!).TaskCompleted(), - this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - - /// - /// Creates an IAsyncInfo from the Task object. The specified task represents the future encapsulated by this IAsyncInfo. - /// The specified CancellationTokenSource and Progress are assumed to be the source of the specified Task's cancellation and - /// the Progress that receives reports from the specified Task. - /// - /// The Task whose operation is represented by this IAsyncInfo - /// The cancellation control for the cancellation token observed - /// by underlyingTask. - /// A progress listener/pugblisher that receives progress notifications - /// form underlyingTask. - internal TaskToAsyncInfoAdapter(Task underlyingTask, - CancellationTokenSource underlyingCancelTokenSource, Progress underlyingProgressDispatcher) - { - if (underlyingTask == null) - throw new ArgumentNullException(nameof(underlyingTask)); - - // Throw InvalidOperation and not Argument for parity with the constructor that takes Delegate taskProvider: - if (underlyingTask.Status == TaskStatus.Created) - throw new InvalidOperationException(SR.InvalidOperation_UnstartedTaskSpecified); - + _startingContext = null; + + // Construct task from the specified provider: + Task task = InvokeTaskProvider(taskProvider); + + if (task == null) + throw new NullReferenceException(SR.NullReference_TaskProviderReturnedNull); + + if (task.Status == TaskStatus.Created) + throw new InvalidOperationException(SR.InvalidOperation_TaskProviderReturnedUnstartedTask); + + _dataContainer = task; + _state = (STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED | STATE_STARTED); + + // Set the completion routine and let the task running: + task.ContinueWith( + (_, this_) => ((TaskToAsyncInfoAdapter)this_!).TaskCompleted(), + this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + + + /// + /// Creates an IAsyncInfo from the Task object. The specified task represents the future encapsulated by this IAsyncInfo. + /// The specified CancellationTokenSource and Progress are assumed to be the source of the specified Task's cancellation and + /// the Progress that receives reports from the specified Task. + /// + /// The Task whose operation is represented by this IAsyncInfo + /// The cancellation control for the cancellation token observed + /// by underlyingTask. + /// A progress listener/pugblisher that receives progress notifications + /// form underlyingTask. + internal TaskToAsyncInfoAdapter(Task underlyingTask, + CancellationTokenSource underlyingCancelTokenSource, Progress underlyingProgressDispatcher) + { + if (underlyingTask == null) + throw new ArgumentNullException(nameof(underlyingTask)); + + // Throw InvalidOperation and not Argument for parity with the constructor that takes Delegate taskProvider: + if (underlyingTask.Status == TaskStatus.Created) + throw new InvalidOperationException(SR.InvalidOperation_UnstartedTaskSpecified); + // The IAsyncInfo is not expected to trigger the Completed and Progress handlers on the same context as the async operation. // Instead the awaiter (co_await in C++/WinRT, await in C#) is expected to make sure the handler runs on the context that the caller // wants it to based on how it was configured. For instance, in C#, by default the await runs on the same context, but a caller // can use ConfigureAwait to say it doesn't want to run on the same context and that should be respected which it is // by allowing the awaiter to decide the context. - _startingContext = null; - - // We do not need to invoke any delegates to get the task, it is provided for us: - _dataContainer = underlyingTask; - - // This must be the cancellation source for the token that the specified underlyingTask observes for cancellation: - // (it may also be null in cases where the specified underlyingTask does nto support cancellation) - _cancelTokenSource = underlyingCancelTokenSource; - - // Iff the specified underlyingTask reports progress, chain the reports to this IAsyncInfo's reporting method: - if (underlyingProgressDispatcher != null) - underlyingProgressDispatcher.ProgressChanged += OnReportChainedProgress; - - _state = (STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED | STATE_STARTED); - - underlyingTask.ContinueWith( - (_, this_) => ((TaskToAsyncInfoAdapter)this_!).TaskCompleted(), - this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - - - /// - /// Creates an IAsyncInfo from the specified result value. The IAsyncInfo is created in the Completed state and the - /// specified synchronousResult is used as the result value. - /// - /// The result of this synchronously completed IAsyncInfo. - internal TaskToAsyncInfoAdapter(TResult synchronousResult) - { - // We already completed. There will be no progress callback invokations and a potential completed handler invokation will be synchronous. - // We do not need the starting SynchronizationContext: - _startingContext = null; - - // Set the synchronous result: - _dataContainer = synchronousResult; - - // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + RUN_TO_COMPLETION: - // (same state as assigned by DangerousSetCompleted()) - _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY - | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET - | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED - | STATE_RUN_TO_COMPLETION); - } - - - ~TaskToAsyncInfoAdapter() - { - TransitionToClosed(); - } - - #endregion Constructors and Destructor - - - #region Synchronous completion controls - - /// This method sets the result on a *synchronously completed* IAsyncInfo. - /// It does not try to deal with the inherit races: Use it only when constructing a synchronously - /// completed IAsyncInfo in a desired state when you understand the threading conditions well. - /// The new result of this synchronously completed IAsyncInfo (may be default(TResult)) - /// FALSE if this IAsyncInfo has not actually completed synchronously and this method had no effects, TRUE otherwise. - internal bool DangerousSetCompleted(TResult synchronousResult) - { - if (!CompletedSynchronously) - return false; - - _dataContainer = synchronousResult; - _error = null; - - // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + RUN_TO_COMPLETION: - _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY - | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET - | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED - | STATE_RUN_TO_COMPLETION); - return true; - } - - - internal bool DangerousSetCanceled() - { - if (!CompletedSynchronously) - return false; - - // Here we do not try to deal with the inherit races: Use this method only when constructing a synchronously - // completed IAsyncInfo in a desired state when you understand the threading conditions well. - - _dataContainer = null; - _error = null; - - // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + CANCELLATION_COMPLETED: - _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY - | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET - | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED - | STATE_CANCELLATION_COMPLETED); - return true; - } - - - internal bool DangerousSetError(Exception error) - { - if (!CompletedSynchronously) - return false; - - if (error == null) - throw new ArgumentNullException(nameof(error)); - - // Here we do not try to deal with the inherit races: Use this method only when constructing a synchronously - // completed IAsyncInfo in a desired state when you understand the threading conditions well. - - _dataContainer = null; - _error = error; - - // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + ERROR: - _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY - | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET - | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED - | STATE_ERROR); - return true; - } - - #endregion Synchronous completion controls - - - #region State bit field operations - - internal bool CompletedSynchronously { [Pure] get { return (0 != (_state & STATEFLAG_COMPLETED_SYNCHRONOUSLY)); } } - - private bool IsInStartedState { [Pure] get { return (0 != (_state & STATE_STARTED)); } } - - private bool IsInRunToCompletionState { [Pure] get { return (0 != (_state & STATE_RUN_TO_COMPLETION)); } } - - private bool IsInErrorState { [Pure] get { return (0 != (_state & STATE_ERROR)); } } - - private bool IsInClosedState { [Pure] get { return (0 != (_state & STATE_CLOSED)); } } - - private bool IsInRunningState - { - [Pure] - get - { - return (0 != (_state & (STATE_STARTED - | STATE_CANCELLATION_REQUESTED))); - } - } - - private bool IsInTerminalState - { - [Pure] - get - { - return (0 != (_state & (STATE_RUN_TO_COMPLETION - | STATE_CANCELLATION_COMPLETED - | STATE_ERROR))); - } - } - - [Pure] - private bool CheckUniqueAsyncState(int state) - { - unchecked - { - uint asyncState = (uint)state; - return (asyncState & (~asyncState + 1)) == asyncState; // This checks if asyncState is 0 or a power of 2. - } - } - - #endregion State bit field operations - - - #region Infrastructure methods - - private SynchronizationContext GetStartingContext() - { -#if DESKTOP // as a reminder that on most platforms we want a different behavior - return SynchronizationContext.CurrentNoFlow; -#else - return SynchronizationContext.Current; -#endif - } - - - Task ITaskAwareAsyncInfo.Task - { - get - { - EnsureNotClosed(); - - if (CompletedSynchronously) - return null; - - return (Task)_dataContainer; - } - } - - - internal CancellationTokenSource CancelTokenSource - { - get { return _cancelTokenSource; } - } - - - [Pure] - internal void EnsureNotClosed() - { - if (!IsInClosedState) - return; - - ObjectDisposedException ex = new ObjectDisposedException(SR.ObjectDisposed_AsyncInfoIsClosed); - ex.SetHResult(E_ILLEGAL_METHOD_CALL); - throw ex; - } - - - internal virtual void OnCompleted(TCompletedHandler userCompletionHandler, AsyncStatus asyncStatus) - { - Debug.Fail("This (sub-)type of IAsyncInfo does not support completion notifications " - + " (" + this.GetType().ToString() + ")"); - } - - - internal virtual void OnProgress(TProgressHandler userProgressHandler, TProgressInfo progressInfo) - { - Debug.Fail("This (sub-)type of IAsyncInfo does not support progress notifications " - + " (" + this.GetType().ToString() + ")"); - } - - - private void OnCompletedInvoker(AsyncStatus status) - { - bool conditionFailed; - - // Get the handler: - TCompletedHandler handler = Volatile.Read(ref _completedHandler); - - // If we might not run the handler now, we need to remember that if it is set later, it will need to be run then: - if (handler == null) - { - // Remember to run the handler when it is set: - SetState(STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET, ~STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET, - conditionBitMask: 0, useCondition: false, conditionFailed: out conditionFailed); - - // The handler may have been set concurrently before we managed to SetState, so check for it again: - handler = Volatile.Read(ref _completedHandler); - - // If handler was not set cuncurrently after all, then no worries: - if (handler == null) - return; - } - - // This method might be running cuncurrently. Create a block by emulating an interlocked un-set of - // the STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED-bit in the m_state bit field. Only the thread that wins the race - // for unsetting this bit, wins, others give up: - SetState(0, ~STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED, - conditionBitMask: STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED, useCondition: true, conditionFailed: out conditionFailed); - - if (conditionFailed) - return; - - // Invoke the user handler: - OnCompleted(handler, status); - } - - - // This is a separate method from IProgress.Report to avoid alocating the closure if it is not used. - private void OnProgressInvokerCrossContext(TProgressHandler handler, TProgressInfo progressInfo) - { - Debug.Assert(handler != null); - Debug.Assert(_startingContext != null); - - _startingContext.Post((tupleObject) => - { - var tuple = (Tuple, - TProgressHandler, - TProgressInfo>)tupleObject!; - - tuple.Item1.OnProgress(tuple.Item2, tuple.Item3); - }, Tuple.Create(this, handler, progressInfo)); - } - - - /// Reports a progress update. - /// The new progress value to report. - void IProgress.Report(TProgressInfo value) - { - // If no progress handler is set, there is nothing to do: - TProgressHandler handler = Volatile.Read(ref _progressHandler); - if (handler == null) - return; - - // Try calling progress handler in the right synchronization context. - // If the user callback throws an exception, it will bubble up through here and reach the - // user worker code running as this async future. The user should catch it. - // If the user does not catch it, it will be treated just as any other exception coming from the async execution code: - // this AsyncInfo will be faulted. - - if (_startingContext == null) - { - // The starting context is null, invoke directly: - OnProgress(handler, value); - } - else - { - // Invoke callback in the right context: - OnProgressInvokerCrossContext(handler, value); - } - } - - - private void OnReportChainedProgress(object sender, TProgressInfo progressInfo) - { - ((IProgress)this).Report(progressInfo); - } - - - /// - /// Sets the m_state bit field to reflect the specified async state with the corresponding STATE_XXX bit mask. - /// - /// Must be one of the STATE_XXX (not STATEYYY_ZZZ !) constants defined in this class. - /// If useCondition is FALSE: this field is ignored. - /// If useCondition is TRUE: Unless this value has at least one bit with m_state in - /// common, this method will not perform any action. - /// If TRUE, use conditionBitMask to determine whether the state should be set; - /// If FALSE, ignore conditionBitMask. - /// If useCondition is FALSE: this field is set to FALSE; - /// If useCondition is TRUE: this field indicated whether the specified conditionBitMask - /// had at least one bit in common with m_state (TRUE) - /// or not (FALSE). - /// (!) Note that the meaning of this parameter to the caller is not quite the same as whether m_state - /// is/was set to the specified value, because m_state may already have had the specified value, or it - /// may be set and then immediately changed by another thread. The true meaning of this parameter is whether or not - /// the specified condition did hold before trying to change the state. - /// The value at which the current invocation of this method left m_state. - private int SetAsyncState(int newAsyncState, int conditionBitMask, bool useCondition, out bool conditionFailed) - { - Debug.Assert(CheckUniqueAsyncState(newAsyncState & STATEMASK_SELECT_ANY_ASYNC_STATE)); - Debug.Assert(CheckUniqueAsyncState(_state & STATEMASK_SELECT_ANY_ASYNC_STATE)); - - int resultState = SetState(newAsyncState, STATEMASK_CLEAR_ALL_ASYNC_STATES, conditionBitMask, useCondition, out conditionFailed); - Debug.Assert(CheckUniqueAsyncState(resultState & STATEMASK_SELECT_ANY_ASYNC_STATE)); - - return resultState; - } - - - /// - /// Sets the specified bits in the m_state bit field according to the specified bit-mask parameters. - /// - /// The bits to turn ON in the m_state bit field - /// Any bits that are OFF in this value will get turned OFF, - /// unless they are explicitly switched on by newStateSetMask. - /// If useCondition is FALSE: this field is ignored. - /// If useCondition is TRUE: Unless this value has at least one bit with m_state in - /// common, this method will not perform any action. - /// If TRUE, use conditionBitMask to determine whether the state should be set; - /// If FALSE, ignore conditionBitMask. - /// If useCondition is FALSE: this field is set to FALSE; - /// If useCondition is TRUE: this field indicated whether the specified conditionBitMask - /// had at least one bit in common with m_state (TRUE) - /// or not (FALSE). - /// (!) Note that the meaning of this parameter to the caller is not quite the same as whether m_state - /// is/was set to the specified value, because m_state may already have had the specified value, or it - /// may be set and then immediately changed by another thread. The true meaning of this parameter is whether or not - /// the specified condition did hold before trying to change the state. - /// The value at which the current invocation of this method left m_state. - private int SetState(int newStateSetMask, int newStateIgnoreMask, int conditionBitMask, bool useCondition, out bool conditionFailed) - { - int origState = _state; - - if (useCondition && 0 == (origState & conditionBitMask)) - { - conditionFailed = true; - return origState; - } - - int newState = (origState & newStateIgnoreMask) | newStateSetMask; - int prevState = Interlocked.CompareExchange(ref _state, newState, origState); - - // If m_state changed concurrently, we want to make sure that the change being made is based on a bitmask that is up to date: - // (this relies of the fact that all state machines that save their state in m_state have no cycles) - while (true) - { - if (prevState == origState) - { - conditionFailed = false; - return newState; - } - - origState = _state; - - if (useCondition && 0 == (origState & conditionBitMask)) - { - conditionFailed = true; - return origState; - } - - newState = (origState & newStateIgnoreMask) | newStateSetMask; - prevState = Interlocked.CompareExchange(ref _state, newState, origState); - } - } - - - private int TransitionToTerminalState() - { - Debug.Assert(IsInRunningState); - Debug.Assert(!CompletedSynchronously); - - Task task = _dataContainer as Task; - Debug.Assert(task != null); - Debug.Assert(task.IsCompleted); - - // Recall that STATE_CANCELLATION_REQUESTED and STATE_CANCELLATION_COMPLETED both map to the public CANCELED state. - // So, we are STARTED or CANCELED. We will ask the task how it completed and possibly transition out of CANCELED. - // This may happen if cancellation was requested while in STARTED state, but the task does not support cancellation, - // or if it can support cancellation in principle, but the Cancel request came in while still STARTED, but after the - // last opportunity to cancel. - // If the underlying operation was not able to react to the cancellation request and instead either run to completion - // or faulted, then the state will transition into COMPLETED or ERROR accordingly. If the operation was really cancelled, - // the state will remain CANCELED. - - // If the switch below defaults, we have an erroneous implementation. - int terminalAsyncState = STATE_ERROR; - - switch (task.Status) - { - case TaskStatus.RanToCompletion: - terminalAsyncState = STATE_RUN_TO_COMPLETION; - break; - - case TaskStatus.Canceled: - terminalAsyncState = STATE_CANCELLATION_COMPLETED; - break; - - case TaskStatus.Faulted: - terminalAsyncState = STATE_ERROR; - break; - - default: - Debug.Fail("Unexpected task.Status: It should be terminal if TaskCompleted() is called."); - break; - } - - bool ignore; - int newState = SetAsyncState(terminalAsyncState, - conditionBitMask: STATEMASK_SELECT_ANY_ASYNC_STATE, useCondition: true, conditionFailed: out ignore); - - Debug.Assert((newState & STATEMASK_SELECT_ANY_ASYNC_STATE) == terminalAsyncState); - Debug.Assert((_state & STATEMASK_SELECT_ANY_ASYNC_STATE) == terminalAsyncState || IsInClosedState, - "We must either be in a state we just entered or we were concurrently closed"); - - return newState; - } - - - private void TaskCompleted() - { - int terminalState = TransitionToTerminalState(); - Debug.Assert(IsInTerminalState); - - // We transitioned into a terminal state, so it became legal to close us concurrently. - // So we use data from this stack and not m_state to get the completion status. - // On this code path we will also fetch m_completedHandler, however that race is benign because in CLOSED the handler - // can only change to null, so it won't be invoked, which is appropriate for CLOSED. - AsyncStatus terminationStatus = GetStatus(terminalState); - - // Try calling completed handler in the right synchronization context. - // If the user callback throws an exception, it will bubble up through here. - // If we let it though, it will be caught and swallowed by the Task subsystem, which is just below us on the stack. - // Instead we follow the same pattern as Task and other parallel libs and re-throw the excpetion on the threadpool - // to ensure a diagnostic message and a fail-fast-like teardown. - try - { - if (_startingContext == null) - { - // The starting context is null, invoking directly: - OnCompletedInvoker(terminationStatus); - } - else - { - // Invoke callback in the right context (delegate cached by compiler): - _startingContext.Post((tupleObject) => - { - var tuple = (Tuple, AsyncStatus>)tupleObject!; - tuple.Item1.OnCompletedInvoker(tuple.Item2); - }, Tuple.Create(this, terminationStatus)); - } - } - catch (Exception ex) - { - ExceptionDispatchHelper.ThrowAsync(ex, _startingContext); - } - } - - - private AsyncStatus GetStatus(int state) - { - int asyncState = state & STATEMASK_SELECT_ANY_ASYNC_STATE; - Debug.Assert(CheckUniqueAsyncState(asyncState)); - - switch (asyncState) - { - case STATE_NOT_INITIALIZED: - Debug.Fail("STATE_NOT_INITIALIZED should only occur when this object was not" - + " fully constructed, in which case we should never get here"); - return AsyncStatus.Error; - - case STATE_STARTED: - return AsyncStatus.Started; - - case STATE_RUN_TO_COMPLETION: - return AsyncStatus.Completed; - - case STATE_CANCELLATION_REQUESTED: - case STATE_CANCELLATION_COMPLETED: - return AsyncStatus.Canceled; - - case STATE_ERROR: - return AsyncStatus.Error; - - case STATE_CLOSED: - Debug.Fail("This method should never be called is this IAsyncInfo is CLOSED"); - return AsyncStatus.Error; - } - - Debug.Fail("The switch above is missing a case"); - return AsyncStatus.Error; - } - - internal TResult GetResultsInternal() - { - EnsureNotClosed(); - - // If this IAsyncInfo has actually faulted, GetResults will throw the same error as returned by ErrorCode: - if (IsInErrorState) - { - Exception error = ErrorCode; - Debug.Assert(error != null); - ExceptionDispatchInfo.Capture(error).Throw(); - } - - // IAsyncInfo throws E_ILLEGAL_METHOD_CALL when called in a state other than COMPLETED: - if (!IsInRunToCompletionState) - throw CreateCannotGetResultsFromIncompleteOperationException(null); - - - // If this is a synchronous operation, use the cached result: - if (CompletedSynchronously) - return (TResult)_dataContainer!; - - // The operation is asynchronous: - Task task = _dataContainer as Task; - - // Since CompletedSynchronously is false and EnsureNotClosed() did not throw, task can only be null if: - // - this IAsyncInfo has completed synchronously, however we checked for this above; - // - it was not converted to Task, which means it is a non-generic Task. In that case we cannot get a result from Task. - if (task == null) - return default(TResult)!; - - Debug.Assert(IsInRunToCompletionState); - - // Pull out the task result and return. - // Any exceptions thrown in the task will be rethrown. - // If this exception is a cancelation exception, meaning there was actually no error except for being cancelled, - // return an error code appropriate for WinRT instead (InvalidOperation with E_ILLEGAL_METHOD_CALL). - try - { - return task.GetAwaiter().GetResult(); - } - catch (TaskCanceledException tcEx) - { - throw CreateCannotGetResultsFromIncompleteOperationException(tcEx); - } - } - - - private Task InvokeTaskProvider(Delegate taskProvider) - { - var funcVoidTask = taskProvider as Func; - if (funcVoidTask != null) - { - return funcVoidTask(); - } - - var funcCTokTask = taskProvider as Func; - if (funcCTokTask != null) - { - _cancelTokenSource = new CancellationTokenSource(); - return funcCTokTask(_cancelTokenSource.Token); - } - - var funcIPrgrTask = taskProvider as Func, Task>; - if (funcIPrgrTask != null) - { - return funcIPrgrTask(this); - } - - var funcCTokIPrgrTask = taskProvider as Func, Task>; - if (funcCTokIPrgrTask != null) - { - _cancelTokenSource = new CancellationTokenSource(); - return funcCTokIPrgrTask(_cancelTokenSource.Token, this); - } - - Debug.Fail("We should never get here!" - + " Public methods creating instances of this class must be typesafe to ensure that taskProvider" - + " can always be cast to one of the above Func types." - + " The taskProvider is " + (taskProvider == null - ? "null." - : "a " + taskProvider.GetType().ToString()) + "."); - return null; - } - - - private void TransitionToClosed() - { - // From the finaliser we always call this Close version since finalisation can happen any time, even when STARTED (e.g. process ends) - // and we do not want to throw in those cases. - - // Always go to closed, even from STATE_NOT_INITIALIZED. - // Any checking whether it is legal to call CLosed inthe current state, should occur in Close(). - bool ignore; - SetAsyncState(STATE_CLOSED, 0, useCondition: false, conditionFailed: out ignore); - - _cancelTokenSource = null; - _dataContainer = null; - _error = null; - _completedHandler = null; - _progressHandler = null; - _startingContext = null; - } - - #endregion Infrastructure methods - - - #region Implementation of IAsyncInfo - - /// - /// Gets or sets the completed handler. - /// - /// We will set the completion handler even when this IAsyncInfo is already started (no other choice). - /// If we the completion handler is set BEFORE this IAsyncInfo completed, then the handler will be called upon completion as normal. - /// If we the completion handler is set AFTER this IAsyncInfo already completed, then this setter will invoke the handler synchronously - /// on the current context. - /// - public virtual TCompletedHandler Completed - { - get - { - TCompletedHandler handler = Volatile.Read(ref _completedHandler); - EnsureNotClosed(); - return handler; - } - - set - { - EnsureNotClosed(); - - // Try setting completion handler, but only if this has not yet been done: - // (Note: We allow setting Completed to null multiple times iff it has not yet been set to anything else than null. - // Some other WinRT projection languages do not allow setting the Completed handler more than once, even if it is set to null. - // We could do the same by introducing a new STATEFLAG_COMPLETION_HNDL_SET bit-flag constant and saving a this state into - // the m_state field to indicate that the completion handler has been set previously, but we choose not to do this.) - TCompletedHandler handlerBefore = Interlocked.CompareExchange(ref _completedHandler, value, null); - if (handlerBefore != null) - { - InvalidOperationException ex = new InvalidOperationException(SR.InvalidOperation_CannotSetCompletionHanlderMoreThanOnce); - ex.SetHResult(E_ILLEGAL_DELEGATE_ASSIGNMENT); - throw ex; - } - - if (value == null) - return; - - // If STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET is OFF then we are done (i.e. no need to invoke the handler synchronously) - if (0 == (_state & STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET)) - return; - - // We have changed the handler and at some point this IAsyncInfo may have transitioned to the Closed state. - // This is OK, but if this happened we need to ensure that we only leave a null handler behind: - if (IsInClosedState) - { - Interlocked.Exchange(ref _completedHandler, null); - return; - } - - // The STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET-flag was set, so we need to call the completion handler now: - Debug.Assert(IsInTerminalState); - OnCompletedInvoker(Status); - } - } - - - /// Gets or sets the progress handler. - public virtual TProgressHandler Progress - { - get - { - TProgressHandler handler = Volatile.Read(ref _progressHandler); - EnsureNotClosed(); - - return handler; - } - - set - { - EnsureNotClosed(); - - Interlocked.Exchange(ref _progressHandler, value); - - // We transitioned into CLOSED after the above check, we will need to null out m_progressHandler: - if (IsInClosedState) - Interlocked.Exchange(ref _progressHandler, null); - } - } - - - /// Cancels the async info. - public virtual void Cancel() - { - // Cancel will be ignored in any terminal state including CLOSED. - // In other words, it is ignored in any state except STARTED. - - bool stateWasNotStarted; - SetAsyncState(STATE_CANCELLATION_REQUESTED, conditionBitMask: STATE_STARTED, useCondition: true, conditionFailed: out stateWasNotStarted); - - if (!stateWasNotStarted) - { // i.e. if state was different from STATE_STARTED: - if (_cancelTokenSource != null) - _cancelTokenSource.Cancel(); - } - } - - - /// Close the async info. - public virtual void Close() - { - if (IsInClosedState) - return; - - // Cannot Close from a non-terminal state: - if (!IsInTerminalState) - { - // If we are STATE_NOT_INITIALIZED, the we probably threw from the ctor. - // The finalizer will be called anyway and we need to free this partially constructed object correctly. - // So we avoid throwing when we are in STATE_NOT_INITIALIZED. - // In other words throw only if *some* async state is set: - if (0 != (_state & STATEMASK_SELECT_ANY_ASYNC_STATE)) - { - InvalidOperationException ex = new InvalidOperationException(SR.InvalidOperation_IllegalStateChange); - ex.SetHResult(E_ILLEGAL_STATE_CHANGE); - throw ex; - } - } - - TransitionToClosed(); - } - - - /// Gets the error code for the async info. - public virtual Exception ErrorCode - { - get - { - EnsureNotClosed(); - - // If the task is faulted, hand back an HR representing its first exception. - // Otherwise, hand back S_OK (which is a null Exception). - - if (!IsInErrorState) - return null; - - Exception error = Volatile.Read(ref _error); - - // ERROR is a terminal state. SO if we have an error, just return it. - // If we completed synchronously, we return the current error iven if it is null since we do not expect this to change: - if (error != null || CompletedSynchronously) - return error; - - Task task = _dataContainer as Task; - Debug.Assert(task != null); - - AggregateException aggregateException = task.Exception; - - // By spec, if task.IsFaulted is true, then task.Exception must not be null and its InnerException must - // also not be null. However, in case something is unexpected on the Task side of the road, lets be defensive - // instead of failing with an inexplicable NullReferenceException: - - if (aggregateException == null) - { - error = new Exception(SR.WinRtCOM_Error); - error.SetHResult(E_FAIL); - } - else - { - Exception innerException = aggregateException.InnerException; - - error = (innerException == null) - ? aggregateException - : innerException; - } - - // If m_error was set concurrently, setError will be non-null. Then we use that - as it is the first m_error - // that was set. If setError we know that we won any races and we can return error: - Exception setError = Interlocked.CompareExchange(ref _error, error, null); - return setError ?? error; - } - } - - - public virtual uint Id - { - get - { - EnsureNotClosed(); - - if (_id != AsyncInfoIdGenerator.InvalidId) - return _id; - - return AsyncInfoIdGenerator.EnsureInitializedThreadsafe(ref _id); - } - } - - - /// Gets the status of the async info. - public virtual AsyncStatus Status - { - get - { - EnsureNotClosed(); - return GetStatus(_state); - } - } - #endregion Implementation of IAsyncInfo - } // class TaskToAsyncInfoAdapter -} // namespace - -// TaskToAsyncInfoAdapter.cs + _startingContext = null; + + // We do not need to invoke any delegates to get the task, it is provided for us: + _dataContainer = underlyingTask; + + // This must be the cancellation source for the token that the specified underlyingTask observes for cancellation: + // (it may also be null in cases where the specified underlyingTask does nto support cancellation) + _cancelTokenSource = underlyingCancelTokenSource; + + // Iff the specified underlyingTask reports progress, chain the reports to this IAsyncInfo's reporting method: + if (underlyingProgressDispatcher != null) + underlyingProgressDispatcher.ProgressChanged += OnReportChainedProgress; + + _state = (STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED | STATE_STARTED); + + underlyingTask.ContinueWith( + (_, this_) => ((TaskToAsyncInfoAdapter)this_!).TaskCompleted(), + this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + + + /// + /// Creates an IAsyncInfo from the specified result value. The IAsyncInfo is created in the Completed state and the + /// specified synchronousResult is used as the result value. + /// + /// The result of this synchronously completed IAsyncInfo. + internal TaskToAsyncInfoAdapter(TResult synchronousResult) + { + // We already completed. There will be no progress callback invokations and a potential completed handler invokation will be synchronous. + // We do not need the starting SynchronizationContext: + _startingContext = null; + + // Set the synchronous result: + _dataContainer = synchronousResult; + + // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + RUN_TO_COMPLETION: + // (same state as assigned by DangerousSetCompleted()) + _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY + | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET + | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED + | STATE_RUN_TO_COMPLETION); + } + + + ~TaskToAsyncInfoAdapter() + { + TransitionToClosed(); + } + + #endregion Constructors and Destructor + + + #region Synchronous completion controls + + /// This method sets the result on a *synchronously completed* IAsyncInfo. + /// It does not try to deal with the inherit races: Use it only when constructing a synchronously + /// completed IAsyncInfo in a desired state when you understand the threading conditions well. + /// The new result of this synchronously completed IAsyncInfo (may be default(TResult)) + /// FALSE if this IAsyncInfo has not actually completed synchronously and this method had no effects, TRUE otherwise. + internal bool DangerousSetCompleted(TResult synchronousResult) + { + if (!CompletedSynchronously) + return false; + + _dataContainer = synchronousResult; + _error = null; + + // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + RUN_TO_COMPLETION: + _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY + | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET + | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED + | STATE_RUN_TO_COMPLETION); + return true; + } + + + internal bool DangerousSetCanceled() + { + if (!CompletedSynchronously) + return false; + + // Here we do not try to deal with the inherit races: Use this method only when constructing a synchronously + // completed IAsyncInfo in a desired state when you understand the threading conditions well. + + _dataContainer = null; + _error = null; + + // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + CANCELLATION_COMPLETED: + _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY + | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET + | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED + | STATE_CANCELLATION_COMPLETED); + return true; + } + + + internal bool DangerousSetError(Exception error) + { + if (!CompletedSynchronously) + return false; + + if (error == null) + throw new ArgumentNullException(nameof(error)); + + // Here we do not try to deal with the inherit races: Use this method only when constructing a synchronously + // completed IAsyncInfo in a desired state when you understand the threading conditions well. + + _dataContainer = null; + _error = error; + + // CompletedSynchronously + MustRunCompletionHandleImmediatelyWhenSet + CompletionHandlerNotYetInvoked + ERROR: + _state = (STATEFLAG_COMPLETED_SYNCHRONOUSLY + | STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET + | STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED + | STATE_ERROR); + return true; + } + + #endregion Synchronous completion controls + + + #region State bit field operations + + internal bool CompletedSynchronously { [Pure] get { return (0 != (_state & STATEFLAG_COMPLETED_SYNCHRONOUSLY)); } } + + private bool IsInStartedState { [Pure] get { return (0 != (_state & STATE_STARTED)); } } + + private bool IsInRunToCompletionState { [Pure] get { return (0 != (_state & STATE_RUN_TO_COMPLETION)); } } + + private bool IsInErrorState { [Pure] get { return (0 != (_state & STATE_ERROR)); } } + + private bool IsInClosedState { [Pure] get { return (0 != (_state & STATE_CLOSED)); } } + + private bool IsInRunningState + { + [Pure] + get + { + return (0 != (_state & (STATE_STARTED + | STATE_CANCELLATION_REQUESTED))); + } + } + + private bool IsInTerminalState + { + [Pure] + get + { + return (0 != (_state & (STATE_RUN_TO_COMPLETION + | STATE_CANCELLATION_COMPLETED + | STATE_ERROR))); + } + } + + [Pure] + private bool CheckUniqueAsyncState(int state) + { + unchecked + { + uint asyncState = (uint)state; + return (asyncState & (~asyncState + 1)) == asyncState; // This checks if asyncState is 0 or a power of 2. + } + } + + #endregion State bit field operations + + + #region Infrastructure methods + + private SynchronizationContext GetStartingContext() + { +#if DESKTOP // as a reminder that on most platforms we want a different behavior + return SynchronizationContext.CurrentNoFlow; +#else + return SynchronizationContext.Current; +#endif + } + + + Task ITaskAwareAsyncInfo.Task + { + get + { + EnsureNotClosed(); + + if (CompletedSynchronously) + return null; + + return (Task)_dataContainer; + } + } + + + internal CancellationTokenSource CancelTokenSource + { + get { return _cancelTokenSource; } + } + + + [Pure] + internal void EnsureNotClosed() + { + if (!IsInClosedState) + return; + + ObjectDisposedException ex = new ObjectDisposedException(SR.ObjectDisposed_AsyncInfoIsClosed); + ex.HResult = E_ILLEGAL_METHOD_CALL; + throw ex; + } + + + internal virtual void OnCompleted(TCompletedHandler userCompletionHandler, AsyncStatus asyncStatus) + { + Debug.Fail("This (sub-)type of IAsyncInfo does not support completion notifications " + + " (" + this.GetType().ToString() + ")"); + } + + + internal virtual void OnProgress(TProgressHandler userProgressHandler, TProgressInfo progressInfo) + { + Debug.Fail("This (sub-)type of IAsyncInfo does not support progress notifications " + + " (" + this.GetType().ToString() + ")"); + } + + + private void OnCompletedInvoker(AsyncStatus status) + { + bool conditionFailed; + + // Get the handler: + TCompletedHandler handler = Volatile.Read(ref _completedHandler); + + // If we might not run the handler now, we need to remember that if it is set later, it will need to be run then: + if (handler == null) + { + // Remember to run the handler when it is set: + SetState(STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET, ~STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET, + conditionBitMask: 0, useCondition: false, conditionFailed: out conditionFailed); + + // The handler may have been set concurrently before we managed to SetState, so check for it again: + handler = Volatile.Read(ref _completedHandler); + + // If handler was not set cuncurrently after all, then no worries: + if (handler == null) + return; + } + + // This method might be running cuncurrently. Create a block by emulating an interlocked un-set of + // the STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED-bit in the m_state bit field. Only the thread that wins the race + // for unsetting this bit, wins, others give up: + SetState(0, ~STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED, + conditionBitMask: STATEFLAG_COMPLETION_HNDL_NOT_YET_INVOKED, useCondition: true, conditionFailed: out conditionFailed); + + if (conditionFailed) + return; + + // Invoke the user handler: + OnCompleted(handler, status); + } + + + // This is a separate method from IProgress.Report to avoid alocating the closure if it is not used. + private void OnProgressInvokerCrossContext(TProgressHandler handler, TProgressInfo progressInfo) + { + Debug.Assert(handler != null); + Debug.Assert(_startingContext != null); + + _startingContext.Post((tupleObject) => + { + var tuple = (Tuple, + TProgressHandler, + TProgressInfo>)tupleObject!; + + tuple.Item1.OnProgress(tuple.Item2, tuple.Item3); + }, Tuple.Create(this, handler, progressInfo)); + } + + + /// Reports a progress update. + /// The new progress value to report. + void IProgress.Report(TProgressInfo value) + { + // If no progress handler is set, there is nothing to do: + TProgressHandler handler = Volatile.Read(ref _progressHandler); + if (handler == null) + return; + + // Try calling progress handler in the right synchronization context. + // If the user callback throws an exception, it will bubble up through here and reach the + // user worker code running as this async future. The user should catch it. + // If the user does not catch it, it will be treated just as any other exception coming from the async execution code: + // this AsyncInfo will be faulted. + + if (_startingContext == null) + { + // The starting context is null, invoke directly: + OnProgress(handler, value); + } + else + { + // Invoke callback in the right context: + OnProgressInvokerCrossContext(handler, value); + } + } + + + private void OnReportChainedProgress(object sender, TProgressInfo progressInfo) + { + ((IProgress)this).Report(progressInfo); + } + + + /// + /// Sets the m_state bit field to reflect the specified async state with the corresponding STATE_XXX bit mask. + /// + /// Must be one of the STATE_XXX (not STATEYYY_ZZZ !) constants defined in this class. + /// If useCondition is FALSE: this field is ignored. + /// If useCondition is TRUE: Unless this value has at least one bit with m_state in + /// common, this method will not perform any action. + /// If TRUE, use conditionBitMask to determine whether the state should be set; + /// If FALSE, ignore conditionBitMask. + /// If useCondition is FALSE: this field is set to FALSE; + /// If useCondition is TRUE: this field indicated whether the specified conditionBitMask + /// had at least one bit in common with m_state (TRUE) + /// or not (FALSE). + /// (!) Note that the meaning of this parameter to the caller is not quite the same as whether m_state + /// is/was set to the specified value, because m_state may already have had the specified value, or it + /// may be set and then immediately changed by another thread. The true meaning of this parameter is whether or not + /// the specified condition did hold before trying to change the state. + /// The value at which the current invocation of this method left m_state. + private int SetAsyncState(int newAsyncState, int conditionBitMask, bool useCondition, out bool conditionFailed) + { + Debug.Assert(CheckUniqueAsyncState(newAsyncState & STATEMASK_SELECT_ANY_ASYNC_STATE)); + Debug.Assert(CheckUniqueAsyncState(_state & STATEMASK_SELECT_ANY_ASYNC_STATE)); + + int resultState = SetState(newAsyncState, STATEMASK_CLEAR_ALL_ASYNC_STATES, conditionBitMask, useCondition, out conditionFailed); + Debug.Assert(CheckUniqueAsyncState(resultState & STATEMASK_SELECT_ANY_ASYNC_STATE)); + + return resultState; + } + + + /// + /// Sets the specified bits in the m_state bit field according to the specified bit-mask parameters. + /// + /// The bits to turn ON in the m_state bit field + /// Any bits that are OFF in this value will get turned OFF, + /// unless they are explicitly switched on by newStateSetMask. + /// If useCondition is FALSE: this field is ignored. + /// If useCondition is TRUE: Unless this value has at least one bit with m_state in + /// common, this method will not perform any action. + /// If TRUE, use conditionBitMask to determine whether the state should be set; + /// If FALSE, ignore conditionBitMask. + /// If useCondition is FALSE: this field is set to FALSE; + /// If useCondition is TRUE: this field indicated whether the specified conditionBitMask + /// had at least one bit in common with m_state (TRUE) + /// or not (FALSE). + /// (!) Note that the meaning of this parameter to the caller is not quite the same as whether m_state + /// is/was set to the specified value, because m_state may already have had the specified value, or it + /// may be set and then immediately changed by another thread. The true meaning of this parameter is whether or not + /// the specified condition did hold before trying to change the state. + /// The value at which the current invocation of this method left m_state. + private int SetState(int newStateSetMask, int newStateIgnoreMask, int conditionBitMask, bool useCondition, out bool conditionFailed) + { + int origState = _state; + + if (useCondition && 0 == (origState & conditionBitMask)) + { + conditionFailed = true; + return origState; + } + + int newState = (origState & newStateIgnoreMask) | newStateSetMask; + int prevState = Interlocked.CompareExchange(ref _state, newState, origState); + + // If m_state changed concurrently, we want to make sure that the change being made is based on a bitmask that is up to date: + // (this relies of the fact that all state machines that save their state in m_state have no cycles) + while (true) + { + if (prevState == origState) + { + conditionFailed = false; + return newState; + } + + origState = _state; + + if (useCondition && 0 == (origState & conditionBitMask)) + { + conditionFailed = true; + return origState; + } + + newState = (origState & newStateIgnoreMask) | newStateSetMask; + prevState = Interlocked.CompareExchange(ref _state, newState, origState); + } + } + + + private int TransitionToTerminalState() + { + Debug.Assert(IsInRunningState); + Debug.Assert(!CompletedSynchronously); + + Task task = _dataContainer as Task; + Debug.Assert(task != null); + Debug.Assert(task.IsCompleted); + + // Recall that STATE_CANCELLATION_REQUESTED and STATE_CANCELLATION_COMPLETED both map to the public CANCELED state. + // So, we are STARTED or CANCELED. We will ask the task how it completed and possibly transition out of CANCELED. + // This may happen if cancellation was requested while in STARTED state, but the task does not support cancellation, + // or if it can support cancellation in principle, but the Cancel request came in while still STARTED, but after the + // last opportunity to cancel. + // If the underlying operation was not able to react to the cancellation request and instead either run to completion + // or faulted, then the state will transition into COMPLETED or ERROR accordingly. If the operation was really cancelled, + // the state will remain CANCELED. + + // If the switch below defaults, we have an erroneous implementation. + int terminalAsyncState = STATE_ERROR; + + switch (task.Status) + { + case TaskStatus.RanToCompletion: + terminalAsyncState = STATE_RUN_TO_COMPLETION; + break; + + case TaskStatus.Canceled: + terminalAsyncState = STATE_CANCELLATION_COMPLETED; + break; + + case TaskStatus.Faulted: + terminalAsyncState = STATE_ERROR; + break; + + default: + Debug.Fail("Unexpected task.Status: It should be terminal if TaskCompleted() is called."); + break; + } + + bool ignore; + int newState = SetAsyncState(terminalAsyncState, + conditionBitMask: STATEMASK_SELECT_ANY_ASYNC_STATE, useCondition: true, conditionFailed: out ignore); + + Debug.Assert((newState & STATEMASK_SELECT_ANY_ASYNC_STATE) == terminalAsyncState); + Debug.Assert((_state & STATEMASK_SELECT_ANY_ASYNC_STATE) == terminalAsyncState || IsInClosedState, + "We must either be in a state we just entered or we were concurrently closed"); + + return newState; + } + + + private void TaskCompleted() + { + int terminalState = TransitionToTerminalState(); + Debug.Assert(IsInTerminalState); + + // We transitioned into a terminal state, so it became legal to close us concurrently. + // So we use data from this stack and not m_state to get the completion status. + // On this code path we will also fetch m_completedHandler, however that race is benign because in CLOSED the handler + // can only change to null, so it won't be invoked, which is appropriate for CLOSED. + AsyncStatus terminationStatus = GetStatus(terminalState); + + // Try calling completed handler in the right synchronization context. + // If the user callback throws an exception, it will bubble up through here. + // If we let it though, it will be caught and swallowed by the Task subsystem, which is just below us on the stack. + // Instead we follow the same pattern as Task and other parallel libs and re-throw the excpetion on the threadpool + // to ensure a diagnostic message and a fail-fast-like teardown. + try + { + if (_startingContext == null) + { + // The starting context is null, invoking directly: + OnCompletedInvoker(terminationStatus); + } + else + { + // Invoke callback in the right context (delegate cached by compiler): + _startingContext.Post((tupleObject) => + { + var tuple = (Tuple, AsyncStatus>)tupleObject!; + tuple.Item1.OnCompletedInvoker(tuple.Item2); + }, Tuple.Create(this, terminationStatus)); + } + } + catch (Exception ex) + { + ExceptionDispatchHelper.ThrowAsync(ex, _startingContext); + } + } + + + private AsyncStatus GetStatus(int state) + { + int asyncState = state & STATEMASK_SELECT_ANY_ASYNC_STATE; + Debug.Assert(CheckUniqueAsyncState(asyncState)); + + switch (asyncState) + { + case STATE_NOT_INITIALIZED: + Debug.Fail("STATE_NOT_INITIALIZED should only occur when this object was not" + + " fully constructed, in which case we should never get here"); + return AsyncStatus.Error; + + case STATE_STARTED: + return AsyncStatus.Started; + + case STATE_RUN_TO_COMPLETION: + return AsyncStatus.Completed; + + case STATE_CANCELLATION_REQUESTED: + case STATE_CANCELLATION_COMPLETED: + return AsyncStatus.Canceled; + + case STATE_ERROR: + return AsyncStatus.Error; + + case STATE_CLOSED: + Debug.Fail("This method should never be called is this IAsyncInfo is CLOSED"); + return AsyncStatus.Error; + } + + Debug.Fail("The switch above is missing a case"); + return AsyncStatus.Error; + } + + internal TResult GetResultsInternal() + { + EnsureNotClosed(); + + // If this IAsyncInfo has actually faulted, GetResults will throw the same error as returned by ErrorCode: + if (IsInErrorState) + { + Exception error = ErrorCode; + Debug.Assert(error != null); + ExceptionDispatchInfo.Capture(error).Throw(); + } + + // IAsyncInfo throws E_ILLEGAL_METHOD_CALL when called in a state other than COMPLETED: + if (!IsInRunToCompletionState) + throw CreateCannotGetResultsFromIncompleteOperationException(null); + + + // If this is a synchronous operation, use the cached result: + if (CompletedSynchronously) + return (TResult)_dataContainer!; + + // The operation is asynchronous: + Task task = _dataContainer as Task; + + // Since CompletedSynchronously is false and EnsureNotClosed() did not throw, task can only be null if: + // - this IAsyncInfo has completed synchronously, however we checked for this above; + // - it was not converted to Task, which means it is a non-generic Task. In that case we cannot get a result from Task. + if (task == null) + return default(TResult)!; + + Debug.Assert(IsInRunToCompletionState); + + // Pull out the task result and return. + // Any exceptions thrown in the task will be rethrown. + // If this exception is a cancelation exception, meaning there was actually no error except for being cancelled, + // return an error code appropriate for WinRT instead (InvalidOperation with E_ILLEGAL_METHOD_CALL). + try + { + return task.GetAwaiter().GetResult(); + } + catch (TaskCanceledException tcEx) + { + throw CreateCannotGetResultsFromIncompleteOperationException(tcEx); + } + } + + + private Task InvokeTaskProvider(Delegate taskProvider) + { + var funcVoidTask = taskProvider as Func; + if (funcVoidTask != null) + { + return funcVoidTask(); + } + + var funcCTokTask = taskProvider as Func; + if (funcCTokTask != null) + { + _cancelTokenSource = new CancellationTokenSource(); + return funcCTokTask(_cancelTokenSource.Token); + } + + var funcIPrgrTask = taskProvider as Func, Task>; + if (funcIPrgrTask != null) + { + return funcIPrgrTask(this); + } + + var funcCTokIPrgrTask = taskProvider as Func, Task>; + if (funcCTokIPrgrTask != null) + { + _cancelTokenSource = new CancellationTokenSource(); + return funcCTokIPrgrTask(_cancelTokenSource.Token, this); + } + + Debug.Fail("We should never get here!" + + " Public methods creating instances of this class must be typesafe to ensure that taskProvider" + + " can always be cast to one of the above Func types." + + " The taskProvider is " + (taskProvider == null + ? "null." + : "a " + taskProvider.GetType().ToString()) + "."); + return null; + } + + + private void TransitionToClosed() + { + // From the finaliser we always call this Close version since finalisation can happen any time, even when STARTED (e.g. process ends) + // and we do not want to throw in those cases. + + // Always go to closed, even from STATE_NOT_INITIALIZED. + // Any checking whether it is legal to call CLosed inthe current state, should occur in Close(). + bool ignore; + SetAsyncState(STATE_CLOSED, 0, useCondition: false, conditionFailed: out ignore); + + _cancelTokenSource = null; + _dataContainer = null; + _error = null; + _completedHandler = null; + _progressHandler = null; + _startingContext = null; + } + + #endregion Infrastructure methods + + + #region Implementation of IAsyncInfo + + /// + /// Gets or sets the completed handler. + /// + /// We will set the completion handler even when this IAsyncInfo is already started (no other choice). + /// If we the completion handler is set BEFORE this IAsyncInfo completed, then the handler will be called upon completion as normal. + /// If we the completion handler is set AFTER this IAsyncInfo already completed, then this setter will invoke the handler synchronously + /// on the current context. + /// + public virtual TCompletedHandler Completed + { + get + { + TCompletedHandler handler = Volatile.Read(ref _completedHandler); + EnsureNotClosed(); + return handler; + } + + set + { + EnsureNotClosed(); + + // Try setting completion handler, but only if this has not yet been done: + // (Note: We allow setting Completed to null multiple times iff it has not yet been set to anything else than null. + // Some other WinRT projection languages do not allow setting the Completed handler more than once, even if it is set to null. + // We could do the same by introducing a new STATEFLAG_COMPLETION_HNDL_SET bit-flag constant and saving a this state into + // the m_state field to indicate that the completion handler has been set previously, but we choose not to do this.) + TCompletedHandler handlerBefore = Interlocked.CompareExchange(ref _completedHandler, value, null); + if (handlerBefore != null) + { + InvalidOperationException ex = new InvalidOperationException(SR.InvalidOperation_CannotSetCompletionHanlderMoreThanOnce); + ex.HResult = E_ILLEGAL_DELEGATE_ASSIGNMENT; + throw ex; + } + + if (value == null) + return; + + // If STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET is OFF then we are done (i.e. no need to invoke the handler synchronously) + if (0 == (_state & STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET)) + return; + + // We have changed the handler and at some point this IAsyncInfo may have transitioned to the Closed state. + // This is OK, but if this happened we need to ensure that we only leave a null handler behind: + if (IsInClosedState) + { + Interlocked.Exchange(ref _completedHandler, null); + return; + } + + // The STATEFLAG_MUST_RUN_COMPLETION_HNDL_WHEN_SET-flag was set, so we need to call the completion handler now: + Debug.Assert(IsInTerminalState); + OnCompletedInvoker(Status); + } + } + + + /// Gets or sets the progress handler. + public virtual TProgressHandler Progress + { + get + { + TProgressHandler handler = Volatile.Read(ref _progressHandler); + EnsureNotClosed(); + + return handler; + } + + set + { + EnsureNotClosed(); + + Interlocked.Exchange(ref _progressHandler, value); + + // We transitioned into CLOSED after the above check, we will need to null out m_progressHandler: + if (IsInClosedState) + Interlocked.Exchange(ref _progressHandler, null); + } + } + + + /// Cancels the async info. + public virtual void Cancel() + { + // Cancel will be ignored in any terminal state including CLOSED. + // In other words, it is ignored in any state except STARTED. + + bool stateWasNotStarted; + SetAsyncState(STATE_CANCELLATION_REQUESTED, conditionBitMask: STATE_STARTED, useCondition: true, conditionFailed: out stateWasNotStarted); + + if (!stateWasNotStarted) + { // i.e. if state was different from STATE_STARTED: + if (_cancelTokenSource != null) + _cancelTokenSource.Cancel(); + } + } + + + /// Close the async info. + public virtual void Close() + { + if (IsInClosedState) + return; + + // Cannot Close from a non-terminal state: + if (!IsInTerminalState) + { + // If we are STATE_NOT_INITIALIZED, the we probably threw from the ctor. + // The finalizer will be called anyway and we need to free this partially constructed object correctly. + // So we avoid throwing when we are in STATE_NOT_INITIALIZED. + // In other words throw only if *some* async state is set: + if (0 != (_state & STATEMASK_SELECT_ANY_ASYNC_STATE)) + { + InvalidOperationException ex = new InvalidOperationException(SR.InvalidOperation_IllegalStateChange); + ex.HResult = E_ILLEGAL_STATE_CHANGE; + throw ex; + } + } + + TransitionToClosed(); + } + + + /// Gets the error code for the async info. + public virtual Exception ErrorCode + { + get + { + EnsureNotClosed(); + + // If the task is faulted, hand back an HR representing its first exception. + // Otherwise, hand back S_OK (which is a null Exception). + + if (!IsInErrorState) + return null; + + Exception error = Volatile.Read(ref _error); + + // ERROR is a terminal state. SO if we have an error, just return it. + // If we completed synchronously, we return the current error iven if it is null since we do not expect this to change: + if (error != null || CompletedSynchronously) + return error; + + Task task = _dataContainer as Task; + Debug.Assert(task != null); + + AggregateException aggregateException = task.Exception; + + // By spec, if task.IsFaulted is true, then task.Exception must not be null and its InnerException must + // also not be null. However, in case something is unexpected on the Task side of the road, lets be defensive + // instead of failing with an inexplicable NullReferenceException: + + if (aggregateException == null) + { + error = new Exception(SR.WinRtCOM_Error); + error.HResult = E_FAIL; + } + else + { + Exception innerException = aggregateException.InnerException; + + error = (innerException == null) + ? aggregateException + : innerException; + } + + // If m_error was set concurrently, setError will be non-null. Then we use that - as it is the first m_error + // that was set. If setError we know that we won any races and we can return error: + Exception setError = Interlocked.CompareExchange(ref _error, error, null); + return setError ?? error; + } + } + + + public virtual uint Id + { + get + { + EnsureNotClosed(); + + if (_id != AsyncInfoIdGenerator.InvalidId) + return _id; + + return AsyncInfoIdGenerator.EnsureInitializedThreadsafe(ref _id); + } + } + + + /// Gets the status of the async info. + public virtual AsyncStatus Status + { + get + { + EnsureNotClosed(); + return GetStatus(_state); + } + } + #endregion Implementation of IAsyncInfo + } // class TaskToAsyncInfoAdapter +} // namespace + +// TaskToAsyncInfoAdapter.cs diff --git a/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs b/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs index fe9605a1a..712cd783f 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs @@ -1,76 +1,76 @@ - -namespace System -{ - using global::System.Diagnostics; - using global::System.Runtime.CompilerServices; - using global::System.Runtime.InteropServices; - using global::System.Threading; - using global::System.Threading.Tasks; - using global::Windows.Foundation; - -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class WindowsRuntimeSystemExtensions - { - public static Task AsTask(this IAsyncAction source, CancellationToken cancellationToken) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); + +namespace System +{ + using global::System.Diagnostics; + using global::System.Runtime.CompilerServices; + using global::System.Runtime.InteropServices; + using global::System.Threading; + using global::System.Threading.Tasks; + using global::Windows.Foundation; + +#if NET + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] +#endif +#if EMBED + internal +#else + public +#endif + static class WindowsRuntimeSystemExtensions + { + public static Task AsTask(this IAsyncAction source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); } -#if NET +#if NET if (source is ITaskAwareAsyncInfo asyncInfo && asyncInfo.Task is Task task) { return cancellationToken.CanBeCanceled ? task.WaitAsync(cancellationToken) : task; - } -#endif - - switch (source.Status) - { - case AsyncStatus.Completed: - return Task.CompletedTask; - - case AsyncStatus.Error: - return Task.FromException(source.ErrorCode); - - case AsyncStatus.Canceled: - return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - - var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); - source.Completed = new AsyncActionCompletedHandler(bridge.CompleteFromAsyncAction); - return bridge.Task; - } - - public static Task AsTask(this IAsyncAction source) - { - return AsTask(source, CancellationToken.None); - } - - public static TaskAwaiter GetAwaiter(this IAsyncAction source) - { - return AsTask(source).GetAwaiter(); - } - - public static void Wait(this IAsyncAction source) - { - AsTask(source).Wait(); - } - - public static Task AsTask(this IAsyncOperation source, CancellationToken cancellationToken) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); + } +#endif + + switch (source.Status) + { + case AsyncStatus.Completed: + return Task.CompletedTask; + + case AsyncStatus.Error: + return Task.FromException(source.ErrorCode); + + case AsyncStatus.Canceled: + return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); + source.Completed = new AsyncActionCompletedHandler(bridge.CompleteFromAsyncAction); + return bridge.Task; + } + + public static Task AsTask(this IAsyncAction source) + { + return AsTask(source, CancellationToken.None); + } + + public static TaskAwaiter GetAwaiter(this IAsyncAction source) + { + return AsTask(source).GetAwaiter(); + } + + public static void Wait(this IAsyncAction source) + { + AsTask(source).Wait(); + } + + public static Task AsTask(this IAsyncOperation source, CancellationToken cancellationToken) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); } #if NET @@ -79,51 +79,51 @@ public static Task AsTask(this IAsyncOperation source return cancellationToken.CanBeCanceled ? task.WaitAsync(cancellationToken) : task; - } -#endif - - switch (source.Status) - { - case AsyncStatus.Completed: - return Task.FromResult(source.GetResults()); - - case AsyncStatus.Error: - return Task.FromException(source.ErrorCode); - - case AsyncStatus.Canceled: - return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - - var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); - source.Completed = new AsyncOperationCompletedHandler(bridge.CompleteFromAsyncOperation); - return bridge.Task; - } - - public static Task AsTask(this IAsyncOperation source) - { - return AsTask(source, CancellationToken.None); - } - - public static TaskAwaiter GetAwaiter(this IAsyncOperation source) - { - return AsTask(source).GetAwaiter(); - } - - public static void Wait(this IAsyncOperation source) + } +#endif + + switch (source.Status) + { + case AsyncStatus.Completed: + return Task.FromResult(source.GetResults()); + + case AsyncStatus.Error: + return Task.FromException(source.ErrorCode); + + case AsyncStatus.Canceled: + return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); + source.Completed = new AsyncOperationCompletedHandler(bridge.CompleteFromAsyncOperation); + return bridge.Task; + } + + public static Task AsTask(this IAsyncOperation source) + { + return AsTask(source, CancellationToken.None); + } + + public static TaskAwaiter GetAwaiter(this IAsyncOperation source) + { + return AsTask(source).GetAwaiter(); + } + + public static void Wait(this IAsyncOperation source) { AsTask(source).Wait(); - } - - public static TResult Get(this IAsyncOperation source) + } + + public static TResult Get(this IAsyncOperation source) { return AsTask(source).Result; - } - - public static Task AsTask(this IAsyncActionWithProgress source, CancellationToken cancellationToken, IProgress progress) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); + } + + public static Task AsTask(this IAsyncActionWithProgress source, CancellationToken cancellationToken, IProgress progress) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); } #if NET @@ -133,67 +133,67 @@ public static Task AsTask(this IAsyncActionWithProgress so return cancellationToken.CanBeCanceled ? task.WaitAsync(cancellationToken) : task; - } -#endif - - switch (source.Status) - { - case AsyncStatus.Completed: - return Task.CompletedTask; - - case AsyncStatus.Error: - return Task.FromException(source.ErrorCode); - - case AsyncStatus.Canceled: - return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - - if (progress != null) - { - SetProgress(source, progress); - } - - var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); - source.Completed = new AsyncActionWithProgressCompletedHandler(bridge.CompleteFromAsyncActionWithProgress); - return bridge.Task; - } - - private static void SetProgress(IAsyncActionWithProgress source, IProgress sink) - { - // This is separated out into a separate method so that we only pay the costs of compiler-generated closure if progress is non-null. - source.Progress = new AsyncActionProgressHandler((_, info) => sink.Report(info)); - } - - public static Task AsTask(this IAsyncActionWithProgress source) - { - return AsTask(source, CancellationToken.None, null); - } - - public static Task AsTask(this IAsyncActionWithProgress source, CancellationToken cancellationToken) - { - return AsTask(source, cancellationToken, null); - } - - public static Task AsTask(this IAsyncActionWithProgress source, IProgress progress) - { - return AsTask(source, CancellationToken.None, progress); - } - - public static TaskAwaiter GetAwaiter(this IAsyncActionWithProgress source) - { - return AsTask(source).GetAwaiter(); - } - - public static void Wait(this IAsyncActionWithProgress source) + } +#endif + + switch (source.Status) + { + case AsyncStatus.Completed: + return Task.CompletedTask; + + case AsyncStatus.Error: + return Task.FromException(source.ErrorCode); + + case AsyncStatus.Canceled: + return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + + if (progress != null) + { + SetProgress(source, progress); + } + + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); + source.Completed = new AsyncActionWithProgressCompletedHandler(bridge.CompleteFromAsyncActionWithProgress); + return bridge.Task; + } + + private static void SetProgress(IAsyncActionWithProgress source, IProgress sink) + { + // This is separated out into a separate method so that we only pay the costs of compiler-generated closure if progress is non-null. + source.Progress = new AsyncActionProgressHandler((_, info) => sink.Report(info)); + } + + public static Task AsTask(this IAsyncActionWithProgress source) + { + return AsTask(source, CancellationToken.None, null); + } + + public static Task AsTask(this IAsyncActionWithProgress source, CancellationToken cancellationToken) + { + return AsTask(source, cancellationToken, null); + } + + public static Task AsTask(this IAsyncActionWithProgress source, IProgress progress) + { + return AsTask(source, CancellationToken.None, progress); + } + + public static TaskAwaiter GetAwaiter(this IAsyncActionWithProgress source) + { + return AsTask(source).GetAwaiter(); + } + + public static void Wait(this IAsyncActionWithProgress source) { AsTask(source).Wait(); - } - - public static Task AsTask(this IAsyncOperationWithProgress source, CancellationToken cancellationToken, IProgress progress) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); + } + + public static Task AsTask(this IAsyncOperationWithProgress source, CancellationToken cancellationToken, IProgress progress) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); } #if NET @@ -203,244 +203,244 @@ public static Task AsTask(this IAsyncOperationWithP return cancellationToken.CanBeCanceled ? task.WaitAsync(cancellationToken) : task; - } -#endif - - switch (source.Status) - { - case AsyncStatus.Completed: - return Task.FromResult(source.GetResults()); - - case AsyncStatus.Error: - return Task.FromException(source.ErrorCode); - - case AsyncStatus.Canceled: - return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - - if (progress != null) - { - SetProgress(source, progress); - } - - var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); - source.Completed = new AsyncOperationWithProgressCompletedHandler(bridge.CompleteFromAsyncOperationWithProgress); - return bridge.Task; - } - - private static void SetProgress(IAsyncOperationWithProgress source, IProgress sink) - { - // This is separated out into a separate method so that we only pay the costs of compiler-generated closure if progress is non-null. - source.Progress = new AsyncOperationProgressHandler((_, info) => sink.Report(info)); - } - - public static Task AsTask(this IAsyncOperationWithProgress source) - { - return AsTask(source, CancellationToken.None, null); - } - - public static Task AsTask(this IAsyncOperationWithProgress source, CancellationToken cancellationToken) - { - return AsTask(source, cancellationToken, null); - } - - public static Task AsTask(this IAsyncOperationWithProgress source, IProgress progress) - { - return AsTask(source, CancellationToken.None, progress); - } - - public static TaskAwaiter GetAwaiter(this IAsyncOperationWithProgress source) - { - return AsTask(source).GetAwaiter(); - } - - public static void Wait(this IAsyncOperationWithProgress source) + } +#endif + + switch (source.Status) + { + case AsyncStatus.Completed: + return Task.FromResult(source.GetResults()); + + case AsyncStatus.Error: + return Task.FromException(source.ErrorCode); + + case AsyncStatus.Canceled: + return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + + if (progress != null) + { + SetProgress(source, progress); + } + + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); + source.Completed = new AsyncOperationWithProgressCompletedHandler(bridge.CompleteFromAsyncOperationWithProgress); + return bridge.Task; + } + + private static void SetProgress(IAsyncOperationWithProgress source, IProgress sink) + { + // This is separated out into a separate method so that we only pay the costs of compiler-generated closure if progress is non-null. + source.Progress = new AsyncOperationProgressHandler((_, info) => sink.Report(info)); + } + + public static Task AsTask(this IAsyncOperationWithProgress source) + { + return AsTask(source, CancellationToken.None, null); + } + + public static Task AsTask(this IAsyncOperationWithProgress source, CancellationToken cancellationToken) + { + return AsTask(source, cancellationToken, null); + } + + public static Task AsTask(this IAsyncOperationWithProgress source, IProgress progress) + { + return AsTask(source, CancellationToken.None, progress); + } + + public static TaskAwaiter GetAwaiter(this IAsyncOperationWithProgress source) + { + return AsTask(source).GetAwaiter(); + } + + public static void Wait(this IAsyncOperationWithProgress source) { AsTask(source).Wait(); - } - - public static TResult Get(this IAsyncOperationWithProgress source) + } + + public static TResult Get(this IAsyncOperationWithProgress source) { return AsTask(source).Result; - } - - public static IAsyncAction AsAsyncAction(this Task source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - return new TaskToAsyncActionAdapter(source, underlyingCancelTokenSource: null); - } - - public static IAsyncOperation AsAsyncOperation(this Task source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - return new TaskToAsyncOperationAdapter(source, underlyingCancelTokenSource: null); - } - } - - // Marker type since generic parameters cannot be 'void' - struct VoidValueTypeParameter { } - - /// This can be used instead of VoidValueTypeParameter when a reference type is required. - /// In case of an actual instantiation (e.g. through default(T)), - /// using VoidValueTypeParameter offers better performance. - internal class VoidReferenceTypeParameter { } - -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - sealed class AsyncInfoToTaskBridge : TaskCompletionSource - { - private readonly CancellationToken _ct; - private readonly CancellationTokenRegistration _asyncInfoRegistration; - private readonly CancellationTokenRegistration _registration; - - internal AsyncInfoToTaskBridge(IAsyncInfo asyncInfo, CancellationToken cancellationToken) + } + + public static IAsyncAction AsAsyncAction(this Task source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return new TaskToAsyncActionAdapter(source, underlyingCancelTokenSource: null); + } + + public static IAsyncOperation AsAsyncOperation(this Task source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + return new TaskToAsyncOperationAdapter(source, underlyingCancelTokenSource: null); + } + } + + // Marker type since generic parameters cannot be 'void' + struct VoidValueTypeParameter { } + + /// This can be used instead of VoidValueTypeParameter when a reference type is required. + /// In case of an actual instantiation (e.g. through default(T)), + /// using VoidValueTypeParameter offers better performance. + internal class VoidReferenceTypeParameter { } + +#if NET + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] +#endif + sealed class AsyncInfoToTaskBridge : TaskCompletionSource + { + private readonly CancellationToken _ct; + private readonly CancellationTokenRegistration _asyncInfoRegistration; + private readonly CancellationTokenRegistration _registration; + + internal AsyncInfoToTaskBridge(IAsyncInfo asyncInfo, CancellationToken cancellationToken) { if (asyncInfo == null) { throw new ArgumentNullException(nameof(asyncInfo)); - } - - this._ct = cancellationToken; - if (this._ct.CanBeCanceled) + } + + this._ct = cancellationToken; + if (this._ct.CanBeCanceled) { -#if NET - _registration = this._ct.Register(static (b, ct) => ((TaskCompletionSource)b).TrySetCanceled(ct), this); -#else - _registration = this._ct.Register((b) => ((TaskCompletionSource)b).TrySetCanceled(this._ct), this); -#endif +#if NET + _registration = this._ct.Register(static (b, ct) => ((TaskCompletionSource)b).TrySetCanceled(ct), this); +#else + _registration = this._ct.Register((b) => ((TaskCompletionSource)b).TrySetCanceled(this._ct), this); +#endif // Handle Exception from Cancel() if the token is already canceled. - try - { - _asyncInfoRegistration = this._ct.Register(static ai => ((IAsyncInfo)ai).Cancel(), asyncInfo); - } - catch (Exception ex) - { - if (!base.Task.IsFaulted) - { - Debug.Fail($"Expected base task to already be faulted but found it in state {base.Task.Status}"); - base.TrySetException(ex); - } - } - } - - // If we're already completed, unregister everything again. Unregistration is idempotent and thread-safe. - if (Task.IsCompleted) - { - this.Cleanup(); - } - } - internal void CompleteFromAsyncAction(IAsyncAction asyncInfo, AsyncStatus asyncStatus) - { - Complete(asyncInfo, null, asyncStatus); - } - - internal void CompleteFromAsyncActionWithProgress(IAsyncActionWithProgress asyncInfo, AsyncStatus asyncStatus) - { - Complete(asyncInfo, null, asyncStatus); - } - - internal void CompleteFromAsyncOperation(IAsyncOperation asyncInfo, AsyncStatus asyncStatus) - { - Complete(asyncInfo, ai => ((IAsyncOperation)ai).GetResults(), asyncStatus); - } - - internal void CompleteFromAsyncOperationWithProgress(IAsyncOperationWithProgress asyncInfo, AsyncStatus asyncStatus) - { - Complete(asyncInfo, ai => ((IAsyncOperationWithProgress)ai).GetResults(), asyncStatus); - } - - private void Complete(IAsyncInfo asyncInfo, Func getResultsFunction, AsyncStatus asyncStatus) - { - if (asyncInfo == null) - { - throw new ArgumentNullException(nameof(asyncInfo)); - } - - try - { - Debug.Assert(asyncInfo.Status == asyncStatus, "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?"); - - if (asyncStatus != AsyncStatus.Completed && asyncStatus != AsyncStatus.Canceled && asyncStatus != AsyncStatus.Error) - { - Debug.Fail("The async operation should be in a terminal state."); - throw new InvalidOperationException("The asynchronous operation could not be completed."); - } - - TResult result = default(TResult); - Exception error = null; - if (asyncStatus == AsyncStatus.Error) - { - error = asyncInfo.ErrorCode; - - // Defend against a faulty IAsyncInfo implementation - if (error is null) - { - Debug.Fail("IAsyncInfo.Status == Error, but ErrorCode returns a null Exception (implying S_OK)."); - error = new InvalidOperationException("The asynchronous operation could not be completed."); - } - } - else if (asyncStatus == AsyncStatus.Completed && getResultsFunction != null) - { - try - { - result = getResultsFunction(asyncInfo); - } - catch (Exception resultsEx) - { - // According to the WinRT team, this can happen in some egde cases, such as marshalling errors in GetResults. - error = resultsEx; - asyncStatus = AsyncStatus.Error; - } - } - - // Complete the task based on the previously retrieved results: - bool success = false; - switch (asyncStatus) - { - case AsyncStatus.Completed: - success = base.TrySetResult(result); - break; - - case AsyncStatus.Error: - Debug.Assert(error != null, "The error should have been retrieved previously."); - success = base.TrySetException(error); - break; - - case AsyncStatus.Canceled: - success = base.TrySetCanceled(this._ct.IsCancellationRequested ? this._ct : new CancellationToken(true)); - break; - } - - if (success) - { - Cleanup(); - } - } - catch (Exception exc) - { - Debug.Fail($"Unexpected exception in Complete: {exc}"); - - if (!base.TrySetException(exc)) - { - Debug.Fail("The task was already completed and thus the exception couldn't be stored."); - throw; - } - else - { - Cleanup(); - } - } - } - - private void Cleanup() - { - _registration.Dispose(); - _asyncInfoRegistration.Dispose(); - } - } -} + try + { + _asyncInfoRegistration = this._ct.Register(static ai => ((IAsyncInfo)ai).Cancel(), asyncInfo); + } + catch (Exception ex) + { + if (!base.Task.IsFaulted) + { + Debug.Fail($"Expected base task to already be faulted but found it in state {base.Task.Status}"); + base.TrySetException(ex); + } + } + } + + // If we're already completed, unregister everything again. Unregistration is idempotent and thread-safe. + if (Task.IsCompleted) + { + this.Cleanup(); + } + } + internal void CompleteFromAsyncAction(IAsyncAction asyncInfo, AsyncStatus asyncStatus) + { + Complete(asyncInfo, null, asyncStatus); + } + + internal void CompleteFromAsyncActionWithProgress(IAsyncActionWithProgress asyncInfo, AsyncStatus asyncStatus) + { + Complete(asyncInfo, null, asyncStatus); + } + + internal void CompleteFromAsyncOperation(IAsyncOperation asyncInfo, AsyncStatus asyncStatus) + { + Complete(asyncInfo, ai => ((IAsyncOperation)ai).GetResults(), asyncStatus); + } + + internal void CompleteFromAsyncOperationWithProgress(IAsyncOperationWithProgress asyncInfo, AsyncStatus asyncStatus) + { + Complete(asyncInfo, ai => ((IAsyncOperationWithProgress)ai).GetResults(), asyncStatus); + } + + private void Complete(IAsyncInfo asyncInfo, Func getResultsFunction, AsyncStatus asyncStatus) + { + if (asyncInfo == null) + { + throw new ArgumentNullException(nameof(asyncInfo)); + } + + try + { + Debug.Assert(asyncInfo.Status == asyncStatus, "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?"); + + if (asyncStatus != AsyncStatus.Completed && asyncStatus != AsyncStatus.Canceled && asyncStatus != AsyncStatus.Error) + { + Debug.Fail("The async operation should be in a terminal state."); + throw new InvalidOperationException("The asynchronous operation could not be completed."); + } + + TResult result = default(TResult); + Exception error = null; + if (asyncStatus == AsyncStatus.Error) + { + error = asyncInfo.ErrorCode; + + // Defend against a faulty IAsyncInfo implementation + if (error is null) + { + Debug.Fail("IAsyncInfo.Status == Error, but ErrorCode returns a null Exception (implying S_OK)."); + error = new InvalidOperationException("The asynchronous operation could not be completed."); + } + } + else if (asyncStatus == AsyncStatus.Completed && getResultsFunction != null) + { + try + { + result = getResultsFunction(asyncInfo); + } + catch (Exception resultsEx) + { + // According to the WinRT team, this can happen in some egde cases, such as marshalling errors in GetResults. + error = resultsEx; + asyncStatus = AsyncStatus.Error; + } + } + + // Complete the task based on the previously retrieved results: + bool success = false; + switch (asyncStatus) + { + case AsyncStatus.Completed: + success = base.TrySetResult(result); + break; + + case AsyncStatus.Error: + Debug.Assert(error != null, "The error should have been retrieved previously."); + success = base.TrySetException(error); + break; + + case AsyncStatus.Canceled: + success = base.TrySetCanceled(this._ct.IsCancellationRequested ? this._ct : new CancellationToken(true)); + break; + } + + if (success) + { + Cleanup(); + } + } + catch (Exception exc) + { + Debug.Fail($"Unexpected exception in Complete: {exc}"); + + if (!base.TrySetException(exc)) + { + Debug.Fail("The task was already completed and thus the exception couldn't be stored."); + throw; + } + else + { + Cleanup(); + } + } + } + + private void Cleanup() + { + _registration.Dispose(); + _asyncInfoRegistration.Dispose(); + } + } +} From a78cde0d8b5cff1d5ba38b8fd6b2cb04e2c10784 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 26 Oct 2025 14:14:12 -0700 Subject: [PATCH 27/43] Fix additions --- src/Projections/Windows/Windows.csproj | 1 - .../InteropServices/RestrictedErrorInfo.cs | 11 + src/cswinrt/cswinrt.vcxproj | 5 +- src/cswinrt/cswinrt.vcxproj.filters | 11 +- src/cswinrt/main.cpp | 13 +- .../IBufferByteAccess.cs | 197 +- .../Windows.Storage.Streams/IMarshal.cs | 499 +++--- .../IMemoryBufferByteAccess.cs | 20 + .../NetFxToWinRtStreamAdapter.cs | 906 +++++----- .../StreamOperationAsyncResult.cs | 6 +- .../StreamOperationsImplementation.cs | 491 +++-- .../StreamTaskAdaptersImplementation.cs | 610 ------- .../WellKnownStreamInterfaceIIDs.cs | 45 + .../WinRtToNetFxStreamAdapter.cs | 1584 ++++++++--------- .../WindowsRuntimeBuffer.cs | 622 +++---- .../WindowsRuntimeBufferExtensions.cs | 868 +++++---- ...shal.cs => WindowsRuntimeBufferMarshal.cs} | 284 ++- .../WindowsRuntimeStreamExtensions.cs | 14 +- .../IStorageFolderHandleAccess.cs | 61 +- .../IStorageItemHandleAccess.cs | 89 +- .../WindowsRuntimeStorageExtensions.cs | 554 +++--- 21 files changed, 3061 insertions(+), 3830 deletions(-) create mode 100644 src/cswinrt/strings/additions/Windows.Storage.Streams/IMemoryBufferByteAccess.cs delete mode 100644 src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs create mode 100644 src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs rename src/cswinrt/strings/additions/Windows.Storage.Streams/{WindowsRuntimeMarshal.cs => WindowsRuntimeBufferMarshal.cs} (71%) diff --git a/src/Projections/Windows/Windows.csproj b/src/Projections/Windows/Windows.csproj index 1fca80cd6..020f8f45f 100644 --- a/src/Projections/Windows/Windows.csproj +++ b/src/Projections/Windows/Windows.csproj @@ -11,7 +11,6 @@ -
diff --git a/src/WinRT.Runtime2/InteropServices/RestrictedErrorInfo.cs b/src/WinRT.Runtime2/InteropServices/RestrictedErrorInfo.cs index b6a76dcd6..4d82ac49e 100644 --- a/src/WinRT.Runtime2/InteropServices/RestrictedErrorInfo.cs +++ b/src/WinRT.Runtime2/InteropServices/RestrictedErrorInfo.cs @@ -75,6 +75,17 @@ public static void SetErrorInfo(Exception exception) // TODO } + /// + /// Attaches the error info stored by the IRestrictedErrorInfo infrastructure to the input exception. + /// + /// The input instance to attach the error info to. + /// The input instance with attached error info. + public static Exception AttachErrorInfo(Exception exception) + { + // TODO + return exception; + } + /// /// Triggers the global error handler when an unhandled exception occurs. /// diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index 0b66b8165..d2c7e2b62 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -88,14 +88,15 @@ + + - - + diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 38c896d7e..0f47585ce 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -174,7 +174,7 @@ strings\additions\Microsoft.UI.Xaml - + strings\additions\Microsoft.UI.Xaml.Controls.Primitives @@ -190,10 +190,7 @@ strings\additions\Windows.Foundation - - strings\additions\Windows.Storage.Streams - - + strings\additions\Windows.Storage.Streams @@ -236,10 +233,10 @@ strings\additions\Microsoft.UI.Xaml - strings\additions\Microsoft.UI.Xaml + strings\additions\Microsoft.UI.Xaml - strings\additions\Microsoft.UI.Xaml + strings\additions\Microsoft.UI.Xaml strings\additions\Microsoft.UI.Xaml.Media.Animation diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index 74564d5c9..3416035c6 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -294,6 +294,17 @@ Where is one or more of: break; } } + + // Attributes need to be written at the start, so handling this addition separately. + if (ns == "Windows.Storage.Streams" && settings.addition_filter.includes(ns)) + { + w.write(R"( +[assembly: TypeMapAssociation( + typeof(global::System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBuffer), + typeof(global::ABI.System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBuffer))] +)"); + } + currentType = ""; w.write_begin_projected(); @@ -409,7 +420,7 @@ Where is one or more of: // Custom additions to namespaces for (auto addition : strings::additions) { - if (ns == addition.name && (ns != "Windows.Storage" && ns != "Windows.Storage.Streams") && settings.addition_filter.includes(ns)) + if (ns == addition.name && settings.addition_filter.includes(ns)) { w.write(addition.value); } diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs index ec81830cc..a4c1e0916 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs @@ -1,144 +1,63 @@ -namespace Windows.Storage.Streams -{ - using global::System; - - [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] +namespace Windows.Storage.Streams +{ + [WindowsRuntimeMetadata("Windows.Foundation.UniversalApiContract")] [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Storage.Streams.IBufferByteAccess))] - internal interface IBufferByteAccess - { - IntPtr Buffer { get; } - } -} - -namespace ABI.Windows.Storage.Streams -{ - using global::System; - using global::System.Runtime.CompilerServices; - using global::System.Runtime.InteropServices; - using global::System.ComponentModel; - -#if !NET - [global::WinRT.ObjectReferenceWrapper(nameof(_obj)), EditorBrowsable(EditorBrowsableState.Never)] - [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - internal unsafe class IBufferByteAccess : global::Windows.Storage.Streams.IBufferByteAccess - { - [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - public struct Vftbl - { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _get_Buffer_0; - public delegate* unmanaged[Stdcall] get_Buffer_0 { get => (delegate* unmanaged[Stdcall])_get_Buffer_0; set => _get_Buffer_0 = value; } - - public unsafe delegate int _get_Buffer_0_delegate(IntPtr thisPtr, IntPtr* result); - private static readonly _get_Buffer_0_delegate DelegateCache; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - _get_Buffer_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache = Do_Abi_get_Buffer_0).ToPointer() - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - public static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - private static int Do_Abi_get_Buffer_0(IntPtr thisPtr, IntPtr* buffer) - { - *buffer = default; - try - { - *buffer = ComWrappersSupport.FindObject(thisPtr).Buffer; - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IBufferByteAccess(IObjectReference obj) => (obj != null) ? new IBufferByteAccess(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IBufferByteAccess(IObjectReference obj) : this(obj.As()) { } - internal IBufferByteAccess(ObjectReference obj) - { - _obj = obj; - } - - public IntPtr Buffer - { - get - { - IntPtr __retval = default; - Marshal.ThrowExceptionForHR(_obj.Vftbl.get_Buffer_0(ThisPtr, &__retval)); - GC.KeepAlive(_obj); - return __retval; - } - } - } -#else - [DynamicInterfaceCastableImplementation] - [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - internal unsafe interface IBufferByteAccess : global::Windows.Storage.Streams.IBufferByteAccess - { - [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - public struct Vftbl + internal unsafe interface IBufferByteAccess + { + byte* Buffer { get; } + } +} + +namespace ABI.Windows.Storage.Streams +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct IBufferByteAccessVftbl + { + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] get_Buffer; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetBufferUnsafe( + void* thisPtr, + byte** result) + { + return ((IBufferByteAccessVftbl*)*(void***)thisPtr)->get_Buffer(thisPtr, result); + } + } + + internal static unsafe class IBufferByteAccessImpl + { + [FixedAddressValueType] + private static readonly IBufferByteAccessVftbl Vftbl; + + static IBufferByteAccessImpl() + { + *(IUnknownVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IUnknownVftbl*)IUnknownImpl.Vtable; + Vftbl.get_Buffer = &Do_Abi_get_Buffer; + } + + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvMemberFunction) })] + private static unsafe int Do_Abi_get_Buffer(void* thisPtr, byte** result) { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public delegate* unmanaged[Stdcall] Get_Buffer_0; + *result = default; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() + try { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr)); - (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - Get_Buffer_0 = &Do_Abi_get_Buffer_0, - }; + *result = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr).Buffer; + return 0; } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static int Do_Abi_get_Buffer_0(IntPtr thisPtr, IntPtr* buffer) - { - *buffer = default; - try - { - *buffer = ComWrappersSupport.FindObject(thisPtr).Buffer; - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IBufferByteAccess); - - IntPtr global::Windows.Storage.Streams.IBufferByteAccess.Buffer - { - get - { - var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Storage.Streams.IBufferByteAccess).TypeHandle); - var ThisPtr = _obj.ThisPtr; - IntPtr buffer = default; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)ThisPtr)[3])(ThisPtr, &buffer)); - GC.KeepAlive(_obj); - return buffer; - } - } - } -#endif -} + catch (Exception __exception__) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(__exception__); + } + } + } +} diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs index 75b735d6f..8bab861e3 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs @@ -1,303 +1,294 @@ -namespace Com +namespace ABI.Windows.Storage.Streams { - using global::System; - - internal enum MSHCTX : int { Local = 0, NoSharedMem = 1, DifferentMachine = 2, InProc = 3, CrossCtx = 4 } - internal enum MSHLFLAGS : int { Normal = 0, TableStrong = 1, TableWeak = 2, NoPing = 4 } - - [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] - [Guid("00000003-0000-0000-c000-000000000046")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Com.IMarshal))] - internal interface IMarshal + /// + /// Binding type for the IMarshal interface vtable. + /// + /// + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct IBufferMarshalVftbl { - unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlFlags, Guid* pCid); + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetUnmarshalClass; + public delegate* unmanaged[MemberFunction] GetMarshalSizeMax; + public delegate* unmanaged[MemberFunction] MarshalInterface; + public delegate* unmanaged[MemberFunction] UnmarshalInterface; + public delegate* unmanaged[MemberFunction] ReleaseMarshalData; + public delegate* unmanaged[MemberFunction] DisconnectObject; - unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags, uint* pSize); + /// + /// Retrieves the CLSID of the unmarshaling code. + /// + /// The target COM object. + /// A reference to the identifier of the interface to be marshaled. + /// A pointer to the interface to be marshaled (can be ). + /// The destination context where the specified interface is to be unmarshaled. + /// This parameter is reserved and must be . + /// Indicates whether the data to be marshaled is to be transmitted back to the client process or written to a global table. + /// A pointer that receives the CLSID to be used to create a proxy in the client process. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUnmarshalClassUnsafe( + void* thisPtr, + Guid* riid, + void* pv, + uint dwDestContext, + void* pvDestContext, + uint mshlflags, + Guid* pCid) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->GetUnmarshalClass(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pCid); + } - unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags); + /// + /// Retrieves the maximum size of the buffer that will be needed during marshaling. + /// + /// The target COM object. + /// A reference to the identifier of the interface to be marshaled. + /// A pointer to the interface to be marshaled (can be ). + /// The destination context where the specified interface is to be unmarshaled. + /// This parameter is reserved and must be . + /// Indicates whether the data to be marshaled is to be transmitted back to the client process or written to a global table. + /// A pointer to a variable that receives the maximum size of the buffer. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMarshalSizeMaxUnsafe( + void* thisPtr, + Guid* riid, + void* pv, + uint dwDestContext, + void* pvDestContext, + uint mshlflags, + uint* pSize) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->GetMarshalSizeMax(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); + } - unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv); + /// + /// Marshals an interface pointer. + /// + /// The target COM object. + /// A pointer to the stream to be used during marshaling. + /// A reference to the identifier of the interface to be marshaled. + /// A pointer to the interface to be marshaled (can be ). + /// The destination context where the specified interface is to be unmarshaled. + /// This parameter is reserved and must be . + /// Indicates whether the data to be marshaled is to be transmitted back to the client process or written to a global table. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int MarshalInterfaceUnsafe( + void* thisPtr, + void* pStm, + Guid* riid, + void* pv, + uint dwDestContext, + void* pvDestContext, + uint mshlflags) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->MarshalInterface(thisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); + } - void ReleaseMarshalData(IntPtr pStm); + /// + /// Unmarshals an interface pointer. + /// + /// The target COM object. + /// A pointer to the stream from which the interface pointer is to be unmarshaled. + /// A reference to the identifier of the interface to be unmarshaled. + /// The address of pointer variable that receives the interface pointer. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int UnmarshalInterfaceUnsafe( + void* thisPtr, + void* pStm, + Guid* riid, + void** ppv) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->UnmarshalInterface(thisPtr, pStm, riid, ppv); + } - void DisconnectObject(uint dwReserved); - } -} + /// + /// Destroys a marshaled data packet. + /// + /// The target COM object. + /// A pointer to a stream that contains the data packet to be destroyed. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReleaseMarshalDataUnsafe(void* thisPtr, void* pStm) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->ReleaseMarshalData(thisPtr, pStm); + } -namespace ABI.Com -{ - using global::System; - using global::System.ComponentModel; - using global::System.Diagnostics.CodeAnalysis; - using global::System.Runtime.CompilerServices; - using global::System.Runtime.InteropServices; + /// + /// Releases all connections to an object. + /// + /// The target COM object. + /// This parameter is reserved and must be 0. + /// The HRESULT for the operation. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int DisconnectObjectUnsafe(void* thisPtr, uint dwReserved) + { + return ((IBufferMarshalVftbl*)*(void***)thisPtr)->DisconnectObject(thisPtr, dwReserved); + } + } - [Guid("00000003-0000-0000-c000-000000000046")] - internal sealed class IMarshal : global::Com.IMarshal + internal static unsafe class IBufferMarshalImpl { - [Guid("00000003-0000-0000-c000-000000000046")] - public unsafe struct Vftbl + /// + /// The value for the managed IMarshal implementation using RoGetBufferMarshaler as its implementation. + /// + [FixedAddressValueType] + private static readonly IBufferMarshalVftbl Vftbl; + + [ThreadStatic] + private static void* t_winRtMarshalProxy = null; + + /// + /// Initializes . + /// + static IBufferMarshalImpl() { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + *(IUnknownVftbl*)Unsafe.AsPointer(ref Vftbl) = *(IUnknownVftbl*)IUnknownImpl.Vtable; -#if !NET - private void* _GetUnmarshalClass_0; - public delegate* unmanaged[Stdcall] GetUnmarshalClass_0 { get => (delegate* unmanaged[Stdcall])_GetUnmarshalClass_0; set => _GetUnmarshalClass_0 = value; } - private void* _GetMarshalSizeMax_1; - public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1 { get => (delegate* unmanaged[Stdcall])_GetMarshalSizeMax_1; set => _GetMarshalSizeMax_1 = value; } - private void* _MarshalInterface_2; - public delegate* unmanaged[Stdcall] MarshalInterface_2 { get => (delegate* unmanaged[Stdcall])_MarshalInterface_2; set => _MarshalInterface_2 = value; } - private void* _UnmarshalInterface_3; - public delegate* unmanaged[Stdcall] UnmarshalInterface_3 { get => (delegate* unmanaged[Stdcall])_UnmarshalInterface_3; set => _UnmarshalInterface_3 = value; } - private void* _ReleaseMarshalData_4; - public delegate* unmanaged[Stdcall] ReleaseMarshalData_4 { get => (delegate* unmanaged[Stdcall])_ReleaseMarshalData_4; set => _ReleaseMarshalData_4 = value; } - private void* _DisconnectObject_5; - public delegate* unmanaged[Stdcall] DisconnectObject_5 { get => (delegate* unmanaged[Stdcall])_DisconnectObject_5; set => _DisconnectObject_5 = value; } + Vftbl.GetUnmarshalClass = &GetUnmarshalClass; + Vftbl.GetMarshalSizeMax = &GetMarshalSizeMax; + Vftbl.MarshalInterface = &MarshalInterface; + Vftbl.UnmarshalInterface = &UnmarshalInterface; + Vftbl.ReleaseMarshalData = &ReleaseMarshalData; + Vftbl.DisconnectObject = &DisconnectObject; + } - private static readonly Delegate[] DelegateCache = new Delegate[6]; - public static readonly Vftbl AbiToProjectionVftable; -#else - public delegate* unmanaged[Stdcall] GetUnmarshalClass_0; - public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1; - public delegate* unmanaged[Stdcall] MarshalInterface_2; - public delegate* unmanaged[Stdcall] UnmarshalInterface_3; - public delegate* unmanaged[Stdcall] ReleaseMarshalData_4; - public delegate* unmanaged[Stdcall] DisconnectObject_5; -#endif + [DllImport("api-ms-win-core-winrt-robuffer-l1-1-0.dll")] + private static extern unsafe int RoGetBufferMarshaler(void** bufferMarshalerPtr); - public static readonly IntPtr AbiToProjectionVftablePtr; + private const string WinTypesDLL = "WinTypes.dll"; - static Vftbl() - { -#if !NET - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - _GetUnmarshalClass_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new IMarshal_Delegates.GetUnmarshalClass_0(Do_Abi_GetUnmarshalClass_0)).ToPointer(), - _GetMarshalSizeMax_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IMarshal_Delegates.GetMarshalSizeMax_1(Do_Abi_GetMarshalSizeMax_1)).ToPointer(), - _MarshalInterface_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IMarshal_Delegates.MarshalInterface_2(Do_Abi_MarshalInterface_2)).ToPointer(), - _UnmarshalInterface_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new IMarshal_Delegates.UnmarshalInterface_3(Do_Abi_UnmarshalInterface_3)).ToPointer(), - _ReleaseMarshalData_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new IMarshal_Delegates.ReleaseMarshalData_4(Do_Abi_ReleaseMarshalData_4)).ToPointer(), - _DisconnectObject_5 = Marshal.GetFunctionPointerForDelegate(DelegateCache[5] = new IMarshal_Delegates.DisconnectObject_5(Do_Abi_DisconnectObject_5)).ToPointer(), - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); -#else - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr) * 6); - (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - GetUnmarshalClass_0 = &Do_Abi_GetUnmarshalClass_0, - GetMarshalSizeMax_1 = &Do_Abi_GetMarshalSizeMax_1, - MarshalInterface_2 = &Do_Abi_MarshalInterface_2, - UnmarshalInterface_3 = &Do_Abi_UnmarshalInterface_3, - ReleaseMarshalData_4 = &Do_Abi_ReleaseMarshalData_4, - DisconnectObject_5 = &Do_Abi_DisconnectObject_5 - }; -#endif - } + private static unsafe void EnsureHasMarshalProxy() + { + if (t_winRtMarshalProxy != null) + return; -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) + try { - *pCid = default; - try + void* proxyPtr = null; + RestrictedErrorInfo.ThrowExceptionForHR(RoGetBufferMarshaler(&proxyPtr)); + if (proxyPtr == null) { - ComWrappersSupport.FindObject(thisPtr).GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid); + throw new NullReferenceException(string.Format("{0} ({1}!RoGetBufferMarshaler)", global::Windows.Storage.Streams.SR.WinRtCOM_Error, WinTypesDLL)); } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; + t_winRtMarshalProxy = proxyPtr; } - -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) + catch (DllNotFoundException ex) { - *pSize = default; - try - { - ComWrappersSupport.FindObject(thisPtr).GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; + throw new NotImplementedException(string.Format(global::Windows.Storage.Streams.SR.NotImplemented_NativeRoutineNotFound, + string.Format("{0}!RoGetBufferMarshaler", WinTypesDLL)), + ex); } + } -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) - { - try - { - ComWrappersSupport.FindObject(thisPtr).MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; - } + /// + /// Gets a pointer to the managed IMarshal implementation using RoGetBufferMarshaler. + /// + public static nint Vtable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (nint)Unsafe.AsPointer(in Vftbl); + } -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv) - { - *ppv = default; - try - { - ComWrappersSupport.FindObject(thisPtr).UnmarshalInterface(pStm, riid, ppv); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; - } + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int GetUnmarshalClass(void* thisPtr, Guid* riid, void* pv, uint dwDestContext, void* pvDestContext, uint mshlflags, Guid* pCid) + { + *pCid = default; -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) + try { - try - { - ComWrappersSupport.FindObject(thisPtr).ReleaseMarshalData(pStm); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.GetUnmarshalClassUnsafe(t_winRtMarshalProxy, riid, pv, dwDestContext, pvDestContext, mshlflags, pCid); } - -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) + catch (Exception ex) { - try - { - ComWrappersSupport.FindObject(thisPtr).DisconnectObject(dwReserved); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); } } -#if NET - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IMarshal); -#else - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IMarshal); -#endif -#if NET - private readonly ObjectReference _obj; -#else - private readonly ObjectReference _obj; -#endif - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; -#if NET - public IMarshal(IObjectReference obj) + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int GetMarshalSizeMax(void* thisPtr, Guid* riid, void* pv, uint dwDestContext, void* pvDestContext, uint mshlflags, uint* pSize) { - _obj = obj.As(global::WinRT.Interop.IID.IID_IMarshal); - } -#else - public IMarshal(IObjectReference obj) : this(obj.As()) { } - internal IMarshal(ObjectReference obj) - { - _obj = obj; - } -#endif + *pSize = 0; - public unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) - { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetUnmarshalClass_0(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); -#endif - GC.KeepAlive(_obj); + try + { + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.GetMarshalSizeMaxUnsafe(t_winRtMarshalProxy, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); + } + catch (Exception ex) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); + } } - public unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int MarshalInterface(void* thisPtr, void* pStm, Guid* riid, void* pv, uint dwDestContext, void* pvDestContext, uint mshlflags) { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetMarshalSizeMax_1(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); -#endif - GC.KeepAlive(_obj); + try + { + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.MarshalInterfaceUnsafe(t_winRtMarshalProxy, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); + } + catch (Exception ex) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); + } } - public unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int UnmarshalInterface(void* thisPtr, void* pStm, Guid* riid, void** ppv) { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])(thisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.MarshalInterface_2(ThisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); -#endif - GC.KeepAlive(_obj); - } + *ppv = null; - public unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) - { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])(thisPtr, pStm, riid, ppv)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.UnmarshalInterface_3(ThisPtr, pStm, riid, ppv)); -#endif - GC.KeepAlive(_obj); + try + { + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.UnmarshalInterfaceUnsafe(t_winRtMarshalProxy, pStm, riid, ppv); + } + catch (Exception ex) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); + } } - public unsafe void ReleaseMarshalData(IntPtr pStm) + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int ReleaseMarshalData(void* thisPtr, void* pStm) { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[7])(thisPtr, pStm)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.ReleaseMarshalData_4(ThisPtr, pStm)); -#endif - GC.KeepAlive(_obj); + try + { + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.ReleaseMarshalDataUnsafe(t_winRtMarshalProxy, pStm); + } + catch (Exception ex) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); + } } - public unsafe void DisconnectObject(uint dwReserved) + /// + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + private static int DisconnectObject(void* thisPtr, uint dwReserved) { -#if NET - IntPtr thisPtr = ThisPtr; - Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[8])(thisPtr, dwReserved)); -#else - Marshal.ThrowExceptionForHR(_obj.Vftbl.DisconnectObject_5(ThisPtr, dwReserved)); -#endif - GC.KeepAlive(_obj); + try + { + EnsureHasMarshalProxy(); + return IBufferMarshalVftbl.DisconnectObjectUnsafe(t_winRtMarshalProxy, dwReserved); + } + catch (Exception ex) + { + return RestrictedErrorInfoExceptionMarshaller.ConvertToUnmanaged(ex); + } } } - -#if !NET - internal static unsafe class IMarshal_Delegates - { - public delegate int GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid); - public delegate int GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize); - public delegate int MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags); - public delegate int UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv); - public delegate int ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm); - public delegate int DisconnectObject_5(IntPtr thisPtr, uint dwReserved); - } -#endif } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/IMemoryBufferByteAccess.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMemoryBufferByteAccess.cs new file mode 100644 index 000000000..f5a0041c4 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMemoryBufferByteAccess.cs @@ -0,0 +1,20 @@ +namespace ABI.Windows.Storage.Streams +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct IMemoryBufferByteAccessVftbl + { + public delegate* unmanaged[MemberFunction] QueryInterface; + public delegate* unmanaged[MemberFunction] AddRef; + public delegate* unmanaged[MemberFunction] Release; + public delegate* unmanaged[MemberFunction] GetBuffer; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetBufferUnsafe( + void* thisPtr, + byte** result, + uint* capacity) + { + return ((IMemoryBufferByteAccessVftbl*)*(void***)thisPtr)->GetBuffer(thisPtr, result, capacity); + } + } +} diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs index 735682be0..14e735cea 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs @@ -1,462 +1,456 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace System.IO -{ - using System.Diagnostics; - using System.IO; - - using System.Runtime.InteropServices; - using System.Threading.Tasks; - using System.Threading; - using global::Windows.Foundation; - using global::Windows.Storage.Streams; - using System.Diagnostics.CodeAnalysis; - /// - /// An wrapper for a managed stream that implements all WinRT stream operations. - /// This class must not implement any WinRT stream interfaces directly. - /// We never create instances of this class directly; instead we use classes defined in - /// the region Interface adapters to implement WinRT ifaces and create instances of those types. - /// See comment in that region for technical details. - /// - internal abstract partial class NetFxToWinRtStreamAdapter : IDisposable - { - private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); - private const int RO_E_CLOSED = unchecked((int)0x80000013); - private const int E_NOTIMPL = unchecked((int)0x80004001); - private const int E_INVALIDARG = unchecked((int)0x80070057); - #region Construction - - #region Interface adapters - - // Instances of private types defined in this section will be returned from NetFxToWinRtStreamAdapter.Create(..). - // Depending on the capabilities of the .NET stream for which we need to construct the adapter, we need to return - // an object that can be QIed (COM speak for "cast") to a well-defined set of ifaces. - // E.g, if the specified stream CanRead, but not CanSeek and not CanWrite, then we *must* return an object that - // can be QIed to IInputStream, but *not* IRandomAccessStream and *not* IOutputStream. - // There are two ways to do that: - // - We could explicitly implement ICustomQueryInterface and respond to QI requests by analyzing the stream capabilities - // - We can use the runtime's ability to do that for us, based on the ifaces the concrete class implements (or does not). - // The latter is much more elegant, and likely also faster. - - - private sealed partial class InputStream : NetFxToWinRtStreamAdapter, IInputStream, IDisposable - { - internal InputStream(Stream stream, StreamReadOperationOptimization readOptimization) - : base(stream, readOptimization) - { - } - } - - - private sealed partial class OutputStream : NetFxToWinRtStreamAdapter, IOutputStream, IDisposable - { - internal OutputStream(Stream stream, StreamReadOperationOptimization readOptimization) - : base(stream, readOptimization) - { - } - } - - - private sealed partial class RandomAccessStream : NetFxToWinRtStreamAdapter, IRandomAccessStream, IInputStream, IOutputStream, IDisposable - { - internal RandomAccessStream(Stream stream, StreamReadOperationOptimization readOptimization) - : base(stream, readOptimization) - { - } - } - - - private sealed partial class InputOutputStream : NetFxToWinRtStreamAdapter, IInputStream, IOutputStream, IDisposable - { - internal InputOutputStream(Stream stream, StreamReadOperationOptimization readOptimization) - : base(stream, readOptimization) - { - } - } - - #endregion Interface adapters - - // We may want to define different behaviour for different types of streams. - // For instance, ReadAsync treats MemoryStream special for performance reasons. - // The enum 'StreamReadOperationOptimization' describes the read optimization to employ for a - // given NetFxToWinRtStreamAdapter instance. In future, we might define other enums to follow a - // similar pattern, e.g. 'StreamWriteOperationOptimization' or 'StreamFlushOperationOptimization'. - private enum StreamReadOperationOptimization - { - AbstractStream = 0, MemoryStream - } - - - internal static NetFxToWinRtStreamAdapter Create(Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - StreamReadOperationOptimization readOptimization = StreamReadOperationOptimization.AbstractStream; - if (stream.CanRead) - readOptimization = DetermineStreamReadOptimization(stream); - - NetFxToWinRtStreamAdapter adapter; - - if (stream.CanSeek) - adapter = new RandomAccessStream(stream, readOptimization); - - else if (stream.CanRead && stream.CanWrite) - adapter = new InputOutputStream(stream, readOptimization); - - else if (stream.CanRead) - adapter = new InputStream(stream, readOptimization); - - else if (stream.CanWrite) - adapter = new OutputStream(stream, readOptimization); - - else - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_NotSufficientCapabilitiesToConvertToWinRtStream); - - return adapter; - } - - - private static StreamReadOperationOptimization DetermineStreamReadOptimization(Stream stream) - { - Debug.Assert(stream != null); - - if (CanApplyReadMemoryStreamOptimization(stream)) - return StreamReadOperationOptimization.MemoryStream; - - return StreamReadOperationOptimization.AbstractStream; - } - - - private static bool CanApplyReadMemoryStreamOptimization(Stream stream) - { - MemoryStream memStream = stream as MemoryStream; - if (memStream == null) - return false; - - ArraySegment arrSeg; - return memStream.TryGetBuffer(out arrSeg); - } - - - private NetFxToWinRtStreamAdapter(Stream stream, StreamReadOperationOptimization readOptimization) - { - Debug.Assert(stream != null); - Debug.Assert(stream.CanRead || stream.CanWrite || stream.CanSeek); - Debug.Assert(!stream.CanRead || (stream.CanRead && this is IInputStream)); - Debug.Assert(!stream.CanWrite || (stream.CanWrite && this is IOutputStream)); - Debug.Assert(!stream.CanSeek || (stream.CanSeek && this is IRandomAccessStream)); - - _readOptimization = readOptimization; - _managedStream = stream; - } - - #endregion Construction - - - #region Instance variables - - private Stream _managedStream = null; - private bool _leaveUnderlyingStreamOpen = true; - private readonly StreamReadOperationOptimization _readOptimization; - - #endregion Instance variables - - - #region Tools and Helpers - - /// - /// We keep tables for mappings between managed and WinRT streams to make sure to always return the same adapter for a given underlying stream. - /// However, in order to avoid global locks on those tables, several instances of this type may be created and then can race to be entered - /// into the appropriate map table. All except for the winning instances will be thrown away. However, we must ensure that when the losers are - /// finalized, they do not dispose the underlying stream. To ensure that, we must call this method on the winner to notify it that it is safe to - /// dispose the underlying stream. - /// - internal void SetWonInitializationRace() - { - _leaveUnderlyingStreamOpen = false; - } - - - public Stream GetManagedStream() - { - return _managedStream; - } - - - private Stream EnsureNotDisposed() - { - Stream str = _managedStream; - - if (str == null) - { - ObjectDisposedException ex = new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); - ex.SetHResult(RO_E_CLOSED); - throw ex; - } - - return str; - } - - #endregion Tools and Helpers - - - #region Common public interface - - /// Implements IDisposable.Dispose (IClosable.Close in WinRT) - void IDisposable.Dispose() - { - Stream str = _managedStream; - if (str == null) - return; - - _managedStream = null; - - if (!_leaveUnderlyingStreamOpen) - str.Dispose(); +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.IO +{ + using System.Diagnostics; + using System.IO; + + using System.Runtime.InteropServices; + using System.Threading.Tasks; + using System.Threading; + using global::Windows.Foundation; + using global::Windows.Storage.Streams; + using System.Diagnostics.CodeAnalysis; + /// + /// An wrapper for a managed stream that implements all WinRT stream operations. + /// This class must not implement any WinRT stream interfaces directly. + /// We never create instances of this class directly; instead we use classes defined in + /// the region Interface adapters to implement WinRT ifaces and create instances of those types. + /// See comment in that region for technical details. + /// + internal abstract partial class NetFxToWinRtStreamAdapter : IDisposable + { + private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); + private const int RO_E_CLOSED = unchecked((int)0x80000013); + private const int E_NOTIMPL = unchecked((int)0x80004001); + private const int E_INVALIDARG = unchecked((int)0x80070057); + #region Construction + + #region Interface adapters + + // Instances of private types defined in this section will be returned from NetFxToWinRtStreamAdapter.Create(..). + // Depending on the capabilities of the .NET stream for which we need to construct the adapter, we need to return + // an object that can be QIed (COM speak for "cast") to a well-defined set of ifaces. + // E.g, if the specified stream CanRead, but not CanSeek and not CanWrite, then we *must* return an object that + // can be QIed to IInputStream, but *not* IRandomAccessStream and *not* IOutputStream. + // There are two ways to do that: + // - We could explicitly implement ICustomQueryInterface and respond to QI requests by analyzing the stream capabilities + // - We can use the runtime's ability to do that for us, based on the ifaces the concrete class implements (or does not). + // The latter is much more elegant, and likely also faster. + + + private sealed partial class InputStream : NetFxToWinRtStreamAdapter, IInputStream, IDisposable + { + internal InputStream(Stream stream, StreamReadOperationOptimization readOptimization) + : base(stream, readOptimization) + { + } } - #endregion Common public interface - - - #region IInputStream public interface - -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) - { - if (buffer == null) - { - // Mapped to E_POINTER. - throw new ArgumentNullException(nameof(buffer)); - } - - if (count < 0 || int.MaxValue < count) - { - ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(count)); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - if (buffer.Capacity < count) - { - ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - if (!(options == InputStreamOptions.None || options == InputStreamOptions.Partial || options == InputStreamOptions.ReadAhead)) - { - ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(options), - global::Windows.Storage.Streams.SR.ArgumentOutOfRange_InvalidInputStreamOptionsEnumValue); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - Stream str = EnsureNotDisposed(); - - IAsyncOperationWithProgress readAsyncOperation; - switch (_readOptimization) - { - case StreamReadOperationOptimization.MemoryStream: - readAsyncOperation = StreamOperationsImplementation.ReadAsync_MemoryStream(str, buffer, count); - break; - - case StreamReadOperationOptimization.AbstractStream: - readAsyncOperation = StreamOperationsImplementation.ReadAsync_AbstractStream(str, buffer, count, options); - break; - - // Use this pattern to add more optimisation options if necessary: - //case StreamReadOperationOptimization.XxxxStream: - // readAsyncOperation = StreamOperationsImplementation.ReadAsync_XxxxStream(str, buffer, count, options); - // break; - - default: - Debug.Fail("We should never get here. Someone forgot to handle an input stream optimisation option."); - readAsyncOperation = null; - break; - } - - return readAsyncOperation; + + private sealed partial class OutputStream : NetFxToWinRtStreamAdapter, IOutputStream, IDisposable + { + internal OutputStream(Stream stream, StreamReadOperationOptimization readOptimization) + : base(stream, readOptimization) + { + } } - #endregion IInputStream public interface - - - #region IOutputStream public interface - -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public IAsyncOperationWithProgress WriteAsync(IBuffer buffer) - { - if (buffer == null) - { - // Mapped to E_POINTER. - throw new ArgumentNullException(nameof(buffer)); - } - - if (buffer.Capacity < buffer.Length) - { - ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferLengthExceedsCapacity); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - Stream str = EnsureNotDisposed(); - return StreamOperationsImplementation.WriteAsync_AbstractStream(str, buffer); + + private sealed partial class RandomAccessStream : NetFxToWinRtStreamAdapter, IRandomAccessStream, IInputStream, IOutputStream, IDisposable + { + internal RandomAccessStream(Stream stream, StreamReadOperationOptimization readOptimization) + : base(stream, readOptimization) + { + } } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public IAsyncOperation FlushAsync() - { - Stream str = EnsureNotDisposed(); - return StreamOperationsImplementation.FlushAsync_AbstractStream(str); - } - - #endregion IOutputStream public interface - - - #region IRandomAccessStream public interface - - - #region IRandomAccessStream public interface: Not cloning related - - public void Seek(ulong position) - { - if (position > long.MaxValue) - { - ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - Stream str = EnsureNotDisposed(); - long pos = unchecked((long)position); - - Debug.Assert(str != null); - Debug.Assert(str.CanSeek, "The underlying str is expected to support Seek, but it does not."); - Debug.Assert(0 <= pos, "Unexpected pos=" + pos + "."); - - str.Seek(pos, SeekOrigin.Begin); - } - - - public bool CanRead - { - get - { - Stream str = EnsureNotDisposed(); - return str.CanRead; - } - } - - - public bool CanWrite - { - get - { - Stream str = EnsureNotDisposed(); - return str.CanWrite; - } - } - - - public ulong Position - { - get - { - Stream str = EnsureNotDisposed(); - return (ulong)str.Position; - } - } - - - public ulong Size - { - get - { - Stream str = EnsureNotDisposed(); - return (ulong)str.Length; - } - - set - { - if (value > long.MaxValue) - { - ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.IO_CannotSetSizeBeyondInt64MaxValue); - ex.SetHResult(E_INVALIDARG); - throw ex; - } - - Stream str = EnsureNotDisposed(); - - if (!str.CanWrite) - { - InvalidOperationException ex = new InvalidOperationException(global::Windows.Storage.Streams.SR.InvalidOperation_CannotSetStreamSizeCannotWrite); - ex.SetHResult(E_ILLEGAL_METHOD_CALL); - throw ex; - } - - long val = unchecked((long)value); - - Debug.Assert(str != null); - Debug.Assert(str.CanSeek, "The underlying str is expected to support Seek, but it does not."); - Debug.Assert(0 <= val, "Unexpected val=" + val + "."); - - str.SetLength(val); - } - } - - #endregion IRandomAccessStream public interface: Not cloning related - - - #region IRandomAccessStream public interface: Cloning related - - // We do not want to support the cloning-related operation for now. - // They appear to mainly target corner-case scenarios in Windows itself, - // and are (mainly) a historical artefact of abandoned early designs - // for IRandonAccessStream. - // Cloning can be added in future, however, it would be quite complex - // to support it correctly for generic streams. - private static void ThrowCloningNotSupported(string methodName) - { - NotSupportedException nse = new NotSupportedException(string.Format(global::Windows.Storage.Streams.SR.NotSupported_CloningNotSupported, methodName)); - nse.SetHResult(E_NOTIMPL); - throw nse; - } - - - public IRandomAccessStream CloneStream() - { - ThrowCloningNotSupported("CloneStream"); - return null; - } - - - public IInputStream GetInputStreamAt(ulong position) - { - ThrowCloningNotSupported("GetInputStreamAt"); - return null; - } - - - public IOutputStream GetOutputStreamAt(ulong position) - { - ThrowCloningNotSupported("GetOutputStreamAt"); - return null; - } - #endregion IRandomAccessStream public interface: Cloning related - - #endregion IRandomAccessStream public interface - - } // class NetFxToWinRtStreamAdapter -} // namespace - -// NetFxToWinRtStreamAdapter.cs + + private sealed partial class InputOutputStream : NetFxToWinRtStreamAdapter, IInputStream, IOutputStream, IDisposable + { + internal InputOutputStream(Stream stream, StreamReadOperationOptimization readOptimization) + : base(stream, readOptimization) + { + } + } + + #endregion Interface adapters + + // We may want to define different behaviour for different types of streams. + // For instance, ReadAsync treats MemoryStream special for performance reasons. + // The enum 'StreamReadOperationOptimization' describes the read optimization to employ for a + // given NetFxToWinRtStreamAdapter instance. In future, we might define other enums to follow a + // similar pattern, e.g. 'StreamWriteOperationOptimization' or 'StreamFlushOperationOptimization'. + private enum StreamReadOperationOptimization + { + AbstractStream = 0, MemoryStream + } + + + internal static NetFxToWinRtStreamAdapter Create(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + StreamReadOperationOptimization readOptimization = StreamReadOperationOptimization.AbstractStream; + if (stream.CanRead) + readOptimization = DetermineStreamReadOptimization(stream); + + NetFxToWinRtStreamAdapter adapter; + + if (stream.CanSeek) + adapter = new RandomAccessStream(stream, readOptimization); + + else if (stream.CanRead && stream.CanWrite) + adapter = new InputOutputStream(stream, readOptimization); + + else if (stream.CanRead) + adapter = new InputStream(stream, readOptimization); + + else if (stream.CanWrite) + adapter = new OutputStream(stream, readOptimization); + + else + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_NotSufficientCapabilitiesToConvertToWinRtStream); + + return adapter; + } + + + private static StreamReadOperationOptimization DetermineStreamReadOptimization(Stream stream) + { + Debug.Assert(stream != null); + + if (CanApplyReadMemoryStreamOptimization(stream)) + return StreamReadOperationOptimization.MemoryStream; + + return StreamReadOperationOptimization.AbstractStream; + } + + + private static bool CanApplyReadMemoryStreamOptimization(Stream stream) + { + MemoryStream memStream = stream as MemoryStream; + if (memStream == null) + return false; + + ArraySegment arrSeg; + return memStream.TryGetBuffer(out arrSeg); + } + + + private NetFxToWinRtStreamAdapter(Stream stream, StreamReadOperationOptimization readOptimization) + { + Debug.Assert(stream != null); + Debug.Assert(stream.CanRead || stream.CanWrite || stream.CanSeek); + Debug.Assert(!stream.CanRead || (stream.CanRead && this is IInputStream)); + Debug.Assert(!stream.CanWrite || (stream.CanWrite && this is IOutputStream)); + Debug.Assert(!stream.CanSeek || (stream.CanSeek && this is IRandomAccessStream)); + + _readOptimization = readOptimization; + _managedStream = stream; + } + + #endregion Construction + + + #region Instance variables + + private Stream _managedStream = null; + private bool _leaveUnderlyingStreamOpen = true; + private readonly StreamReadOperationOptimization _readOptimization; + + #endregion Instance variables + + + #region Tools and Helpers + + /// + /// We keep tables for mappings between managed and WinRT streams to make sure to always return the same adapter for a given underlying stream. + /// However, in order to avoid global locks on those tables, several instances of this type may be created and then can race to be entered + /// into the appropriate map table. All except for the winning instances will be thrown away. However, we must ensure that when the losers are + /// finalized, they do not dispose the underlying stream. To ensure that, we must call this method on the winner to notify it that it is safe to + /// dispose the underlying stream. + /// + internal void SetWonInitializationRace() + { + _leaveUnderlyingStreamOpen = false; + } + + + public Stream GetManagedStream() + { + return _managedStream; + } + + + private Stream EnsureNotDisposed() + { + Stream str = _managedStream; + + if (str == null) + { + ObjectDisposedException ex = new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); + ex.HResult = RO_E_CLOSED; + throw ex; + } + + return str; + } + + #endregion Tools and Helpers + + + #region Common public interface + + /// Implements IDisposable.Dispose (IClosable.Close in WinRT) + void IDisposable.Dispose() + { + Stream str = _managedStream; + if (str == null) + return; + + _managedStream = null; + + if (!_leaveUnderlyingStreamOpen) + str.Dispose(); + } + + #endregion Common public interface + + + #region IInputStream public interface + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) + { + if (buffer == null) + { + // Mapped to E_POINTER. + throw new ArgumentNullException(nameof(buffer)); + } + + if (count < 0 || int.MaxValue < count) + { + ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(count)); + ex.HResult = E_INVALIDARG; + throw ex; + } + + if (buffer.Capacity < count) + { + ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); + ex.HResult = E_INVALIDARG; + throw ex; + } + + if (!(options == InputStreamOptions.None || options == InputStreamOptions.Partial || options == InputStreamOptions.ReadAhead)) + { + ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(options), + global::Windows.Storage.Streams.SR.ArgumentOutOfRange_InvalidInputStreamOptionsEnumValue); + ex.HResult = E_INVALIDARG; + throw ex; + } + + Stream str = EnsureNotDisposed(); + + IAsyncOperationWithProgress readAsyncOperation; + switch (_readOptimization) + { + case StreamReadOperationOptimization.MemoryStream: + readAsyncOperation = StreamOperationsImplementation.ReadAsync_MemoryStream(str, buffer, count); + break; + + case StreamReadOperationOptimization.AbstractStream: + readAsyncOperation = StreamOperationsImplementation.ReadAsync_AbstractStream(str, buffer, count, options); + break; + + // Use this pattern to add more optimisation options if necessary: + //case StreamReadOperationOptimization.XxxxStream: + // readAsyncOperation = StreamOperationsImplementation.ReadAsync_XxxxStream(str, buffer, count, options); + // break; + + default: + Debug.Fail("We should never get here. Someone forgot to handle an input stream optimisation option."); + readAsyncOperation = null; + break; + } + + return readAsyncOperation; + } + + #endregion IInputStream public interface + + + #region IOutputStream public interface + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public IAsyncOperationWithProgress WriteAsync(IBuffer buffer) + { + if (buffer == null) + { + // Mapped to E_POINTER. + throw new ArgumentNullException(nameof(buffer)); + } + + if (buffer.Capacity < buffer.Length) + { + ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferLengthExceedsCapacity); + ex.HResult = E_INVALIDARG; + throw ex; + } + + Stream str = EnsureNotDisposed(); + return StreamOperationsImplementation.WriteAsync_AbstractStream(str, buffer); + } + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public IAsyncOperation FlushAsync() + { + Stream str = EnsureNotDisposed(); + return StreamOperationsImplementation.FlushAsync_AbstractStream(str); + } + + #endregion IOutputStream public interface + + + #region IRandomAccessStream public interface + + + #region IRandomAccessStream public interface: Not cloning related + + public void Seek(ulong position) + { + if (position > long.MaxValue) + { + ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); + ex.HResult = E_INVALIDARG; + throw ex; + } + + Stream str = EnsureNotDisposed(); + long pos = unchecked((long)position); + + Debug.Assert(str != null); + Debug.Assert(str.CanSeek, "The underlying str is expected to support Seek, but it does not."); + Debug.Assert(0 <= pos, "Unexpected pos=" + pos + "."); + + str.Seek(pos, SeekOrigin.Begin); + } + + + public bool CanRead + { + get + { + Stream str = EnsureNotDisposed(); + return str.CanRead; + } + } + + + public bool CanWrite + { + get + { + Stream str = EnsureNotDisposed(); + return str.CanWrite; + } + } + + + public ulong Position + { + get + { + Stream str = EnsureNotDisposed(); + return (ulong)str.Position; + } + } + + + public ulong Size + { + get + { + Stream str = EnsureNotDisposed(); + return (ulong)str.Length; + } + + set + { + if (value > long.MaxValue) + { + ArgumentException ex = new ArgumentException(global::Windows.Storage.Streams.SR.IO_CannotSetSizeBeyondInt64MaxValue); + ex.HResult = E_INVALIDARG; + throw ex; + } + + Stream str = EnsureNotDisposed(); + + if (!str.CanWrite) + { + InvalidOperationException ex = new InvalidOperationException(global::Windows.Storage.Streams.SR.InvalidOperation_CannotSetStreamSizeCannotWrite); + ex.HResult = E_ILLEGAL_METHOD_CALL; + throw ex; + } + + long val = unchecked((long)value); + + Debug.Assert(str != null); + Debug.Assert(str.CanSeek, "The underlying str is expected to support Seek, but it does not."); + Debug.Assert(0 <= val, "Unexpected val=" + val + "."); + + str.SetLength(val); + } + } + + #endregion IRandomAccessStream public interface: Not cloning related + + + #region IRandomAccessStream public interface: Cloning related + + // We do not want to support the cloning-related operation for now. + // They appear to mainly target corner-case scenarios in Windows itself, + // and are (mainly) a historical artefact of abandoned early designs + // for IRandonAccessStream. + // Cloning can be added in future, however, it would be quite complex + // to support it correctly for generic streams. + private static void ThrowCloningNotSupported(string methodName) + { + NotSupportedException nse = new NotSupportedException(string.Format(global::Windows.Storage.Streams.SR.NotSupported_CloningNotSupported, methodName)); + nse.HResult = E_NOTIMPL; + throw nse; + } + + + public IRandomAccessStream CloneStream() + { + ThrowCloningNotSupported("CloneStream"); + return null; + } + + + public IInputStream GetInputStreamAt(ulong position) + { + ThrowCloningNotSupported("GetInputStreamAt"); + return null; + } + + + public IOutputStream GetOutputStreamAt(ulong position) + { + ThrowCloningNotSupported("GetOutputStreamAt"); + return null; + } + #endregion IRandomAccessStream public interface: Cloning related + + #endregion IRandomAccessStream public interface + + } // class NetFxToWinRtStreamAdapter +} // namespace + +// NetFxToWinRtStreamAdapter.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationAsyncResult.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationAsyncResult.cs index 1d916f226..2bc16f732 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationAsyncResult.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationAsyncResult.cs @@ -16,7 +16,7 @@ namespace System.IO using System.Diagnostics.CodeAnalysis; #region class StreamOperationAsyncResult - internal abstract partial class StreamOperationAsyncResult : IAsyncResult + internal abstract class StreamOperationAsyncResult : IAsyncResult { private readonly AsyncCallback _userCompletionCallback = null; private readonly object _userAsyncStateInfo = null; @@ -177,7 +177,7 @@ internal void CloseStreamOperation() internal abstract void ProcessConcreteCompletedOperation(IAsyncInfo completedOperation, out long bytesCompleted); - private static void ProcessCompletedOperation_InvalidOperationThrowHelper(ExceptionDispatchInfo errInfo, string errMsg) + private static void ProcessCompletedOperation_InvalidOperationThrowHelper(ExceptionDispatchInfo errInfo, string errMsg) { Exception errInfosrc = (errInfo == null) ? null : errInfo.SourceException; @@ -284,7 +284,7 @@ internal void StreamOperationCompletedCallback(IAsyncInfo completedOperation, As private void ThrowWithIOExceptionDispatchInfo(Exception e) { - WinRtIOHelper.NativeExceptionToIOExceptionInfo(WinRT.ExceptionHelpers.AttachRestrictedErrorInfo(_completedOperation.ErrorCode)).Throw(); + WinRtIOHelper.NativeExceptionToIOExceptionInfo(RestrictedErrorInfo.AttachErrorInfo(_completedOperation.ErrorCode)).Throw(); } } // class StreamOperationAsyncResult diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs index 018ec23e8..7e283bccc 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs @@ -1,255 +1,246 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace Windows.Storage.Streams -{ - using global::System.Diagnostics; - using global::System.IO; - - using global::System.Runtime.InteropServices; - using global::System.Threading.Tasks; - using global::System.Threading; - using global::System.Runtime.InteropServices.WindowsRuntime; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace Windows.Storage.Streams +{ + using global::System.Diagnostics; + using global::System.IO; + + using global::System.Runtime.InteropServices; + using global::System.Threading.Tasks; + using global::System.Threading; + using global::System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; - /// Depending on the concrete type of the stream managed by a NetFxToWinRtStreamAdapter, - /// we want the ReadAsync / WriteAsync / FlushAsync / etc. operation to be implemented - /// differently. This is for best performance as we can take advantage of the specifics of particular stream - /// types. For instance, ReadAsync currently has a special implementation for memory streams. - /// Moreover, knowledge about the actual runtime type of the IBuffer can also help choosing the optimal - /// implementation. This type provides static methods that encapsulate the performance logic and can be used - /// by NetFxToWinRtStreamAdapter. -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - internal static class StreamOperationsImplementation + /// Depending on the concrete type of the stream managed by a NetFxToWinRtStreamAdapter, + /// we want the ReadAsync / WriteAsync / FlushAsync / etc. operation to be implemented + /// differently. This is for best performance as we can take advantage of the specifics of particular stream + /// types. For instance, ReadAsync currently has a special implementation for memory streams. + /// Moreover, knowledge about the actual runtime type of the IBuffer can also help choosing the optimal + /// implementation. This type provides static methods that encapsulate the performance logic and can be used + /// by NetFxToWinRtStreamAdapter. + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + internal static class StreamOperationsImplementation { -#if NET - static StreamOperationsImplementation() + #region ReadAsync implementations + + internal static IAsyncOperationWithProgress ReadAsync_MemoryStream(Stream stream, IBuffer buffer, uint count) + { + Debug.Assert(stream != null); + Debug.Assert(stream is MemoryStream); + Debug.Assert(stream.CanRead); + Debug.Assert(stream.CanSeek); + Debug.Assert(buffer != null); + Debug.Assert(0 <= count); + Debug.Assert(count <= int.MaxValue); + Debug.Assert(count <= buffer.Capacity); + + // We will return a different buffer to the user backed directly by the memory stream (avoids memory copy). + // This is permitted by the WinRT stream contract. + // The user specified buffer will not have any data put into it: + buffer.Length = 0; + + MemoryStream memStream = stream as MemoryStream; + Debug.Assert(memStream != null); + + try + { + IBuffer dataBuffer = memStream.GetWindowsRuntimeBuffer((int)memStream.Position, (int)count); + if (dataBuffer.Length > 0) + memStream.Seek(dataBuffer.Length, SeekOrigin.Current); + + return AsyncInfo.FromResultWithProgress(dataBuffer); + } + catch (Exception ex) + { + return AsyncInfo.FromExceptionWithProgress(ex); + } + } // ReadAsync_MemoryStream + + + internal static IAsyncOperationWithProgress ReadAsync_AbstractStream(Stream stream, IBuffer buffer, uint count, + InputStreamOptions options) { - _ = StreamTaskAdaptersImplementation.Initialized; + Debug.Assert(stream != null); + Debug.Assert(stream.CanRead); + Debug.Assert(buffer != null); + Debug.Assert(0 <= count); + Debug.Assert(count <= int.MaxValue); + Debug.Assert(count <= buffer.Capacity); + Debug.Assert(options == InputStreamOptions.None || options == InputStreamOptions.Partial || options == InputStreamOptions.ReadAhead); + + int bytesrequested = (int)count; + + // Check if the buffer is our implementation. + // IF YES: In that case, we can read directly into its data array. + // IF NO: The buffer is of unknown implementation. It's not backed by a managed array, but the wrapped stream can only + // read into a managed array. If we used the user-supplied buffer we would need to copy data into it after every read. + // The spec allows to return a buffer instance that is not the same as passed by the user. So, we will create an own + // buffer instance, read data *directly* into the array backing it and then return it to the user. + // Note: the allocation costs we are paying for the new buffer are unavoidable anyway, as we would need to create + // an array to read into either way. + + IBuffer dataBuffer = buffer as WindowsRuntimeBuffer; + + if (dataBuffer == null) + dataBuffer = WindowsRuntimeBuffer.Create((int)Math.Min((uint)int.MaxValue, buffer.Capacity)); + + // This operation delegate will we run inside of the returned IAsyncOperationWithProgress: + Func, Task> readOperation = async (cancelToken, progressListener) => + { + // No bytes read yet: + dataBuffer.Length = 0; + + // Get the buffer backing array: + byte[] data; + int offset; + bool managedBufferAssert = dataBuffer.TryGetUnderlyingData(out data, out offset); + Debug.Assert(managedBufferAssert); + + // Init tracking values: + bool done = cancelToken.IsCancellationRequested; + int bytesCompleted = 0; + + // Loop until EOS, cancelled or read enough data according to options: + while (!done) + { + int bytesread = 0; + + try + { + // Read asynchronously: + bytesread = await stream.ReadAsync(data!, offset + bytesCompleted, bytesrequested - bytesCompleted, cancelToken) + .ConfigureAwait(continueOnCapturedContext: false); + + // We will continue here on a different thread when read async completed: + bytesCompleted += bytesread; + // We will handle a cancelation exception and re-throw all others: + } + catch (OperationCanceledException) + { + // We assume that cancelToken.IsCancellationRequested is has been set and simply proceed. + // (we check cancelToken.IsCancellationRequested later) + Debug.Assert(cancelToken.IsCancellationRequested); + + // This is because if the cancellation came after we read some bytes we want to return the results we got instead + // of an empty cancelled task, so if we have not yet read anything at all, then we can throw cancellation: + if (bytesCompleted == 0 && bytesread == 0) + throw; + } + + // Update target buffer: + dataBuffer.Length = (uint)bytesCompleted; + + Debug.Assert(bytesCompleted <= bytesrequested); + + // Check if we are done: + done = options == InputStreamOptions.Partial // If no complete read was requested, any amount of data is OK + || bytesread == 0 // this implies EndOfStream + || bytesCompleted == bytesrequested // read all requested bytes + || cancelToken.IsCancellationRequested; // operation was cancelled + + // Call user Progress handler: + if (progressListener != null) + progressListener.Report(dataBuffer.Length); + } // while (!done) + + // If we got here, then no error was detected. Return the results buffer: + return dataBuffer; + }; // readOperation + + return AsyncInfo.Run(readOperation); + } // ReadAsync_AbstractStream + + #endregion ReadAsync implementations + + + #region WriteAsync implementations + + internal static IAsyncOperationWithProgress WriteAsync_AbstractStream(Stream stream, IBuffer buffer) + { + Debug.Assert(stream != null); + Debug.Assert(stream.CanWrite); + Debug.Assert(buffer != null); + + // Choose the optimal writing strategy for the kind of buffer supplied: + Func, Task> writeOperation; + byte[] data; + int offset; + + // If buffer is backed by a managed array: + if (buffer.TryGetUnderlyingData(out data, out offset)) + { + writeOperation = async (cancelToken, progressListener) => + { + if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable + return 0; + + Debug.Assert(buffer.Length <= int.MaxValue); + + int bytesToWrite = (int)buffer.Length; + + await stream.WriteAsync(data, offset, bytesToWrite, cancelToken).ConfigureAwait(continueOnCapturedContext: false); + + if (progressListener != null) + progressListener.Report((uint)bytesToWrite); + + return (uint)bytesToWrite; + }; + // Otherwise buffer is of an unknown implementation: + } + else + { + writeOperation = async (cancelToken, progressListener) => + { + if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable + return 0; + + uint bytesToWrite = buffer.Length; + Stream dataStream = buffer.AsStream(); + + int buffSize = 0x4000; + if (bytesToWrite < buffSize) + buffSize = (int)bytesToWrite; + + if (buffSize > 0) + await dataStream.CopyToAsync(stream, buffSize, cancelToken).ConfigureAwait(continueOnCapturedContext: false); + + if (progressListener != null) + progressListener.Report((uint)bytesToWrite); + + return (uint)bytesToWrite; + }; + } // if-else + + // Construct and run the async operation: + return AsyncInfo.Run(writeOperation); + } // WriteAsync_AbstractStream + + #endregion WriteAsync implementations + + + #region FlushAsync implementations + + internal static IAsyncOperation FlushAsync_AbstractStream(Stream stream) + { + Debug.Assert(stream != null); + Debug.Assert(stream.CanWrite); + + Func> flushOperation = async (cancelToken) => + { + if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable + return false; + + await stream.FlushAsync(cancelToken).ConfigureAwait(continueOnCapturedContext: false); + return true; + }; + + // Construct and run the async operation: + return AsyncInfo.Run(flushOperation); } -#endif - - #region ReadAsync implementations - - internal static IAsyncOperationWithProgress ReadAsync_MemoryStream(Stream stream, IBuffer buffer, uint count) - { - Debug.Assert(stream != null); - Debug.Assert(stream is MemoryStream); - Debug.Assert(stream.CanRead); - Debug.Assert(stream.CanSeek); - Debug.Assert(buffer != null); - Debug.Assert(0 <= count); - Debug.Assert(count <= int.MaxValue); - Debug.Assert(count <= buffer.Capacity); - - // We will return a different buffer to the user backed directly by the memory stream (avoids memory copy). - // This is permitted by the WinRT stream contract. - // The user specified buffer will not have any data put into it: - buffer.Length = 0; - - MemoryStream memStream = stream as MemoryStream; - Debug.Assert(memStream != null); - - try - { - IBuffer dataBuffer = memStream.GetWindowsRuntimeBuffer((int)memStream.Position, (int)count); - if (dataBuffer.Length > 0) - memStream.Seek(dataBuffer.Length, SeekOrigin.Current); - - return AsyncInfo.FromResultWithProgress(dataBuffer); - } - catch (Exception ex) - { - return AsyncInfo.FromExceptionWithProgress(ex); - } - } // ReadAsync_MemoryStream - - - internal static IAsyncOperationWithProgress ReadAsync_AbstractStream(Stream stream, IBuffer buffer, uint count, - InputStreamOptions options) - { - Debug.Assert(stream != null); - Debug.Assert(stream.CanRead); - Debug.Assert(buffer != null); - Debug.Assert(0 <= count); - Debug.Assert(count <= int.MaxValue); - Debug.Assert(count <= buffer.Capacity); - Debug.Assert(options == InputStreamOptions.None || options == InputStreamOptions.Partial || options == InputStreamOptions.ReadAhead); - - int bytesrequested = (int)count; - - // Check if the buffer is our implementation. - // IF YES: In that case, we can read directly into its data array. - // IF NO: The buffer is of unknown implementation. It's not backed by a managed array, but the wrapped stream can only - // read into a managed array. If we used the user-supplied buffer we would need to copy data into it after every read. - // The spec allows to return a buffer instance that is not the same as passed by the user. So, we will create an own - // buffer instance, read data *directly* into the array backing it and then return it to the user. - // Note: the allocation costs we are paying for the new buffer are unavoidable anyway, as we would need to create - // an array to read into either way. - - IBuffer dataBuffer = buffer as WindowsRuntimeBuffer; - - if (dataBuffer == null) - dataBuffer = WindowsRuntimeBuffer.Create((int)Math.Min((uint)int.MaxValue, buffer.Capacity)); - - // This operation delegate will we run inside of the returned IAsyncOperationWithProgress: - Func, Task> readOperation = async (cancelToken, progressListener) => - { - // No bytes read yet: - dataBuffer.Length = 0; - - // Get the buffer backing array: - byte[] data; - int offset; - bool managedBufferAssert = dataBuffer.TryGetUnderlyingData(out data, out offset); - Debug.Assert(managedBufferAssert); - - // Init tracking values: - bool done = cancelToken.IsCancellationRequested; - int bytesCompleted = 0; - - // Loop until EOS, cancelled or read enough data according to options: - while (!done) - { - int bytesread = 0; - - try - { - // Read asynchronously: - bytesread = await stream.ReadAsync(data!, offset + bytesCompleted, bytesrequested - bytesCompleted, cancelToken) - .ConfigureAwait(continueOnCapturedContext: false); - - // We will continue here on a different thread when read async completed: - bytesCompleted += bytesread; - // We will handle a cancelation exception and re-throw all others: - } - catch (OperationCanceledException) - { - // We assume that cancelToken.IsCancellationRequested is has been set and simply proceed. - // (we check cancelToken.IsCancellationRequested later) - Debug.Assert(cancelToken.IsCancellationRequested); - - // This is because if the cancellation came after we read some bytes we want to return the results we got instead - // of an empty cancelled task, so if we have not yet read anything at all, then we can throw cancellation: - if (bytesCompleted == 0 && bytesread == 0) - throw; - } - - // Update target buffer: - dataBuffer.Length = (uint)bytesCompleted; - - Debug.Assert(bytesCompleted <= bytesrequested); - - // Check if we are done: - done = options == InputStreamOptions.Partial // If no complete read was requested, any amount of data is OK - || bytesread == 0 // this implies EndOfStream - || bytesCompleted == bytesrequested // read all requested bytes - || cancelToken.IsCancellationRequested; // operation was cancelled - - // Call user Progress handler: - if (progressListener != null) - progressListener.Report(dataBuffer.Length); - } // while (!done) - - // If we got here, then no error was detected. Return the results buffer: - return dataBuffer; - }; // readOperation - - return AsyncInfo.Run(readOperation); - } // ReadAsync_AbstractStream - - #endregion ReadAsync implementations - - - #region WriteAsync implementations - - internal static IAsyncOperationWithProgress WriteAsync_AbstractStream(Stream stream, IBuffer buffer) - { - Debug.Assert(stream != null); - Debug.Assert(stream.CanWrite); - Debug.Assert(buffer != null); - - // Choose the optimal writing strategy for the kind of buffer supplied: - Func, Task> writeOperation; - byte[] data; - int offset; - - // If buffer is backed by a managed array: - if (buffer.TryGetUnderlyingData(out data, out offset)) - { - writeOperation = async (cancelToken, progressListener) => - { - if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable - return 0; - - Debug.Assert(buffer.Length <= int.MaxValue); - - int bytesToWrite = (int)buffer.Length; - - await stream.WriteAsync(data, offset, bytesToWrite, cancelToken).ConfigureAwait(continueOnCapturedContext: false); - - if (progressListener != null) - progressListener.Report((uint)bytesToWrite); - - return (uint)bytesToWrite; - }; - // Otherwise buffer is of an unknown implementation: - } - else - { - writeOperation = async (cancelToken, progressListener) => - { - if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable - return 0; - - uint bytesToWrite = buffer.Length; - Stream dataStream = buffer.AsStream(); - - int buffSize = 0x4000; - if (bytesToWrite < buffSize) - buffSize = (int)bytesToWrite; - - if (buffSize > 0) - await dataStream.CopyToAsync(stream, buffSize, cancelToken).ConfigureAwait(continueOnCapturedContext: false); - - if (progressListener != null) - progressListener.Report((uint)bytesToWrite); - - return (uint)bytesToWrite; - }; - } // if-else - - // Construct and run the async operation: - return AsyncInfo.Run(writeOperation); - } // WriteAsync_AbstractStream - - #endregion WriteAsync implementations - - - #region FlushAsync implementations - - internal static IAsyncOperation FlushAsync_AbstractStream(Stream stream) - { - Debug.Assert(stream != null); - Debug.Assert(stream.CanWrite); - - Func> flushOperation = async (cancelToken) => - { - if (cancelToken.IsCancellationRequested) // CancellationToken is non-nullable - return false; - - await stream.FlushAsync(cancelToken).ConfigureAwait(continueOnCapturedContext: false); - return true; - }; - - // Construct and run the async operation: - return AsyncInfo.Run(flushOperation); - } - #endregion FlushAsync implementations - - } // class StreamOperationsImplementation -} // namespace + #endregion FlushAsync implementations + + } // class StreamOperationsImplementation +} // namespace diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs deleted file mode 100644 index 77ea52140..000000000 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs +++ /dev/null @@ -1,610 +0,0 @@ -#if NET -namespace Windows.Storage.Streams -{ - // Given we do not do automatic lookup table generation for projections, this class defines - // one for the scenarios which are used by StreamOperationsImplementations for its task adapters. - internal static class StreamTaskAdaptersImplementation - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - global::WinRT.ComWrappersSupport.RegisterTypeComInterfaceEntriesLookup(LookupVtableEntries); - global::WinRT.ComWrappersSupport.RegisterTypeRuntimeClassNameLookup(new Func(LookupRuntimeClassName)); - return true; - } - - private static ComWrappers.ComInterfaceEntry[] LookupVtableEntries(Type type) - { - var typeName = type.ToString(); - if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[Windows.Storage.Streams.IBuffer,System.UInt32]") - { - _ = IAsyncOperationWithProgress_IBuffer_uint.Initialized; - - return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] - { - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.AbiToProjectionVftablePtr - }, - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr - }, - }; - } - else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[System.UInt32,System.UInt32]") - { - _ = IAsyncOperationWithProgress_uint_uint.Initialized; - - return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] - { - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.AbiToProjectionVftablePtr - }, - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr - }, - }; - } - else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationAdapter`1[System.Boolean]") - { - _ = IAsyncOperation_bool.Initialized; - - return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] - { - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncOperationMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncOperationMethods.AbiToProjectionVftablePtr - }, - new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry - { - IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, - Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr - }, - }; - } - - return default; - } - - private static string LookupRuntimeClassName(Type type) - { - var typeName = type.ToString(); - if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[Windows.Storage.Streams.IBuffer,System.UInt32]") - { - return "Windows.Foundation.IAsyncOperationWithProgress`2"; - } - else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[System.UInt32,System.UInt32]") - { - return "Windows.Foundation.IAsyncOperationWithProgress`2"; - } - else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationAdapter`1[System.Boolean]") - { - return "Windows.Foundation.IAsyncOperation`1"; - } - - return default; - } - - private static class IAsyncOperationWithProgress_uint_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.InitCcw( - &Do_Abi_put_Progress_0, - &Do_Abi_get_Progress_1, - &Do_Abi_put_Completed_2, - &Do_Abi_get_Completed_3, - &Do_Abi_GetResults_4 - ); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr, uint* __return_value__) - { - uint ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_GetResults_4(thisPtr); - *__return_value__ = ____return_value__; - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_put_Progress_0(IntPtr thisPtr, IntPtr handler) - { - _ = AsyncOperationProgressHandler_uint_uint.Initialized; - try - { - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Progress_0( - thisPtr, - global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromAbi(handler)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_get_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) - { - _ = AsyncOperationProgressHandler_uint_uint.Initialized; - global::Windows.Foundation.AsyncOperationProgressHandler ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Progress_1(thisPtr); - *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_put_Completed_2(IntPtr thisPtr, IntPtr handler) - { - _ = AsyncOperationWithProgressCompletedHandler_uint_uint.Initialized; - try - { - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Completed_2( - thisPtr, - global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromAbi(handler)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_get_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) - { - _ = AsyncOperationWithProgressCompletedHandler_uint_uint.Initialized; - global::Windows.Foundation.AsyncOperationWithProgressCompletedHandler ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Completed_3(thisPtr); - *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - private static class AsyncOperationProgressHandler_uint_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitCcw(&Do_Abi_Invoke); - _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitRcwHelper(&Invoke); - return true; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, uint progressInfo) - { - try - { - global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.Abi_Invoke( - thisPtr, - MarshalInterface>.FromAbi(asyncInfo), - progressInfo); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - private static unsafe void Invoke(IObjectReference objRef, global::Windows.Foundation.IAsyncOperationWithProgress asyncInfo, uint progressInfo) - { - IntPtr ThisPtr = objRef.ThisPtr; - ObjectReferenceValue __asyncInfo = default; - - try - { - __asyncInfo = MarshalInterface>.CreateMarshaler2( - asyncInfo, - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID); - IntPtr abiAsyncInfo = MarshalInspectable.GetAbi(__asyncInfo); - - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( - ThisPtr, - abiAsyncInfo, - progressInfo)); - GC.KeepAlive(objRef); - } - finally - { - MarshalInterface>.DisposeMarshaler(__asyncInfo); - - } - } - } - - private static class AsyncOperationWithProgressCompletedHandler_uint_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods. - InitCcw(&Do_Abi_Invoke); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) - { - try - { - global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods.Abi_Invoke( - thisPtr, - MarshalInterface>.FromAbi(asyncInfo), - asyncStatus); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - private static class IAsyncOperationWithProgress_IBuffer_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.InitCcw( - &Do_Abi_put_Progress_0, - &Do_Abi_get_Progress_1, - &Do_Abi_put_Completed_2, - &Do_Abi_get_Completed_3, - &Do_Abi_GetResults_4 - ); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr, IntPtr* __return_value__) - { - Windows.Storage.Streams.IBuffer ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods. - Do_Abi_GetResults_4(thisPtr); - *__return_value__ = MarshalInterface.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_put_Progress_0(IntPtr thisPtr, IntPtr handler) - { - _ = AsyncOperationProgressHandler_IBuffer_uint.Initialized; - try - { - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Progress_0( - thisPtr, - global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromAbi(handler)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_get_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) - { - _ = AsyncOperationProgressHandler_IBuffer_uint.Initialized; - global::Windows.Foundation.AsyncOperationProgressHandler ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Progress_1(thisPtr); - *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_put_Completed_2(IntPtr thisPtr, IntPtr handler) - { - _ = AsyncOperationWithProgressCompletedHandler_IBuffer_uint.Initialized; - try - { - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Completed_2( - thisPtr, - global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromAbi(handler)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_get_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) - { - _ = AsyncOperationWithProgressCompletedHandler_IBuffer_uint.Initialized; - global::Windows.Foundation.AsyncOperationWithProgressCompletedHandler ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Completed_3(thisPtr); - *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - private static class AsyncOperationProgressHandler_IBuffer_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitCcw(&Do_Abi_Invoke); - _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitRcwHelper(&Invoke); - return true; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, uint progressInfo) - { - try - { - global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.Abi_Invoke( - thisPtr, - MarshalInterface>.FromAbi(asyncInfo), - progressInfo); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - private static unsafe void Invoke( - IObjectReference objRef, - global::Windows.Foundation.IAsyncOperationWithProgress asyncInfo, - uint progressInfo) - { - IntPtr ThisPtr = objRef.ThisPtr; - ObjectReferenceValue __asyncInfo = default; - - try - { - __asyncInfo = MarshalInterface>.CreateMarshaler2( - asyncInfo, - global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID); - IntPtr abiAsyncInfo = MarshalInspectable.GetAbi(__asyncInfo); - - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( - ThisPtr, - abiAsyncInfo, - progressInfo)); - GC.KeepAlive(objRef); - } - finally - { - MarshalInterface>.DisposeMarshaler(__asyncInfo); - - } - } - } - - private static class AsyncOperationWithProgressCompletedHandler_IBuffer_uint - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods. - InitCcw(&Do_Abi_Invoke); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) - { - try - { - global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods.Abi_Invoke( - thisPtr, - MarshalInterface>.FromAbi(asyncInfo), - asyncStatus); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - private static class IAsyncOperation_bool - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.IAsyncOperationMethods.InitCcw( - &Do_Abi_put_Completed_0, - &Do_Abi_get_Completed_1, - &Do_Abi_GetResults_2 - ); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_GetResults_2(IntPtr thisPtr, byte* __return_value__) - { - bool ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_GetResults_2(thisPtr); - *__return_value__ = (byte)(____return_value__ ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_put_Completed_0(IntPtr thisPtr, IntPtr handler) - { - _ = AsyncOperationCompletedHandler_bool.Initialized; - try - { - global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_put_Completed_0(thisPtr, global::ABI.Windows.Foundation.AsyncOperationCompletedHandler.FromAbi(handler)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_get_Completed_1(IntPtr thisPtr, IntPtr* __return_value__) - { - _ = AsyncOperationCompletedHandler_bool.Initialized; - global::Windows.Foundation.AsyncOperationCompletedHandler ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_get_Completed_1(thisPtr); - *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationCompletedHandler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - private static class AsyncOperationCompletedHandler_bool - { - private static readonly bool _initialized = Init(); - internal static bool Initialized => _initialized; - - private static unsafe bool Init() - { - return global::ABI.Windows.Foundation.AsyncOperationCompletedHandlerMethods.InitCcw( - &Do_Abi_Invoke - ); - } - - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) - { - try - { - global::ABI.Windows.Foundation.AsyncOperationCompletedHandlerMethods.Abi_Invoke( - thisPtr, - MarshalInterface>.FromAbi(asyncInfo), - asyncStatus); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - } -} -#endif \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs new file mode 100644 index 000000000..424ba1e9a --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs @@ -0,0 +1,45 @@ +namespace ABI.Windows.Storage.Streams +{ + internal static class WellKnownStreamInterfaceIIDs + { + public static ref readonly Guid IID_IBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xEF, 0x0F, 0x5A, 0x90, 0x53, 0xBC, 0xDF, 0x11, 0x8C, 0x49, 0x00, 0x1E, 0x4F, 0xC6, 0x86, 0xDA + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + public static ref readonly Guid IID_IMarshal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + public static ref readonly Guid IID_IMemoryBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x35, 0x32, 0x0D, 0x5B, 0xBA, 0x4D, 0x44, 0x4D, 0x86, 0x5E, 0x8F, 0x1D, 0x0E, 0x4F, 0xD0, 0x4D + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WinRtToNetFxStreamAdapter.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WinRtToNetFxStreamAdapter.cs index 35939e896..0cd3d46f6 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WinRtToNetFxStreamAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WinRtToNetFxStreamAdapter.cs @@ -1,818 +1,804 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace Windows.Storage.Streams -{ - using global::System.Diagnostics; - using global::System.Diagnostics.Contracts; - using global::System.IO; - using global::System.Runtime.InteropServices; - using global::System.Runtime.InteropServices.WindowsRuntime; - using global::System.Threading.Tasks; - using global::System.Threading; - using Windows.Foundation; - using global::System.Diagnostics.CodeAnalysis; - /// - /// A Stream used to wrap a Windows Runtime stream to expose it as a managed steam. - /// - internal sealed class WinRtToNetFxStreamAdapter : Stream, IDisposable - { - #region Construction - - internal static WinRtToNetFxStreamAdapter Create(object windowsruntimeStream) - { - if (windowsruntimeStream == null) - throw new ArgumentNullException(nameof(windowsruntimeStream)); - - bool canRead = windowsruntimeStream is IInputStream; - bool canWrite = windowsruntimeStream is IOutputStream; - bool canSeek = windowsruntimeStream is IRandomAccessStream; - - if (!canRead && !canWrite && !canSeek) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_ObjectMustBeWinRtStreamToConvertToNetFxStream); - - // Proactively guard against a non-conforming curstomer implementations: - if (canSeek) - { - IRandomAccessStream iras = (IRandomAccessStream)windowsruntimeStream; - - if (!canRead && iras.CanRead) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InstancesImplementingIRASThatCanReadMustImplementIIS); - - if (!canWrite && iras.CanWrite) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InstancesImplementingIRASThatCanWriteMustImplementIOS); - - if (!iras.CanRead) - canRead = false; - - if (!iras.CanWrite) - canWrite = false; - } - - if (!canRead && !canWrite) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_WinRtStreamCannotReadOrWrite); - - return new WinRtToNetFxStreamAdapter(windowsruntimeStream, canRead, canWrite, canSeek); - } - - - private WinRtToNetFxStreamAdapter(object winRtStream, bool canRead, bool canWrite, bool canSeek) - { - Debug.Assert(winRtStream != null); - Debug.Assert(winRtStream is IInputStream || winRtStream is IOutputStream || winRtStream is IRandomAccessStream); - - Debug.Assert((canSeek && (winRtStream is IRandomAccessStream)) || (!canSeek && !(winRtStream is IRandomAccessStream))); - - Debug.Assert((canRead && (winRtStream is IInputStream)) - || - (!canRead && ( - !(winRtStream is IInputStream) - || - (winRtStream is IRandomAccessStream && !((IRandomAccessStream)winRtStream).CanRead) - )) - ); - - Debug.Assert((canWrite && (winRtStream is IOutputStream)) - || - (!canWrite && ( - !(winRtStream is IOutputStream) - || - (winRtStream is IRandomAccessStream && !((IRandomAccessStream)winRtStream).CanWrite) - )) - ); - - _winRtStream = winRtStream; - _canRead = canRead; - _canWrite = canWrite; - _canSeek = canSeek; - } - - #endregion Construction - - - #region Instance variables - - private byte[] _oneByteBuffer = null; - private bool _leaveUnderlyingStreamOpen = true; - - private object _winRtStream; - private readonly bool _canRead; - private readonly bool _canWrite; - private readonly bool _canSeek; - - #endregion Instance variables - - - #region Tools and Helpers - - /// - /// We keep tables for mappings between managed and WinRT streams to make sure to always return the same adapter for a given underlying stream. - /// However, in order to avoid global locks on those tables, several instances of this type may be created and then can race to be entered - /// into the appropriate map table. All except for the winning instances will be thrown away. However, we must ensure that when the losers are - /// finalized, the do not dispose the underlying stream. To ensure that, we must call this method on the winner to notify it that it is safe to - /// dispose the underlying stream. - /// - internal void SetWonInitializationRace() - { - _leaveUnderlyingStreamOpen = false; - } - - - public TWinRtStream GetWindowsRuntimeStream() where TWinRtStream : class - { - object wrtStr = _winRtStream; - - if (wrtStr == null) - return null; - - Debug.Assert(wrtStr is TWinRtStream, - $"Attempted to get the underlying WinRT stream typed as \"{typeof(TWinRtStream)}\", " + - $"but the underlying WinRT stream cannot be cast to that type. Its actual type is \"{wrtStr.GetType()}\"."); - - return wrtStr as TWinRtStream; - } - - - private byte[] OneByteBuffer - { - get - { - byte[] obb = _oneByteBuffer; - if (obb == null) // benign race for multiple init - _oneByteBuffer = obb = new byte[1]; - return obb; - } - } - - private TWinRtStream EnsureNotDisposed() where TWinRtStream : class - { - object wrtStr = _winRtStream; - - if (wrtStr == null) - throw new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); - - return (wrtStr as TWinRtStream); - } - - - private void EnsureNotDisposed() - { - if (_winRtStream == null) - throw new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); - } - - - private void EnsureCanRead() - { - if (!_canRead) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotReadFromStream); - } - - - private void EnsureCanWrite() - { - if (!_canWrite) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotWriteToStream); - } - - #endregion Tools and Helpers - - - #region Simple overrides - - protected override void Dispose(bool disposing) - { - // WinRT streams should implement IDisposable (IClosable in WinRT), but let's be defensive: - if (disposing && _winRtStream != null && !_leaveUnderlyingStreamOpen) - { - IDisposable disposableWinRtStream = _winRtStream as IDisposable; // benign race on winRtStream - if (disposableWinRtStream != null) - disposableWinRtStream.Dispose(); - } - - _winRtStream = null; - base.Dispose(disposing); - } - - - public override bool CanRead - { - [Pure] - get - { return (_canRead && _winRtStream != null); } - } - - - public override bool CanWrite - { - [Pure] - get - { return (_canWrite && _winRtStream != null); } - } - - - public override bool CanSeek - { - [Pure] - get - { return (_canSeek && _winRtStream != null); } - } - - #endregion Simple overrides - - - #region Length and Position functions - - public override long Length - { - get - { - IRandomAccessStream wrtStr = EnsureNotDisposed(); - - if (!_canSeek) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUseLength_StreamNotSeekable); - - Debug.Assert(wrtStr != null); - - ulong size = wrtStr.Size; - - // These are over 8000 PetaBytes, we do not expect this to happen. However, let's be defensive: - if (size > (ulong)long.MaxValue) - throw new IOException(global::Windows.Storage.Streams.SR.IO_UnderlyingWinRTStreamTooLong_CannotUseLengthOrPosition); - - return unchecked((long)size); - } - } - - - public override long Position - { - get - { - IRandomAccessStream wrtStr = EnsureNotDisposed(); - - if (!_canSeek) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUsePosition_StreamNotSeekable); - - Debug.Assert(wrtStr != null); - - ulong pos = wrtStr.Position; - - // These are over 8000 PetaBytes, we do not expect this to happen. However, let's be defensive: - if (pos > (ulong)long.MaxValue) - throw new IOException(global::Windows.Storage.Streams.SR.IO_UnderlyingWinRTStreamTooLong_CannotUseLengthOrPosition); - - return unchecked((long)pos); - } - - set - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(Position), global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); - - IRandomAccessStream wrtStr = EnsureNotDisposed(); - - if (!_canSeek) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUsePosition_StreamNotSeekable); - - Debug.Assert(wrtStr != null); - - wrtStr.Seek(unchecked((ulong)value)); - } - } - - - public override long Seek(long offset, SeekOrigin origin) - { - IRandomAccessStream wrtStr = EnsureNotDisposed(); - - if (!_canSeek) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotSeekInStream); - - Debug.Assert(wrtStr != null); - - switch (origin) - { - case SeekOrigin.Begin: - { - Position = offset; - return offset; - } - - case SeekOrigin.Current: - { - long curPos = Position; - - if (long.MaxValue - curPos < offset) - throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); - - long newPos = curPos + offset; - - if (newPos < 0) - throw new IOException(global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); - - Position = newPos; - return newPos; - } - - case SeekOrigin.End: - { - ulong size = wrtStr.Size; - long newPos; - - if (size > (ulong)long.MaxValue) - { - if (offset >= 0) - throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); - - Debug.Assert(offset < 0); - - ulong absOffset = (offset == long.MinValue) ? ((ulong)long.MaxValue) + 1 : (ulong)(-offset); - Debug.Assert(absOffset <= size); - - ulong np = size - absOffset; - if (np > (ulong)long.MaxValue) - throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); - - newPos = (long)np; - } - else - { - Debug.Assert(size <= (ulong)long.MaxValue); - - long s = unchecked((long)size); - - if (long.MaxValue - s < offset) - throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); - - newPos = s + offset; - - if (newPos < 0) - throw new IOException(global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); - } - - Position = newPos; - return newPos; - } - - default: - { - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InvalidSeekOrigin, nameof(origin)); - } - } - } - - - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), global::Windows.Storage.Streams.SR.ArgumentOutOfRange_CannotResizeStreamToNegative); - - IRandomAccessStream wrtStr = EnsureNotDisposed(); - - if (!_canSeek) - throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotSeekInStream); - - EnsureCanWrite(); - - Debug.Assert(wrtStr != null); - - wrtStr.Size = unchecked((ulong)value); - - // If the length is set to a value < that the current position, then we need to set the position to that value - // Because we can't directly set the position, we are going to seek to it. - if (wrtStr.Size < wrtStr.Position) - wrtStr.Seek(unchecked((ulong)value)); +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace Windows.Storage.Streams +{ + using global::System.Diagnostics; + using global::System.Diagnostics.Contracts; + using global::System.IO; + using global::System.Runtime.InteropServices; + using global::System.Runtime.InteropServices.WindowsRuntime; + using global::System.Threading.Tasks; + using global::System.Threading; + using Windows.Foundation; + using global::System.Diagnostics.CodeAnalysis; + /// + /// A Stream used to wrap a Windows Runtime stream to expose it as a managed steam. + /// + internal sealed class WinRtToNetFxStreamAdapter : Stream, IDisposable + { + #region Construction + + internal static WinRtToNetFxStreamAdapter Create(object windowsruntimeStream) + { + if (windowsruntimeStream == null) + throw new ArgumentNullException(nameof(windowsruntimeStream)); + + bool canRead = windowsruntimeStream is IInputStream; + bool canWrite = windowsruntimeStream is IOutputStream; + bool canSeek = windowsruntimeStream is IRandomAccessStream; + + if (!canRead && !canWrite && !canSeek) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_ObjectMustBeWinRtStreamToConvertToNetFxStream); + + // Proactively guard against a non-conforming curstomer implementations: + if (canSeek) + { + IRandomAccessStream iras = (IRandomAccessStream)windowsruntimeStream; + + if (!canRead && iras.CanRead) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InstancesImplementingIRASThatCanReadMustImplementIIS); + + if (!canWrite && iras.CanWrite) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InstancesImplementingIRASThatCanWriteMustImplementIOS); + + if (!iras.CanRead) + canRead = false; + + if (!iras.CanWrite) + canWrite = false; + } + + if (!canRead && !canWrite) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_WinRtStreamCannotReadOrWrite); + + return new WinRtToNetFxStreamAdapter(windowsruntimeStream, canRead, canWrite, canSeek); + } + + + private WinRtToNetFxStreamAdapter(object winRtStream, bool canRead, bool canWrite, bool canSeek) + { + Debug.Assert(winRtStream != null); + Debug.Assert(winRtStream is IInputStream || winRtStream is IOutputStream || winRtStream is IRandomAccessStream); + + Debug.Assert((canSeek && (winRtStream is IRandomAccessStream)) || (!canSeek && !(winRtStream is IRandomAccessStream))); + + Debug.Assert((canRead && (winRtStream is IInputStream)) + || + (!canRead && ( + !(winRtStream is IInputStream) + || + (winRtStream is IRandomAccessStream && !((IRandomAccessStream)winRtStream).CanRead) + )) + ); + + Debug.Assert((canWrite && (winRtStream is IOutputStream)) + || + (!canWrite && ( + !(winRtStream is IOutputStream) + || + (winRtStream is IRandomAccessStream && !((IRandomAccessStream)winRtStream).CanWrite) + )) + ); + + _winRtStream = winRtStream; + _canRead = canRead; + _canWrite = canWrite; + _canSeek = canSeek; + } + + #endregion Construction + + + #region Instance variables + + private byte[] _oneByteBuffer = null; + private bool _leaveUnderlyingStreamOpen = true; + + private object _winRtStream; + private readonly bool _canRead; + private readonly bool _canWrite; + private readonly bool _canSeek; + + #endregion Instance variables + + + #region Tools and Helpers + + /// + /// We keep tables for mappings between managed and WinRT streams to make sure to always return the same adapter for a given underlying stream. + /// However, in order to avoid global locks on those tables, several instances of this type may be created and then can race to be entered + /// into the appropriate map table. All except for the winning instances will be thrown away. However, we must ensure that when the losers are + /// finalized, the do not dispose the underlying stream. To ensure that, we must call this method on the winner to notify it that it is safe to + /// dispose the underlying stream. + /// + internal void SetWonInitializationRace() + { + _leaveUnderlyingStreamOpen = false; + } + + + public TWinRtStream GetWindowsRuntimeStream() where TWinRtStream : class + { + object wrtStr = _winRtStream; + + if (wrtStr == null) + return null; + + Debug.Assert(wrtStr is TWinRtStream, + $"Attempted to get the underlying WinRT stream typed as \"{typeof(TWinRtStream)}\", " + + $"but the underlying WinRT stream cannot be cast to that type. Its actual type is \"{wrtStr.GetType()}\"."); + + return wrtStr as TWinRtStream; + } + + + private byte[] OneByteBuffer + { + get + { + byte[] obb = _oneByteBuffer; + if (obb == null) // benign race for multiple init + _oneByteBuffer = obb = new byte[1]; + return obb; + } + } + + private TWinRtStream EnsureNotDisposed() where TWinRtStream : class + { + object wrtStr = _winRtStream; + + if (wrtStr == null) + throw new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); + + return (wrtStr as TWinRtStream); + } + + + private void EnsureNotDisposed() + { + if (_winRtStream == null) + throw new ObjectDisposedException(global::Windows.Storage.Streams.SR.ObjectDisposed_CannotPerformOperation); + } + + + private void EnsureCanRead() + { + if (!_canRead) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotReadFromStream); + } + + + private void EnsureCanWrite() + { + if (!_canWrite) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotWriteToStream); + } + + #endregion Tools and Helpers + + + #region Simple overrides + + protected override void Dispose(bool disposing) + { + // WinRT streams should implement IDisposable (IClosable in WinRT), but let's be defensive: + if (disposing && _winRtStream != null && !_leaveUnderlyingStreamOpen) + { + IDisposable disposableWinRtStream = _winRtStream as IDisposable; // benign race on winRtStream + if (disposableWinRtStream != null) + disposableWinRtStream.Dispose(); + } + + _winRtStream = null; + base.Dispose(disposing); + } + + + public override bool CanRead + { + [Pure] + get + { return (_canRead && _winRtStream != null); } + } + + + public override bool CanWrite + { + [Pure] + get + { return (_canWrite && _winRtStream != null); } + } + + + public override bool CanSeek + { + [Pure] + get + { return (_canSeek && _winRtStream != null); } + } + + #endregion Simple overrides + + + #region Length and Position functions + + public override long Length + { + get + { + IRandomAccessStream wrtStr = EnsureNotDisposed(); + + if (!_canSeek) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUseLength_StreamNotSeekable); + + Debug.Assert(wrtStr != null); + + ulong size = wrtStr.Size; + + // These are over 8000 PetaBytes, we do not expect this to happen. However, let's be defensive: + if (size > (ulong)long.MaxValue) + throw new IOException(global::Windows.Storage.Streams.SR.IO_UnderlyingWinRTStreamTooLong_CannotUseLengthOrPosition); + + return unchecked((long)size); + } + } + + + public override long Position + { + get + { + IRandomAccessStream wrtStr = EnsureNotDisposed(); + + if (!_canSeek) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUsePosition_StreamNotSeekable); + + Debug.Assert(wrtStr != null); + + ulong pos = wrtStr.Position; + + // These are over 8000 PetaBytes, we do not expect this to happen. However, let's be defensive: + if (pos > (ulong)long.MaxValue) + throw new IOException(global::Windows.Storage.Streams.SR.IO_UnderlyingWinRTStreamTooLong_CannotUseLengthOrPosition); + + return unchecked((long)pos); + } + + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(Position), global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); + + IRandomAccessStream wrtStr = EnsureNotDisposed(); + + if (!_canSeek) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotUsePosition_StreamNotSeekable); + + Debug.Assert(wrtStr != null); + + wrtStr.Seek(unchecked((ulong)value)); + } + } + + + public override long Seek(long offset, SeekOrigin origin) + { + IRandomAccessStream wrtStr = EnsureNotDisposed(); + + if (!_canSeek) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotSeekInStream); + + Debug.Assert(wrtStr != null); + + switch (origin) + { + case SeekOrigin.Begin: + { + Position = offset; + return offset; + } + + case SeekOrigin.Current: + { + long curPos = Position; + + if (long.MaxValue - curPos < offset) + throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); + + long newPos = curPos + offset; + + if (newPos < 0) + throw new IOException(global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); + + Position = newPos; + return newPos; + } + + case SeekOrigin.End: + { + ulong size = wrtStr.Size; + long newPos; + + if (size > (ulong)long.MaxValue) + { + if (offset >= 0) + throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); + + Debug.Assert(offset < 0); + + ulong absOffset = (offset == long.MinValue) ? ((ulong)long.MaxValue) + 1 : (ulong)(-offset); + Debug.Assert(absOffset <= size); + + ulong np = size - absOffset; + if (np > (ulong)long.MaxValue) + throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); + + newPos = (long)np; + } + else + { + Debug.Assert(size <= (ulong)long.MaxValue); + + long s = unchecked((long)size); + + if (long.MaxValue - s < offset) + throw new IOException(global::Windows.Storage.Streams.SR.IO_CannotSeekBeyondInt64MaxValue); + + newPos = s + offset; + + if (newPos < 0) + throw new IOException(global::Windows.Storage.Streams.SR.ArgumentOutOfRange_IO_CannotSeekToNegativePosition); + } + + Position = newPos; + return newPos; + } + + default: + { + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InvalidSeekOrigin, nameof(origin)); + } + } + } + + + public override void SetLength(long value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), global::Windows.Storage.Streams.SR.ArgumentOutOfRange_CannotResizeStreamToNegative); + + IRandomAccessStream wrtStr = EnsureNotDisposed(); + + if (!_canSeek) + throw new NotSupportedException(global::Windows.Storage.Streams.SR.NotSupported_CannotSeekInStream); + + EnsureCanWrite(); + + Debug.Assert(wrtStr != null); + + wrtStr.Size = unchecked((ulong)value); + + // If the length is set to a value < that the current position, then we need to set the position to that value + // Because we can't directly set the position, we are going to seek to it. + if (wrtStr.Size < wrtStr.Position) + wrtStr.Seek(unchecked((ulong)value)); + } + + #endregion Length and Position functions + + + #region Reading + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + private IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state, bool usedByBlockingWrapper) + { + // This method is somewhat tricky: We could consider just calling ReadAsync (recall that Task implements IAsyncResult). + // It would be OK for cases where BeginRead is invoked directly by the public user. + // However, in cases where it is invoked by Read to achieve a blocking (synchronous) IO operation, the ReadAsync-approach may deadlock: + // + // The sync-over-async IO operation will be doing a blocking wait on the completion of the async IO operation assuming that + // a wait handle would be signalled by the completion handler. Recall that the IAsyncInfo representing the IO operation may + // not be free-threaded and not "free-marshalled"; it may also belong to an ASTA compartment because the underlying WinRT + // stream lives in an ASTA compartment. The completion handler is invoked on a pool thread, i.e. in MTA. + // That handler needs to fetch the results from the async IO operation, which requires a cross-compartment call from MTA into ASTA. + // But because the ASTA thread is busy waiting this call will deadlock. + // (Recall that although WaitOne pumps COM, ASTA specifically schedules calls on the outermost ?idle? pump only.) + // + // The solution is to make sure that: + // - In cases where main thread is waiting for the async IO to complete: + // Fetch results on the main thread after it has been signalled by the completion callback. + // - In cases where main thread is not waiting for the async IO to complete: + // Fetch results in the completion callback. + // + // But the Task-plumbing around IAsyncInfo.AsTask *always* fetches results in the completion handler because it has + // no way of knowing whether or not someone is waiting. So, instead of using ReadAsync here we implement our own IAsyncResult + // and our own completion handler which can behave differently according to whether it is being used by a blocking IO + // operation wrapping a BeginRead/EndRead pair, or by an actual async operation based on the old Begin/End pattern. + + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (buffer.Length - offset < count) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); + + IInputStream wrtStr = EnsureNotDisposed(); + EnsureCanRead(); + + Debug.Assert(wrtStr != null); + + IBuffer userBuffer = buffer.AsBuffer(offset, count); + IAsyncOperationWithProgress asyncReadOperation = wrtStr.ReadAsync(userBuffer, + unchecked((uint)count), + InputStreamOptions.Partial); + + StreamReadAsyncResult asyncResult = new StreamReadAsyncResult(asyncReadOperation, userBuffer, callback, state, + processCompletedOperationInCallback: !usedByBlockingWrapper); + + // The StreamReadAsyncResult will set a private instance method to act as a Completed handler for asyncOperation. + // This will cause a CCW to be created for the delegate and the delegate has a reference to its target, i.e. to + // asyncResult, so asyncResult will not be collected. If we loose the entire AppDomain, then asyncResult and its CCW + // will be collected but the stub will remain and the callback will fail gracefully. The underlying buffer is the only + // item to which we expose a direct pointer and this is properly pinned using a mechanism similar to Overlapped. + + return asyncResult; + } + + public override int EndRead(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + EnsureNotDisposed(); + EnsureCanRead(); + + StreamOperationAsyncResult streamAsyncResult = asyncResult as StreamOperationAsyncResult; + if (streamAsyncResult == null) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_UnexpectedAsyncResult, nameof(asyncResult)); + + streamAsyncResult.Wait(); + + try + { + // If the async result did NOT process the async IO operation in its completion handler (i.e. check for errors, + // cache results etc), then we need to do that processing now. This is to allow blocking-over-async IO operations. + // See the big comment in BeginRead for details. + + if (!streamAsyncResult.ProcessCompletedOperationInCallback) + streamAsyncResult.ProcessCompletedOperation(); + + // Rethrow errors caught in the completion callback, if any: + if (streamAsyncResult.HasError) + { + streamAsyncResult.CloseStreamOperation(); + streamAsyncResult.ThrowCachedError(); + } + + // Done: + + long bytesCompleted = streamAsyncResult.BytesCompleted; + Debug.Assert(bytesCompleted <= unchecked((long)int.MaxValue)); + + return (int)bytesCompleted; + } + finally + { + // Closing multiple times is Ok. + streamAsyncResult.CloseStreamOperation(); + } + } + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (buffer.Length - offset < count) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); + + EnsureNotDisposed(); + EnsureCanRead(); + + // If already cancelled, bail early: + cancellationToken.ThrowIfCancellationRequested(); + + // State is Ok. Do the actual read: + return ReadAsyncInternal(buffer, offset, count, cancellationToken); + } + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public override int Read(byte[] buffer, int offset, int count) + { + // Arguments validation and not-disposed validation are done in BeginRead. + + IAsyncResult asyncResult = BeginRead(buffer, offset, count, null, null, usedByBlockingWrapper: true); + int bytesread = EndRead(asyncResult); + return bytesread; + } + + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public override int ReadByte() + { + // EnsureNotDisposed will be called in Read->BeginRead. + + byte[] oneByteArray = OneByteBuffer; + + if (0 == Read(oneByteArray, 0, 1)) + return -1; + + int value = oneByteArray[0]; + return value; + } + + #endregion Reading + + + #region Writing + + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return BeginWrite(buffer, offset, count, callback, state, usedByBlockingWrapper: false); + } + + private IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state, bool usedByBlockingWrapper) + { + // See the large comment in BeginRead about why we are not using this.WriteAsync, + // and instead using a custom implementation of IAsyncResult. + + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (buffer.Length - offset < count) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + + IOutputStream wrtStr = EnsureNotDisposed(); + EnsureCanWrite(); + + Debug.Assert(wrtStr != null); + + IBuffer asyncWriteBuffer = buffer.AsBuffer(offset, count); + + IAsyncOperationWithProgress asyncWriteOperation = wrtStr.WriteAsync(asyncWriteBuffer); + + StreamWriteAsyncResult asyncResult = new StreamWriteAsyncResult(asyncWriteOperation, callback, state, + processCompletedOperationInCallback: !usedByBlockingWrapper); + + // The StreamReadAsyncResult will set a private instance method to act as a Completed handler for asyncOperation. + // This will cause a CCW to be created for the delegate and the delegate has a reference to its target, i.e. to + // asyncResult, so asyncResult will not be collected. If we loose the entire AppDomain, then asyncResult and its CCW + // will be collected but the stub will remain and the callback will fail gracefully. The underlying buffer if the only + // item to which we expose a direct pointer and this is properly pinned using a mechanism similar to Overlapped. + + return asyncResult; } - #endregion Length and Position functions - - - #region Reading - -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - private IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state, bool usedByBlockingWrapper) - { - // This method is somewhat tricky: We could consider just calling ReadAsync (recall that Task implements IAsyncResult). - // It would be OK for cases where BeginRead is invoked directly by the public user. - // However, in cases where it is invoked by Read to achieve a blocking (synchronous) IO operation, the ReadAsync-approach may deadlock: - // - // The sync-over-async IO operation will be doing a blocking wait on the completion of the async IO operation assuming that - // a wait handle would be signalled by the completion handler. Recall that the IAsyncInfo representing the IO operation may - // not be free-threaded and not "free-marshalled"; it may also belong to an ASTA compartment because the underlying WinRT - // stream lives in an ASTA compartment. The completion handler is invoked on a pool thread, i.e. in MTA. - // That handler needs to fetch the results from the async IO operation, which requires a cross-compartment call from MTA into ASTA. - // But because the ASTA thread is busy waiting this call will deadlock. - // (Recall that although WaitOne pumps COM, ASTA specifically schedules calls on the outermost ?idle? pump only.) - // - // The solution is to make sure that: - // - In cases where main thread is waiting for the async IO to complete: - // Fetch results on the main thread after it has been signalled by the completion callback. - // - In cases where main thread is not waiting for the async IO to complete: - // Fetch results in the completion callback. - // - // But the Task-plumbing around IAsyncInfo.AsTask *always* fetches results in the completion handler because it has - // no way of knowing whether or not someone is waiting. So, instead of using ReadAsync here we implement our own IAsyncResult - // and our own completion handler which can behave differently according to whether it is being used by a blocking IO - // operation wrapping a BeginRead/EndRead pair, or by an actual async operation based on the old Begin/End pattern. - - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - if (buffer.Length - offset < count) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); - - IInputStream wrtStr = EnsureNotDisposed(); - EnsureCanRead(); - - Debug.Assert(wrtStr != null); - - IBuffer userBuffer = buffer.AsBuffer(offset, count); - IAsyncOperationWithProgress asyncReadOperation = wrtStr.ReadAsync(userBuffer, - unchecked((uint)count), - InputStreamOptions.Partial); - - StreamReadAsyncResult asyncResult = new StreamReadAsyncResult(asyncReadOperation, userBuffer, callback, state, - processCompletedOperationInCallback: !usedByBlockingWrapper); - - // The StreamReadAsyncResult will set a private instance method to act as a Completed handler for asyncOperation. - // This will cause a CCW to be created for the delegate and the delegate has a reference to its target, i.e. to - // asyncResult, so asyncResult will not be collected. If we loose the entire AppDomain, then asyncResult and its CCW - // will be collected but the stub will remain and the callback will fail gracefully. The underlying buffer is the only - // item to which we expose a direct pointer and this is properly pinned using a mechanism similar to Overlapped. - - return asyncResult; - } - - public override int EndRead(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException(nameof(asyncResult)); - - EnsureNotDisposed(); - EnsureCanRead(); - - StreamOperationAsyncResult streamAsyncResult = asyncResult as StreamOperationAsyncResult; - if (streamAsyncResult == null) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_UnexpectedAsyncResult, nameof(asyncResult)); - - streamAsyncResult.Wait(); - - try - { - // If the async result did NOT process the async IO operation in its completion handler (i.e. check for errors, - // cache results etc), then we need to do that processing now. This is to allow blocking-over-async IO operations. - // See the big comment in BeginRead for details. - - if (!streamAsyncResult.ProcessCompletedOperationInCallback) - streamAsyncResult.ProcessCompletedOperation(); - - // Rethrow errors caught in the completion callback, if any: - if (streamAsyncResult.HasError) - { - streamAsyncResult.CloseStreamOperation(); - streamAsyncResult.ThrowCachedError(); - } - - // Done: - - long bytesCompleted = streamAsyncResult.BytesCompleted; - Debug.Assert(bytesCompleted <= unchecked((long)int.MaxValue)); - - return (int)bytesCompleted; - } - finally - { - // Closing multiple times is Ok. - streamAsyncResult.CloseStreamOperation(); - } + public override void EndWrite(IAsyncResult asyncResult) + { + if (asyncResult == null) + throw new ArgumentNullException(nameof(asyncResult)); + + EnsureNotDisposed(); + EnsureCanWrite(); + + StreamOperationAsyncResult streamAsyncResult = asyncResult as StreamOperationAsyncResult; + if (streamAsyncResult == null) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_UnexpectedAsyncResult, nameof(asyncResult)); + + streamAsyncResult.Wait(); + + try + { + // If the async result did NOT process the async IO operation in its completion handler (i.e. check for errors, + // cache results etc), then we need to do that processing now. This is to allow blocking-over-async IO operations. + // See the big comment in BeginWrite for details. + + if (!streamAsyncResult.ProcessCompletedOperationInCallback) + streamAsyncResult.ProcessCompletedOperation(); + + // Rethrow errors caught in the completion callback, if any: + if (streamAsyncResult.HasError) + { + streamAsyncResult.CloseStreamOperation(); + streamAsyncResult.ThrowCachedError(); + } + } + finally + { + // Closing multiple times is Ok. + streamAsyncResult.CloseStreamOperation(); + } } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - if (buffer.Length - offset < count) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); - - EnsureNotDisposed(); - EnsureCanRead(); - - // If already cancelled, bail early: - cancellationToken.ThrowIfCancellationRequested(); - - // State is Ok. Do the actual read: - return ReadAsyncInternal(buffer, offset, count, cancellationToken); + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (buffer.Length - offset < count) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + + IOutputStream wrtStr = EnsureNotDisposed(); + EnsureCanWrite(); + + Debug.Assert(wrtStr != null); + + // If already cancelled, bail early: + cancellationToken.ThrowIfCancellationRequested(); + + IBuffer asyncWriteBuffer = buffer.AsBuffer(offset, count); + + IAsyncOperationWithProgress asyncWriteOperation = wrtStr.WriteAsync(asyncWriteBuffer); + Task asyncWriteTask = asyncWriteOperation.AsTask(cancellationToken); + + // The underlying IBuffer is the only object to which we expose a direct pointer to native, + // and that is properly pinned using a mechanism similar to Overlapped. + + return asyncWriteTask; } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public override int Read(byte[] buffer, int offset, int count) - { - // Arguments validation and not-disposed validation are done in BeginRead. - - IAsyncResult asyncResult = BeginRead(buffer, offset, count, null, null, usedByBlockingWrapper: true); - int bytesread = EndRead(asyncResult); - return bytesread; + + public override void Write(byte[] buffer, int offset, int count) + { + // Arguments validation and not-disposed validation are done in BeginWrite. + + IAsyncResult asyncResult = BeginWrite(buffer, offset, count, null, null, usedByBlockingWrapper: true); + EndWrite(asyncResult); } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public override int ReadByte() - { - // EnsureNotDisposed will be called in Read->BeginRead. - - byte[] oneByteArray = OneByteBuffer; - - if (0 == Read(oneByteArray, 0, 1)) - return -1; - - int value = oneByteArray[0]; - return value; - } - - #endregion Reading - - - #region Writing - - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - return BeginWrite(buffer, offset, count, callback, state, usedByBlockingWrapper: false); - } - - private IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state, bool usedByBlockingWrapper) - { - // See the large comment in BeginRead about why we are not using this.WriteAsync, - // and instead using a custom implementation of IAsyncResult. - - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - if (buffer.Length - offset < count) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - - IOutputStream wrtStr = EnsureNotDisposed(); - EnsureCanWrite(); - - Debug.Assert(wrtStr != null); - - IBuffer asyncWriteBuffer = buffer.AsBuffer(offset, count); - - IAsyncOperationWithProgress asyncWriteOperation = wrtStr.WriteAsync(asyncWriteBuffer); - - StreamWriteAsyncResult asyncResult = new StreamWriteAsyncResult(asyncWriteOperation, callback, state, - processCompletedOperationInCallback: !usedByBlockingWrapper); - - // The StreamReadAsyncResult will set a private instance method to act as a Completed handler for asyncOperation. - // This will cause a CCW to be created for the delegate and the delegate has a reference to its target, i.e. to - // asyncResult, so asyncResult will not be collected. If we loose the entire AppDomain, then asyncResult and its CCW - // will be collected but the stub will remain and the callback will fail gracefully. The underlying buffer if the only - // item to which we expose a direct pointer and this is properly pinned using a mechanism similar to Overlapped. - - return asyncResult; - } - - public override void EndWrite(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException(nameof(asyncResult)); - - EnsureNotDisposed(); - EnsureCanWrite(); - - StreamOperationAsyncResult streamAsyncResult = asyncResult as StreamOperationAsyncResult; - if (streamAsyncResult == null) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_UnexpectedAsyncResult, nameof(asyncResult)); - - streamAsyncResult.Wait(); - - try - { - // If the async result did NOT process the async IO operation in its completion handler (i.e. check for errors, - // cache results etc), then we need to do that processing now. This is to allow blocking-over-async IO operations. - // See the big comment in BeginWrite for details. - - if (!streamAsyncResult.ProcessCompletedOperationInCallback) - streamAsyncResult.ProcessCompletedOperation(); - - // Rethrow errors caught in the completion callback, if any: - if (streamAsyncResult.HasError) - { - streamAsyncResult.CloseStreamOperation(); - streamAsyncResult.ThrowCachedError(); - } - } - finally - { - // Closing multiple times is Ok. - streamAsyncResult.CloseStreamOperation(); - } + + public override void WriteByte(byte value) + { + // EnsureNotDisposed will be called in Write->BeginWrite. + + byte[] oneByteArray = OneByteBuffer; + oneByteArray[0] = value; + + Write(oneByteArray, 0, 1); } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (offset < 0) - throw new ArgumentOutOfRangeException(nameof(offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - if (buffer.Length - offset < count) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - - IOutputStream wrtStr = EnsureNotDisposed(); - EnsureCanWrite(); - - Debug.Assert(wrtStr != null); - - // If already cancelled, bail early: - cancellationToken.ThrowIfCancellationRequested(); - - IBuffer asyncWriteBuffer = buffer.AsBuffer(offset, count); - - IAsyncOperationWithProgress asyncWriteOperation = wrtStr.WriteAsync(asyncWriteBuffer); - Task asyncWriteTask = asyncWriteOperation.AsTask(cancellationToken); - - // The underlying IBuffer is the only object to which we expose a direct pointer to native, - // and that is properly pinned using a mechanism similar to Overlapped. - - return asyncWriteTask; - } - - - public override void Write(byte[] buffer, int offset, int count) - { - // Arguments validation and not-disposed validation are done in BeginWrite. - - IAsyncResult asyncResult = BeginWrite(buffer, offset, count, null, null, usedByBlockingWrapper: true); - EndWrite(asyncResult); - } - - - public override void WriteByte(byte value) - { - // EnsureNotDisposed will be called in Write->BeginWrite. - - byte[] oneByteArray = OneByteBuffer; - oneByteArray[0] = value; - - Write(oneByteArray, 0, 1); - } - - #endregion Writing - - - #region Flushing - - public override void Flush() - { - // See the large comment in BeginRead about why we are not using this.FlushAsync, - // and instead using a custom implementation of IAsyncResult. - - IOutputStream wrtStr = EnsureNotDisposed(); - - // Calling Flush in a non-writable stream is a no-op, not an error: - if (!_canWrite) - return; - - Debug.Assert(wrtStr != null); - - IAsyncOperation asyncFlushOperation = wrtStr.FlushAsync(); - StreamFlushAsyncResult asyncResult = new StreamFlushAsyncResult(asyncFlushOperation, processCompletedOperationInCallback: false); - - asyncResult.Wait(); - - try - { - // We got signaled, so process the async Flush operation back on this thread: - // (This is to allow blocking-over-async IO operations. See the big comment in BeginRead for details.) - asyncResult.ProcessCompletedOperation(); - - // Rethrow errors cached by the async result, if any: - if (asyncResult.HasError) - { - asyncResult.CloseStreamOperation(); - asyncResult.ThrowCachedError(); - } - } - finally - { - // Closing multiple times is Ok. - asyncResult.CloseStreamOperation(); - } + #endregion Writing + + + #region Flushing + + public override void Flush() + { + // See the large comment in BeginRead about why we are not using this.FlushAsync, + // and instead using a custom implementation of IAsyncResult. + + IOutputStream wrtStr = EnsureNotDisposed(); + + // Calling Flush in a non-writable stream is a no-op, not an error: + if (!_canWrite) + return; + + Debug.Assert(wrtStr != null); + + IAsyncOperation asyncFlushOperation = wrtStr.FlushAsync(); + StreamFlushAsyncResult asyncResult = new StreamFlushAsyncResult(asyncFlushOperation, processCompletedOperationInCallback: false); + + asyncResult.Wait(); + + try + { + // We got signaled, so process the async Flush operation back on this thread: + // (This is to allow blocking-over-async IO operations. See the big comment in BeginRead for details.) + asyncResult.ProcessCompletedOperation(); + + // Rethrow errors cached by the async result, if any: + if (asyncResult.HasError) + { + asyncResult.CloseStreamOperation(); + asyncResult.ThrowCachedError(); + } + } + finally + { + // Closing multiple times is Ok. + asyncResult.CloseStreamOperation(); + } } -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - public override Task FlushAsync(CancellationToken cancellationToken) - { - IOutputStream wrtStr = EnsureNotDisposed(); - - // Calling Flush in a non-writable stream is a no-op, not an error: - if (!_canWrite) - return Task.CompletedTask; - - Debug.Assert(wrtStr != null); - - cancellationToken.ThrowIfCancellationRequested(); - - IAsyncOperation asyncFlushOperation = wrtStr.FlushAsync(); - Task asyncFlushTask = asyncFlushOperation.AsTask(cancellationToken); - return asyncFlushTask; + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + public override Task FlushAsync(CancellationToken cancellationToken) + { + IOutputStream wrtStr = EnsureNotDisposed(); + + // Calling Flush in a non-writable stream is a no-op, not an error: + if (!_canWrite) + return Task.CompletedTask; + + Debug.Assert(wrtStr != null); + + cancellationToken.ThrowIfCancellationRequested(); + + IAsyncOperation asyncFlushOperation = wrtStr.FlushAsync(); + Task asyncFlushTask = asyncFlushOperation.AsTask(cancellationToken); + return asyncFlushTask; } - #endregion Flushing + #endregion Flushing - #region ReadAsyncInternal implementation + #region ReadAsyncInternal implementation // Moved it to the end while using Dev10 VS because it does not understand async and everything that follows looses intellisense. // Should move this code into the Reading regios once using Dev11 VS becomes the norm. -#if NET - [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] -#endif - private async Task ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - Debug.Assert(buffer != null); - Debug.Assert(offset >= 0); - Debug.Assert(count >= 0); - Debug.Assert(buffer.Length - offset >= count); - Debug.Assert(_canRead); - - IInputStream wrtStr = EnsureNotDisposed(); - - Debug.Assert(wrtStr != null); - - try - { - IBuffer userBuffer = buffer.AsBuffer(offset, count); - IAsyncOperationWithProgress asyncReadOperation = wrtStr.ReadAsync(userBuffer, - unchecked((uint)count), - InputStreamOptions.Partial); - - IBuffer resultBuffer = await asyncReadOperation.AsTask(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); - - // If cancellationToken was cancelled until now, then we are currently propagating the corresponding cancellation exception. - // (It will be correctly rethrown by the catch block below and overall we will return a cancelled task.) - // But if the underlying operation managed to complete before it was cancelled, we want - // the entire task to complete as well. This is ok as the continuation is very lightweight: - - if (resultBuffer == null) - return 0; - - WinRtIOHelper.EnsureResultsInUserBuffer(userBuffer, resultBuffer); - - Debug.Assert(resultBuffer.Length <= unchecked((uint)int.MaxValue)); - return (int)resultBuffer.Length; - } - catch (Exception ex) - { - // If the interop layer gave us an Exception, we assume that it hit a general/unknown case and wrap it into - // an IOException as this is what Stream users expect. - WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); - return 0; - } - } - #endregion ReadAsyncInternal implementation - - } // class WinRtToNetFxStreamAdapter -} // namespace - -// WinRtToNetFxStreamAdapter.cs + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] + private async Task ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + Debug.Assert(buffer != null); + Debug.Assert(offset >= 0); + Debug.Assert(count >= 0); + Debug.Assert(buffer.Length - offset >= count); + Debug.Assert(_canRead); + + IInputStream wrtStr = EnsureNotDisposed(); + + Debug.Assert(wrtStr != null); + + try + { + IBuffer userBuffer = buffer.AsBuffer(offset, count); + IAsyncOperationWithProgress asyncReadOperation = wrtStr.ReadAsync(userBuffer, + unchecked((uint)count), + InputStreamOptions.Partial); + + IBuffer resultBuffer = await asyncReadOperation.AsTask(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); + + // If cancellationToken was cancelled until now, then we are currently propagating the corresponding cancellation exception. + // (It will be correctly rethrown by the catch block below and overall we will return a cancelled task.) + // But if the underlying operation managed to complete before it was cancelled, we want + // the entire task to complete as well. This is ok as the continuation is very lightweight: + + if (resultBuffer == null) + return 0; + + WinRtIOHelper.EnsureResultsInUserBuffer(userBuffer, resultBuffer); + + Debug.Assert(resultBuffer.Length <= unchecked((uint)int.MaxValue)); + return (int)resultBuffer.Length; + } + catch (Exception ex) + { + // If the interop layer gave us an Exception, we assume that it hit a general/unknown case and wrap it into + // an IOException as this is what Stream users expect. + WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); + return 0; + } + } + #endregion ReadAsyncInternal implementation + + } // class WinRtToNetFxStreamAdapter +} // namespace + +// WinRtToNetFxStreamAdapter.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs index db96cf969..7d3934be7 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs @@ -1,346 +1,284 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace System.Runtime.InteropServices.WindowsRuntime -{ - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - using System.Threading; - using global::Windows.Foundation; - using global::Windows.Storage.Streams; - using Com; - /// - /// Contains an implementation of the WinRT IBuffer interface that conforms to all requirements on classes that implement that interface, - /// such as implementing additional interfaces. - /// -#if NET - [global::WinRT.WinRTExposedType(typeof(global::ABI.System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferWinRTTypeDetails))] -#endif -#if EMBED - internal -#else - public -#endif - sealed class WindowsRuntimeBuffer : IBuffer, IBufferByteAccess, IMarshal - { - [DllImport("api-ms-win-core-winrt-robuffer-l1-1-0.dll")] - private static extern unsafe int RoGetBufferMarshaler(IntPtr* bufferMarshalerPtr); - #region Constants - - private const string WinTypesDLL = "WinTypes.dll"; - private const int E_BOUNDS = unchecked((int)0x8000000B); - - #endregion Constants - - - #region Static factory methods - - public static IBuffer Create(int capacity) - { - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); - - return new WindowsRuntimeBuffer(capacity); - } - - - public static IBuffer Create(byte[] data, int offset, int length, int capacity) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); - if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); - if (data.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (data.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); - - byte[] underlyingData = new byte[capacity]; - Array.Copy(data, offset, underlyingData, 0, length); - return new WindowsRuntimeBuffer(underlyingData, 0, length, capacity); - } - - #endregion Static factory methods - - - #region Static fields and helpers - - // This object handles IMarshal calls for us: - [ThreadStatic] - private static IMarshal t_winRtMarshalProxy = null; - - private static unsafe void EnsureHasMarshalProxy() - { - if (t_winRtMarshalProxy != null) - return; - - try - { - IntPtr proxyPtr = default; - int hr = RoGetBufferMarshaler(&proxyPtr); - IMarshal proxy = new ABI.Com.IMarshal(ObjectReference.Attach(ref proxyPtr, global::WinRT.Interop.IID.IID_IMarshal)); - t_winRtMarshalProxy = proxy; - - if (hr != 0) - { - Exception ex = new Exception(string.Format("{0} ({1}!RoGetBufferMarshaler)", global::Windows.Storage.Streams.SR.WinRtCOM_Error, WinTypesDLL)); - ex.SetHResult(hr); - throw ex; - } - - if (proxy == null) - throw new NullReferenceException(string.Format("{0} ({1}!RoGetBufferMarshaler)", global::Windows.Storage.Streams.SR.WinRtCOM_Error, WinTypesDLL)); - } - catch (DllNotFoundException ex) - { - throw new NotImplementedException(string.Format(global::Windows.Storage.Streams.SR.NotImplemented_NativeRoutineNotFound, - string.Format("{0}!RoGetBufferMarshaler", WinTypesDLL)), - ex); - } - } - - #endregion Static fields and helpers - - - #region Fields - - private readonly byte[] _data; - private readonly int _dataStartOffs = 0; - private int _usefulDataLength = 0; - private readonly int _maxDataCapacity = 0; - private GCHandle _pinHandle; - - // Pointer to data[dataStartOffs] when data is pinned: - private IntPtr _dataPtr = IntPtr.Zero; - - #endregion Fields - - - #region Constructors - - internal WindowsRuntimeBuffer(int capacity) - { - if (capacity < 0) - throw new ArgumentOutOfRangeException(nameof(capacity)); - - _data = new byte[capacity]; - _dataStartOffs = 0; - _usefulDataLength = 0; - _maxDataCapacity = capacity; - _dataPtr = IntPtr.Zero; - } - - - internal WindowsRuntimeBuffer(byte[] data, int offset, int length, int capacity) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); - if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); - if (data.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (data.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); - - _data = data; - _dataStartOffs = offset; - _usefulDataLength = length; - _maxDataCapacity = capacity; - _dataPtr = IntPtr.Zero; - } - - #endregion Constructors - - - #region Helpers - - internal void GetUnderlyingData(out byte[] underlyingDataArray, out int underlyingDataArrayStartOffset) - { - underlyingDataArray = _data; - underlyingDataArrayStartOffset = _dataStartOffs; - } - - - private unsafe byte* PinUnderlyingData() - { - GCHandle gcHandle = default(GCHandle); - bool ptrWasStored = false; - IntPtr buffPtr; - - try { } - finally - { - try - { - // Pin the data array: - gcHandle = GCHandle.Alloc(_data, GCHandleType.Pinned); - buffPtr = gcHandle.AddrOfPinnedObject() + _dataStartOffs; - - // Store the pin IFF it has not been assigned: - ptrWasStored = (Interlocked.CompareExchange(ref _dataPtr, buffPtr, IntPtr.Zero) == IntPtr.Zero); - } - finally - { - if (!ptrWasStored) - { - // There is a race with another thread also trying to create a pin and they were first - // in assigning to data pin. That's ok, just give it up. - // Unpin again (the pin from the other thread remains): - gcHandle.Free(); - } - else - { - if (_pinHandle.IsAllocated) - _pinHandle.Free(); - - // Make sure we keep track of the handle - _pinHandle = gcHandle; - } - } - } - - // Ok, now all is good: - return (byte*)buffPtr; - } - - ~WindowsRuntimeBuffer() - { - if (_pinHandle.IsAllocated) - _pinHandle.Free(); - } - - #endregion Helpers - - - #region Implementation of Windows.Foundation.IBuffer - - uint IBuffer.Capacity - { - get { return unchecked((uint)_maxDataCapacity); } - } - - - uint IBuffer.Length - { - get - { - return unchecked((uint)_usefulDataLength); - } - - set - { - if (value > ((IBuffer)this).Capacity) - { - ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(value), global::Windows.Storage.Streams.SR.Argument_BufferLengthExceedsCapacity); - ex.SetHResult(E_BOUNDS); - throw ex; - } - - // Capacity is ensured to not exceed Int32.MaxValue, so Length is within this limit and this cast is safe: - Debug.Assert(((IBuffer)this).Capacity <= int.MaxValue); - _usefulDataLength = unchecked((int)value); - } - } - - #endregion Implementation of Windows.Foundation.IBuffer - - - #region Implementation of IBufferByteAccess - - unsafe IntPtr IBufferByteAccess.Buffer - { - get - { - // Get pin handle: - IntPtr buffPtr = Volatile.Read(ref _dataPtr); - - // If we are already pinned, return the pointer and have a nice day: - if (buffPtr != IntPtr.Zero) - return buffPtr; - - // Ok, we are not yet pinned. Let's do it. - return new IntPtr(PinUnderlyingData()); - } - } - - #endregion Implementation of IBufferByteAccess - - #region Implementation of IMarshal - - void IMarshal.DisconnectObject(uint dwReserved) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.DisconnectObject(dwReserved); - } - - - unsafe void IMarshal.GetMarshalSizeMax(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags, uint* pSize) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); - } - - - unsafe void IMarshal.GetUnmarshalClass(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlFlags, Guid* pCid) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid); - } - - - unsafe void IMarshal.MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); - } - - - void IMarshal.ReleaseMarshalData(IntPtr pStm) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.ReleaseMarshalData(pStm); - } - - - unsafe void IMarshal.UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) - { - EnsureHasMarshalProxy(); - t_winRtMarshalProxy!.UnmarshalInterface(pStm, riid, ppv); - } - #endregion Implementation of IMarshal - } // class WindowsRuntimeBuffer -} // namespace - -#if NET +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Security; + using System.Threading; + using global::Windows.Foundation; + using global::Windows.Storage.Streams; + + /// + /// Contains an implementation of the WinRT IBuffer interface that conforms to all requirements on classes that implement that interface, + /// such as implementing additional interfaces. + /// + public sealed class WindowsRuntimeBuffer : IBuffer, IBufferByteAccess + { + #region Constants + + private const int E_BOUNDS = unchecked((int)0x8000000B); + + #endregion Constants + + + #region Static factory methods + + public static IBuffer Create(int capacity) + { + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + + return new WindowsRuntimeBuffer(capacity); + } + + + public static IBuffer Create(byte[] data, int offset, int length, int capacity) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + if (data.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (data.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); + + byte[] underlyingData = new byte[capacity]; + Array.Copy(data, offset, underlyingData, 0, length); + return new WindowsRuntimeBuffer(underlyingData, 0, length, capacity); + } + + #endregion Static factory methods + + #region Fields + + private readonly byte[] _data; + private readonly int _dataStartOffs = 0; + private int _usefulDataLength = 0; + private readonly int _maxDataCapacity = 0; + private GCHandle _pinHandle; + + // Pointer to data[dataStartOffs] when data is pinned: + private IntPtr _dataPtr = IntPtr.Zero; + + #endregion Fields + + + #region Constructors + + internal WindowsRuntimeBuffer(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(nameof(capacity)); + + _data = new byte[capacity]; + _dataStartOffs = 0; + _usefulDataLength = 0; + _maxDataCapacity = capacity; + _dataPtr = IntPtr.Zero; + } + + + internal WindowsRuntimeBuffer(byte[] data, int offset, int length, int capacity) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + if (data.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (data.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); + + _data = data; + _dataStartOffs = offset; + _usefulDataLength = length; + _maxDataCapacity = capacity; + _dataPtr = IntPtr.Zero; + } + + #endregion Constructors + + + #region Helpers + + internal void GetUnderlyingData(out byte[] underlyingDataArray, out int underlyingDataArrayStartOffset) + { + underlyingDataArray = _data; + underlyingDataArrayStartOffset = _dataStartOffs; + } + + private unsafe byte* PinUnderlyingData() + { + GCHandle gcHandle = default(GCHandle); + bool ptrWasStored = false; + IntPtr buffPtr; + + try { } + finally + { + try + { + // Pin the data array: + gcHandle = GCHandle.Alloc(_data, GCHandleType.Pinned); + buffPtr = gcHandle.AddrOfPinnedObject() + _dataStartOffs; + + // Store the pin IFF it has not been assigned: + ptrWasStored = (Interlocked.CompareExchange(ref _dataPtr, buffPtr, IntPtr.Zero) == IntPtr.Zero); + } + finally + { + if (!ptrWasStored) + { + // There is a race with another thread also trying to create a pin and they were first + // in assigning to data pin. That's ok, just give it up. + // Unpin again (the pin from the other thread remains): + gcHandle.Free(); + } + else + { + if (_pinHandle.IsAllocated) + _pinHandle.Free(); + + // Make sure we keep track of the handle + _pinHandle = gcHandle; + } + } + } + + // Ok, now all is good: + return (byte*)buffPtr; + } + + ~WindowsRuntimeBuffer() + { + if (_pinHandle.IsAllocated) + _pinHandle.Free(); + } + + #endregion Helpers + + + #region Implementation of Windows.Foundation.IBuffer + + uint IBuffer.Capacity + { + get { return unchecked((uint)_maxDataCapacity); } + } + + + uint IBuffer.Length + { + get + { + return unchecked((uint)_usefulDataLength); + } + + set + { + if (value > ((IBuffer)this).Capacity) + { + ArgumentOutOfRangeException ex = new ArgumentOutOfRangeException(nameof(value), global::Windows.Storage.Streams.SR.Argument_BufferLengthExceedsCapacity); + ex.HResult = E_BOUNDS; + throw ex; + } + + // Capacity is ensured to not exceed Int32.MaxValue, so Length is within this limit and this cast is safe: + Debug.Assert(((IBuffer)this).Capacity <= int.MaxValue); + _usefulDataLength = unchecked((int)value); + } + } + + #endregion Implementation of Windows.Foundation.IBuffer + + + #region Implementation of IBufferByteAccess + + unsafe byte* IBufferByteAccess.Buffer + { + get + { + // Get pin handle: + IntPtr buffPtr = Volatile.Read(ref _dataPtr); + + // If we are already pinned, return the pointer: + if (buffPtr != IntPtr.Zero) + return (byte*)buffPtr; + + // Otherwise pin it. + return PinUnderlyingData(); + } + } + + #endregion Implementation of IBufferByteAccess + } // class WindowsRuntimeBuffer +} // namespace + namespace ABI.System.Runtime.InteropServices.WindowsRuntime { - internal sealed class WindowsRuntimeBufferWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails - { - public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() - { - return new ComWrappers.ComInterfaceEntry[] - { - new ComWrappers.ComInterfaceEntry - { - IID = global::WinRT.Interop.IID.IID_IBuffer, - Vtable = global::ABI.Windows.Storage.Streams.IBuffer.AbiToProjectionVftablePtr - }, - new ComWrappers.ComInterfaceEntry - { - IID = global::WinRT.Interop.IID.IID_IBufferByteAccess, - Vtable = global::ABI.Windows.Storage.Streams.IBufferByteAccess.Vftbl.AbiToProjectionVftablePtr - }, - new ComWrappers.ComInterfaceEntry - { - IID = global::WinRT.Interop.IID.IID_IMarshal, - Vtable = global::ABI.Com.IMarshal.Vftbl.AbiToProjectionVftablePtr - } - }; + [WindowsRuntimeClassName("Windows.Storage.Streams.IBuffer")] + [WindowsRuntimeBufferComWrappersMarshaller] + file static class WindowsRuntimeBuffer; + + file struct WindowsRuntimeBufferInterfaceEntries + { + public ComInterfaceEntry IBuffer; + public ComInterfaceEntry IBufferByteAccess; + public ComInterfaceEntry IStringable; + public ComInterfaceEntry IWeakReferenceSource; + public ComInterfaceEntry IMarshal; + public ComInterfaceEntry IAgileObject; + public ComInterfaceEntry IInspectable; + public ComInterfaceEntry IUnknown; + } + + file static class WindowsRuntimeBufferInterfaceEntriesImpl + { + [FixedAddressValueType] + public static readonly WindowsRuntimeBufferInterfaceEntries Entries; + + /// + /// Initializes . + /// + static WindowsRuntimeBufferInterfaceEntriesImpl() + { + Entries.IBuffer.IID = ABI.InterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; + Entries.IBuffer.Vtable = ABI.Windows.Storage.Streams.IBufferImpl.Vtable; + Entries.IBufferByteAccess.IID = ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IBufferByteAccess; + Entries.IBufferByteAccess.Vtable = ABI.Windows.Storage.Streams.IBufferByteAccessImpl.Vtable; + Entries.IStringable.IID = IStringableImpl.IID; + Entries.IStringable.Vtable = IStringableImpl.Vtable; + Entries.IWeakReferenceSource.IID = IWeakReferenceSourceImpl.IID; + Entries.IWeakReferenceSource.Vtable = IWeakReferenceSourceImpl.Vtable; + Entries.IMarshal.IID = ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IMarshal; + Entries.IMarshal.Vtable = ABI.Windows.Storage.Streams.IBufferMarshalImpl.Vtable; + Entries.IAgileObject.IID = IAgileObjectImpl.IID; + Entries.IAgileObject.Vtable = IAgileObjectImpl.Vtable; + Entries.IInspectable.IID = IInspectableImpl.IID; + Entries.IInspectable.Vtable = IInspectableImpl.Vtable; + Entries.IUnknown.IID = IUnknownImpl.IID; + Entries.IUnknown.Vtable = IUnknownImpl.Vtable; + } + } + + file sealed unsafe class WindowsRuntimeBufferComWrappersMarshallerAttribute : WindowsRuntimeComWrappersMarshallerAttribute + { + /// + public override void* GetOrCreateComInterfaceForObject(object value) + { + return WindowsRuntimeComWrappersMarshal.GetOrCreateComInterfaceForObject(value, CreateComInterfaceFlags.TrackerSupport); + } + + /// + public override ComInterfaceEntry* ComputeVtables(out int count) + { + count = sizeof(WindowsRuntimeBufferInterfaceEntries) / sizeof(ComInterfaceEntry); + + return (ComInterfaceEntry*)Unsafe.AsPointer(in WindowsRuntimeBufferInterfaceEntriesImpl.Entries); } } -} -#endif - -// WindowsRuntimeBuffer.cs +} + +// WindowsRuntimeBuffer.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs index defb5ff75..0ea735d8c 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs @@ -1,65 +1,60 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace System.Runtime.InteropServices.WindowsRuntime -{ - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - using global::Windows.Foundation; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using global::Windows.Foundation; using global::Windows.Storage.Streams; - using WinRT; - /// - /// Contains extension methods that expose operations on WinRT Windows.Foundation.IBuffer. - /// -#if EMBED - internal -#else - public -#endif - static class WindowsRuntimeBufferExtensions - { -#region (Byte []).AsBuffer extensions - - public static IBuffer AsBuffer(this byte[] source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - return AsBuffer(source, 0, source.Length, source.Length); - } - - - public static IBuffer AsBuffer(this byte[] source, int offset, int length) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); - if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (source.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - - return AsBuffer(source, offset, length, length); - } - - - public static IBuffer AsBuffer(this byte[] source, int offset, int length, int capacity) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); - if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); - if (source.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (source.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); - - return new WindowsRuntimeBuffer(source, offset, length, capacity); - } - -#endregion (Byte []).AsBuffer extensions - - + + /// + /// Contains extension methods that expose operations on WinRT Windows.Foundation.IBuffer. + /// + public static class WindowsRuntimeBufferExtensions + { +#region (Byte []).AsBuffer extensions + + public static IBuffer AsBuffer(this byte[] source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return AsBuffer(source, 0, source.Length, source.Length); + } + + + public static IBuffer AsBuffer(this byte[] source, int offset, int length) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); + if (source.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + + return AsBuffer(source, offset, length, length); + } + + + public static IBuffer AsBuffer(this byte[] source, int offset, int length, int capacity) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); + if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); + if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + if (source.Length - offset < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (source.Length - offset < capacity) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (capacity < length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientBufferCapacity); + + return new WindowsRuntimeBuffer(source, offset, length, capacity); + } + +#endregion (Byte []).AsBuffer extensions + + #region (Span).CopyTo extensions for copying to an (IBuffer) /// @@ -112,88 +107,88 @@ public static void CopyTo(this Span source, IBuffer destination, uint dest #endregion (Span).CopyTo extensions for copying to an (IBuffer) -#region (Byte []).CopyTo extensions for copying to an (IBuffer) - - /// - /// Copies the contents of source to destination starting at offset 0. - /// This method does NOT update destination.Length. - /// - /// Array to copy data from. - /// The buffer to copy to. - public static void CopyTo(this byte[] source, IBuffer destination) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - +#region (Byte []).CopyTo extensions for copying to an (IBuffer) + + /// + /// Copies the contents of source to destination starting at offset 0. + /// This method does NOT update destination.Length. + /// + /// Array to copy data from. + /// The buffer to copy to. + public static void CopyTo(this byte[] source, IBuffer destination) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + CopyTo(source.AsSpan(), destination, 0); - } - - - /// - /// Copies count bytes from source starting at offset sourceIndex - /// to destination starting at destinationIndex. - /// This method does NOT update destination.Length. - /// - /// Array to copy data from. - /// Position in the array from where to start copying. - /// The buffer to copy to. - /// Position in the buffer to where to start copying. - /// The number of bytes to copy. - public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - + } + + + /// + /// Copies count bytes from source starting at offset sourceIndex + /// to destination starting at destinationIndex. + /// This method does NOT update destination.Length. + /// + /// Array to copy data from. + /// Position in the array from where to start copying. + /// The buffer to copy to. + /// Position in the buffer to where to start copying. + /// The number of bytes to copy. + public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + CopyTo(source.AsSpan(sourceIndex, count), destination, destinationIndex); } - -#endregion (Byte []).CopyTo extensions for copying to an (IBuffer) - - -#region (IBuffer).ToArray extensions for copying to a new (Byte []) - - public static byte[] ToArray(this IBuffer source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - return ToArray(source, 0, checked((int)source.Length)); - } - - - public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); - if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); - - if (count == 0) - return Array.Empty(); - - byte[] destination = new byte[count]; - source.CopyTo(sourceIndex, destination, 0, count); - return destination; - } - -#endregion (IBuffer).ToArray extensions for copying to a new (Byte []) - - + +#endregion (Byte []).CopyTo extensions for copying to an (IBuffer) + + +#region (IBuffer).ToArray extensions for copying to a new (Byte []) + + public static byte[] ToArray(this IBuffer source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return ToArray(source, 0, checked((int)source.Length)); + } + + + public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + + if (count == 0) + return Array.Empty(); + + byte[] destination = new byte[count]; + source.CopyTo(sourceIndex, destination, 0, count); + return destination; + } + +#endregion (IBuffer).ToArray extensions for copying to a new (Byte []) + + #region (IBuffer).CopyTo extensions for copying to a (Span) - + public static void CopyTo(this IBuffer source, Span destination) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - + { + if (source == null) throw new ArgumentNullException(nameof(source)); + CopyTo(source, 0, destination, checked((int)source.Length)); - } - + } + public static void CopyTo(this IBuffer source, uint sourceIndex, Span destination, int count) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); - if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); if (destination.Length < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (count == 0) return; - + if (count == 0) return; + Debug.Assert(sourceIndex <= int.MaxValue); Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, count) : source.GetSpanForCapacityUnsafe(sourceIndex).Slice(0, (int)count); @@ -214,300 +209,280 @@ public static void CopyTo(this IBuffer source, byte[] destination) CopyTo(source, destination.AsSpan()); } - + public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destination, int destinationIndex, int count) { if (source == null) throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); CopyTo(source, sourceIndex, destination.AsSpan(destinationIndex, count), count); - } - -#endregion (IBuffer).CopyTo extensions for copying to a (Byte []) - - -#region (IBuffer).CopyTo extensions for copying to an (IBuffer) - - public static void CopyTo(this IBuffer source, IBuffer destination) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - - CopyTo(source, 0, destination, 0, source.Length); - } - - - public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destination, uint destinationIndex, uint count) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); - if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); - if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); - if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); - if (count == 0) return; - + } + +#endregion (IBuffer).CopyTo extensions for copying to a (Byte []) + + +#region (IBuffer).CopyTo extensions for copying to an (IBuffer) + + public static void CopyTo(this IBuffer source, IBuffer destination) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + + CopyTo(source, 0, destination, 0, source.Length); + } + + + public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destination, uint destinationIndex, uint count) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); + if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); + if (count == 0) return; + Debug.Assert(count <= int.MaxValue); Debug.Assert(sourceIndex <= int.MaxValue); Debug.Assert(destinationIndex <= int.MaxValue); - + // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning: Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, (int)count) : source.GetSpanForCapacityUnsafe(sourceIndex).Slice(0, (int)count); Span destSpan = destination.TryGetUnderlyingData(out byte[] destDataArr, out int destOffset) ? destDataArr.AsSpan(destOffset + (int)destinationIndex) : destination.GetSpanForCapacityUnsafe(destinationIndex).Slice(0, (int)count); - + srcSpan.CopyTo(destSpan); - + // Ensure source and destination stay alive for the copy operation GC.KeepAlive(source); GC.KeepAlive(destination); - + // Update Length last to make sure the data is valid if (destinationIndex + count > destination.Length) - { + { destination.Length = destinationIndex + count; - } - } - -#endregion (IBuffer).CopyTo extensions for copying to an (IBuffer) - - -#region Access to underlying array optimised for IBuffers backed by managed arrays (to avoid pinning) - - /// - /// If the specified IBuffer is backed by a managed array, this method will return true and - /// set underlyingDataArray to refer to that array - /// and underlyingDataArrayStartOffset to the value at which the buffer data begins in that array. - /// If the specified IBuffer is not backed by a managed array, this method will return false. - /// This method is required by managed APIs that wish to use the buffer's data with other managed APIs that use - /// arrays without a need for a memory copy. - /// - /// An IBuffer. - /// Will be set to the data array backing buffer or to null. - /// Will be set to the start offset of the buffer data in the backing array - /// or to -1. - /// Whether the IBuffer is backed by a managed byte array. - internal static bool TryGetUnderlyingData(this IBuffer buffer, out byte[] underlyingDataArray, out int underlyingDataArrayStartOffset) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - WindowsRuntimeBuffer winRtBuffer = buffer as WindowsRuntimeBuffer; - if (winRtBuffer == null) - { - underlyingDataArray = null; - underlyingDataArrayStartOffset = -1; - return false; - } - - winRtBuffer.GetUnderlyingData(out underlyingDataArray, out underlyingDataArrayStartOffset); - return true; - } - - - /// - /// Checks if the underlying memory backing two IBuffer instances is actually the same memory. - /// When applied to IBuffer instances backed by managed arrays this method is preferable to a naive comparison - /// (such as ((IBufferByteAccess) buffer).Buffer == ((IBufferByteAccess) otherBuffer).Buffer) because it avoids - /// pinning the backing array which would be necessary if a direct memory pointer was obtained. - /// - /// An IBuffer instance. - /// An IBuffer instance or null. - /// true if the underlying Buffer memory pointer is the same for both specified - /// IBuffer instances (i.e. if they are backed by the same memory); false otherwise. - public static bool IsSameData(this IBuffer buffer, IBuffer otherBuffer) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - if (otherBuffer == null) - return false; - - if (buffer == otherBuffer) - return true; - - byte[] thisDataArr, otherDataArr; - int thisDataOffs, otherDataOffs; - - bool thisIsManaged = buffer.TryGetUnderlyingData(out thisDataArr, out thisDataOffs); - bool otherIsManaged = otherBuffer.TryGetUnderlyingData(out otherDataArr, out otherDataOffs); - - if (thisIsManaged != otherIsManaged) - return false; - - if (thisIsManaged) - return (thisDataArr == otherDataArr) && (thisDataOffs == otherDataOffs); - - if (!WindowsRuntimeMarshal.TryGetDataUnsafe(buffer, out IntPtr thisBuff) || - !WindowsRuntimeMarshal.TryGetDataUnsafe(otherBuffer, out IntPtr otherBuff)) - { + } + } + +#endregion (IBuffer).CopyTo extensions for copying to an (IBuffer) + + +#region Access to underlying array optimised for IBuffers backed by managed arrays (to avoid pinning) + + /// + /// If the specified IBuffer is backed by a managed array, this method will return true and + /// set underlyingDataArray to refer to that array + /// and underlyingDataArrayStartOffset to the value at which the buffer data begins in that array. + /// If the specified IBuffer is not backed by a managed array, this method will return false. + /// This method is required by managed APIs that wish to use the buffer's data with other managed APIs that use + /// arrays without a need for a memory copy. + /// + /// An IBuffer. + /// Will be set to the data array backing buffer or to null. + /// Will be set to the start offset of the buffer data in the backing array + /// or to -1. + /// Whether the IBuffer is backed by a managed byte array. + internal static bool TryGetUnderlyingData(this IBuffer buffer, out byte[] underlyingDataArray, out int underlyingDataArrayStartOffset) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + WindowsRuntimeBuffer winRtBuffer = buffer as WindowsRuntimeBuffer; + if (winRtBuffer == null) + { + underlyingDataArray = null; + underlyingDataArrayStartOffset = -1; + return false; + } + + winRtBuffer.GetUnderlyingData(out underlyingDataArray, out underlyingDataArrayStartOffset); + return true; + } + + + /// + /// Checks if the underlying memory backing two IBuffer instances is actually the same memory. + /// When applied to IBuffer instances backed by managed arrays this method is preferable to a naive comparison + /// (such as ((IBufferByteAccess) buffer).Buffer == ((IBufferByteAccess) otherBuffer).Buffer) because it avoids + /// pinning the backing array which would be necessary if a direct memory pointer was obtained. + /// + /// An IBuffer instance. + /// An IBuffer instance or null. + /// true if the underlying Buffer memory pointer is the same for both specified + /// IBuffer instances (i.e. if they are backed by the same memory); false otherwise. + public static unsafe bool IsSameData(this IBuffer buffer, IBuffer otherBuffer) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + if (otherBuffer == null) return false; - } - - return thisBuff == otherBuff; - } - -#endregion Access to underlying array optimised for IBuffers backed by managed arrays (to avoid pinning) - - -#region Extensions for co-operation with memory streams (share mem stream data; expose data as managed/unmanaged mem stream) - /// - /// Creates a new IBuffer instance backed by the same memory as is backing the specified MemoryStream. - /// The MemoryStream may re-sized in future, as a result the stream will be backed by a different memory region. - /// In such case, the buffer created by this method will remain backed by the memory behind the stream at the time the buffer was created.
- /// This method can throw an ObjectDisposedException if the specified stream is closed.
- /// This method can throw an UnauthorizedAccessException if the specified stream cannot expose its underlying memory buffer. - ///
- /// A memory stream to share the data memory with the buffer being created. - /// A new IBuffer backed by the same memory as this specified stream. - // The naming inconsistency with (Byte []).AsBuffer is intentional: as this extension method will appear on - // MemoryStream, consistency with method names on MemoryStream is more important. There we already have an API - // called GetBuffer which returns the underlying array. - public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream underlyingStream) - { - if (underlyingStream == null) - throw new ArgumentNullException(nameof(underlyingStream)); - - ArraySegment streamData; - if (!underlyingStream.TryGetBuffer(out streamData)) - { - throw new UnauthorizedAccessException(global::Windows.Storage.Streams.SR.UnauthorizedAccess_InternalBuffer); - } - return new WindowsRuntimeBuffer(streamData.Array!, (int)streamData.Offset, (int)underlyingStream.Length, underlyingStream.Capacity); - } - - - /// - /// Creates a new IBuffer instance backed by the same memory as is backing the specified MemoryStream. - /// The MemoryStream may re-sized in future, as a result the stream will be backed by a different memory region. - /// In such case buffer created by this method will remain backed by the memory behind the stream at the time the buffer was created.
- /// This method can throw an ObjectDisposedException if the specified stream is closed.
- /// This method can throw an UnauthorizedAccessException if the specified stream cannot expose its underlying memory buffer. - /// The created buffer begins at position positionInStream in the stream and extends over up to length bytes. - /// If the stream has less than length bytes after the specified starting position, the created buffer covers only as many - /// bytes as available in the stream. In either case, the Length and the Capacity properties of the created - /// buffer are set accordingly: Capacity - number of bytes between positionInStream and the stream capacity end, - /// but not more than length; Length - number of bytes between positionInStream and the stream - /// length end, or zero if positionInStream is beyond stream length end, but not more than length. - ///
- /// A memory stream to share the data memory with the buffer being created. - /// The position of the shared memory region. - /// The maximum size of the shared memory region. - /// A new IBuffer backed by the same memory as this specified stream. - public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream underlyingStream, int positionInStream, int length) - { - // The naming inconsistency with (Byte []).AsBuffer is intentional: as this extension method will appear on - // MemoryStream, consistency with method names on MemoryStream is more important. There we already have an API - // called GetBuffer which returns the underlying array. - - if (underlyingStream == null) - throw new ArgumentNullException(nameof(underlyingStream)); - - if (positionInStream < 0) - throw new ArgumentOutOfRangeException(nameof(positionInStream)); - - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length)); - - if (underlyingStream.Length < positionInStream) - throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_StreamPositionBeyondEOS); - - ArraySegment streamData; - - if (!underlyingStream.TryGetBuffer(out streamData)) - { - throw new UnauthorizedAccessException(global::Windows.Storage.Streams.SR.UnauthorizedAccess_InternalBuffer); - } - - int originInStream = streamData.Offset; - int buffCapacity = Math.Min(length, underlyingStream.Capacity - positionInStream); - int buffLength = Math.Max(0, Math.Min(length, ((int)underlyingStream.Length) - positionInStream)); - return new WindowsRuntimeBuffer(streamData.Array!, originInStream + positionInStream, buffLength, buffCapacity); - } - - - public static Stream AsStream(this IBuffer source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - byte[] dataArr; - int dataOffs; - if (source.TryGetUnderlyingData(out dataArr, out dataOffs)) - { - Debug.Assert(source.Capacity < int.MaxValue); - return new WindowsRuntimeBufferMemoryStream(source, dataArr, dataOffs); - } - - if (!WindowsRuntimeMarshal.TryGetDataUnsafe(source, out IntPtr sourceBuff)) - { + + if (buffer == otherBuffer) + return true; + + byte[] thisDataArr, otherDataArr; + int thisDataOffs, otherDataOffs; + + bool thisIsManaged = buffer.TryGetUnderlyingData(out thisDataArr, out thisDataOffs); + bool otherIsManaged = otherBuffer.TryGetUnderlyingData(out otherDataArr, out otherDataOffs); + + if (thisIsManaged != otherIsManaged) + return false; + + if (thisIsManaged) + return (thisDataArr == otherDataArr) && (thisDataOffs == otherDataOffs); + + if (!WindowsRuntimeBufferMarshal.TryGetDataUnsafe(buffer, out byte* thisBuff) || + !WindowsRuntimeBufferMarshal.TryGetDataUnsafe(otherBuffer, out byte* otherBuff)) + { + return false; + } + + return thisBuff == otherBuff; + } + +#endregion Access to underlying array optimised for IBuffers backed by managed arrays (to avoid pinning) + + +#region Extensions for co-operation with memory streams (share mem stream data; expose data as managed/unmanaged mem stream) + /// + /// Creates a new IBuffer instance backed by the same memory as is backing the specified MemoryStream. + /// The MemoryStream may re-sized in future, as a result the stream will be backed by a different memory region. + /// In such case, the buffer created by this method will remain backed by the memory behind the stream at the time the buffer was created.
+ /// This method can throw an ObjectDisposedException if the specified stream is closed.
+ /// This method can throw an UnauthorizedAccessException if the specified stream cannot expose its underlying memory buffer. + ///
+ /// A memory stream to share the data memory with the buffer being created. + /// A new IBuffer backed by the same memory as this specified stream. + // The naming inconsistency with (Byte []).AsBuffer is intentional: as this extension method will appear on + // MemoryStream, consistency with method names on MemoryStream is more important. There we already have an API + // called GetBuffer which returns the underlying array. + public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream underlyingStream) + { + if (underlyingStream == null) + throw new ArgumentNullException(nameof(underlyingStream)); + + ArraySegment streamData; + if (!underlyingStream.TryGetBuffer(out streamData)) + { + throw new UnauthorizedAccessException(global::Windows.Storage.Streams.SR.UnauthorizedAccess_InternalBuffer); + } + return new WindowsRuntimeBuffer(streamData.Array!, (int)streamData.Offset, (int)underlyingStream.Length, underlyingStream.Capacity); + } + + + /// + /// Creates a new IBuffer instance backed by the same memory as is backing the specified MemoryStream. + /// The MemoryStream may re-sized in future, as a result the stream will be backed by a different memory region. + /// In such case buffer created by this method will remain backed by the memory behind the stream at the time the buffer was created.
+ /// This method can throw an ObjectDisposedException if the specified stream is closed.
+ /// This method can throw an UnauthorizedAccessException if the specified stream cannot expose its underlying memory buffer. + /// The created buffer begins at position positionInStream in the stream and extends over up to length bytes. + /// If the stream has less than length bytes after the specified starting position, the created buffer covers only as many + /// bytes as available in the stream. In either case, the Length and the Capacity properties of the created + /// buffer are set accordingly: Capacity - number of bytes between positionInStream and the stream capacity end, + /// but not more than length; Length - number of bytes between positionInStream and the stream + /// length end, or zero if positionInStream is beyond stream length end, but not more than length. + ///
+ /// A memory stream to share the data memory with the buffer being created. + /// The position of the shared memory region. + /// The maximum size of the shared memory region. + /// A new IBuffer backed by the same memory as this specified stream. + public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream underlyingStream, int positionInStream, int length) + { + // The naming inconsistency with (Byte []).AsBuffer is intentional: as this extension method will appear on + // MemoryStream, consistency with method names on MemoryStream is more important. There we already have an API + // called GetBuffer which returns the underlying array. + + if (underlyingStream == null) + throw new ArgumentNullException(nameof(underlyingStream)); + + if (positionInStream < 0) + throw new ArgumentOutOfRangeException(nameof(positionInStream)); + + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length)); + + if (underlyingStream.Length < positionInStream) + throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_StreamPositionBeyondEOS); + + ArraySegment streamData; + + if (!underlyingStream.TryGetBuffer(out streamData)) + { + throw new UnauthorizedAccessException(global::Windows.Storage.Streams.SR.UnauthorizedAccess_InternalBuffer); + } + + int originInStream = streamData.Offset; + int buffCapacity = Math.Min(length, underlyingStream.Capacity - positionInStream); + int buffLength = Math.Max(0, Math.Min(length, ((int)underlyingStream.Length) - positionInStream)); + return new WindowsRuntimeBuffer(streamData.Array!, originInStream + positionInStream, buffLength, buffCapacity); + } + + + public static unsafe Stream AsStream(this IBuffer source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + byte[] dataArr; + int dataOffs; + if (source.TryGetUnderlyingData(out dataArr, out dataOffs)) + { + Debug.Assert(source.Capacity < int.MaxValue); + return new WindowsRuntimeBufferMemoryStream(source, dataArr, dataOffs); + } + + if (!WindowsRuntimeBufferMarshal.TryGetDataUnsafe(source, out byte* sourceBuff)) + { throw new InvalidCastException(); - } - - return new WindowsRuntimeBufferUnmanagedMemoryStream(source, sourceBuff); - } - -#endregion Extensions for co-operation with memory streams (share mem stream data; expose data as managed/unmanaged mem stream) - - -#region Extensions for direct by-offset access to buffer data elements - - public static byte GetByte(this IBuffer source, uint byteOffset) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (source.Length <= byteOffset) throw new ArgumentException("The specified buffer offset is not within the buffer length."); - - byte[] srcDataArr; - int srcDataOffs; - if (source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs)) - { - return srcDataArr[srcDataOffs + byteOffset]; - } - - Span srcSpan = source.GetSpanForCapacityUnsafe(byteOffset); + } + + return new WindowsRuntimeBufferUnmanagedMemoryStream(source, sourceBuff); + } + +#endregion Extensions for co-operation with memory streams (share mem stream data; expose data as managed/unmanaged mem stream) + + +#region Extensions for direct by-offset access to buffer data elements + + public static byte GetByte(this IBuffer source, uint byteOffset) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (source.Length <= byteOffset) throw new ArgumentException("The specified buffer offset is not within the buffer length."); + + byte[] srcDataArr; + int srcDataOffs; + if (source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs)) + { + return srcDataArr[srcDataOffs + byteOffset]; + } + + Span srcSpan = source.GetSpanForCapacityUnsafe(byteOffset); byte value = srcSpan[0]; // Ensure source stays alive while we read values. GC.KeepAlive(source); - return value; + return value; } - #endregion Extensions for direct by-offset access to buffer data elements + #endregion Extensions for direct by-offset access to buffer data elements - #region Private plumbing + #region Private plumbing -#if NET - private sealed class StreamWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails + private sealed class WindowsRuntimeBufferMemoryStream : MemoryStream { - public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + private readonly IBuffer _sourceBuffer; + + internal WindowsRuntimeBufferMemoryStream(IBuffer sourceBuffer, byte[] dataArr, int dataOffs) + : base(dataArr, dataOffs, (int)sourceBuffer.Capacity, writable: true) { - return new ComWrappers.ComInterfaceEntry[] - { - new ComWrappers.ComInterfaceEntry - { - IID = global::ABI.System.IDisposableMethods.IID, - Vtable = global::ABI.System.IDisposableMethods.AbiToProjectionVftablePtr - }, - }; + _sourceBuffer = sourceBuffer; + + SetLength((long)sourceBuffer.Length); } - } -#endif - -#if NET - [global::WinRT.WinRTExposedType(typeof(StreamWinRTTypeDetails))] -#endif - private sealed class WindowsRuntimeBufferMemoryStream : MemoryStream - { - private readonly IBuffer _sourceBuffer; - - internal WindowsRuntimeBufferMemoryStream(IBuffer sourceBuffer, byte[] dataArr, int dataOffs) - : base(dataArr, dataOffs, (int)sourceBuffer.Capacity, writable: true) - { - _sourceBuffer = sourceBuffer; - - SetLength((long)sourceBuffer.Length); - } - + public override void SetLength(long value) { base.SetLength(value); @@ -515,8 +490,8 @@ public override void SetLength(long value) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } - + } + public override void Write(byte[] buffer, int offset, int count) { base.Write(buffer, offset, count); @@ -526,7 +501,6 @@ public override void Write(byte[] buffer, int offset, int count) _sourceBuffer.Length = (uint)Length; } -#if NET public override void Write(ReadOnlySpan buffer) { base.Write(buffer); @@ -534,18 +508,16 @@ public override void Write(ReadOnlySpan buffer) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } -#endif - + } + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { await base.WriteAsync(buffer, offset, count, cancellationToken); // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } + } -#if NET public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { await base.WriteAsync(buffer, cancellationToken); @@ -553,9 +525,8 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } -#endif - + } + public override void WriteByte(byte value) { base.WriteByte(value); @@ -563,27 +534,24 @@ public override void WriteByte(byte value) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } - } // class WindowsRuntimeBufferMemoryStream - -#if NET - [global::WinRT.WinRTExposedType(typeof(StreamWinRTTypeDetails))] -#endif - private sealed class WindowsRuntimeBufferUnmanagedMemoryStream : UnmanagedMemoryStream - { - // We need this class because if we construct an UnmanagedMemoryStream on an IBuffer backed by native memory, - // we must keep around a reference to the IBuffer from which we got the memory pointer. Otherwise the ref count - // of the underlying COM object may drop to zero and the memory may get freed. - - private readonly IBuffer _sourceBuffer; - - internal unsafe WindowsRuntimeBufferUnmanagedMemoryStream(IBuffer sourceBuffer, IntPtr dataPtr) - - : base((byte*)dataPtr, (long)sourceBuffer.Length, (long)sourceBuffer.Capacity, FileAccess.ReadWrite) - { - _sourceBuffer = sourceBuffer; - } - + } + } // class WindowsRuntimeBufferMemoryStream + + private sealed class WindowsRuntimeBufferUnmanagedMemoryStream : UnmanagedMemoryStream + { + // We need this class because if we construct an UnmanagedMemoryStream on an IBuffer backed by native memory, + // we must keep around a reference to the IBuffer from which we got the memory pointer. Otherwise the ref count + // of the underlying COM object may drop to zero and the memory may get freed. + + private readonly IBuffer _sourceBuffer; + + internal unsafe WindowsRuntimeBufferUnmanagedMemoryStream(IBuffer sourceBuffer, byte* dataPtr) + + : base(dataPtr, (long)sourceBuffer.Length, (long)sourceBuffer.Capacity, FileAccess.ReadWrite) + { + _sourceBuffer = sourceBuffer; + } + public override void SetLength(long value) { base.SetLength(value); @@ -591,8 +559,8 @@ public override void SetLength(long value) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } - + } + public override void Write(byte[] buffer, int offset, int count) { base.Write(buffer, offset, count); @@ -602,7 +570,6 @@ public override void Write(byte[] buffer, int offset, int count) _sourceBuffer.Length = (uint)Length; } -#if NET public override void Write(ReadOnlySpan buffer) { base.Write(buffer); @@ -610,18 +577,16 @@ public override void Write(ReadOnlySpan buffer) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } -#endif - + } + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { await base.WriteAsync(buffer, offset, count, cancellationToken); // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } + } -#if NET public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { await base.WriteAsync(buffer, cancellationToken); @@ -629,9 +594,8 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } -#endif - + } + public override void WriteByte(byte value) { base.WriteByte(value); @@ -639,25 +603,25 @@ public override void WriteByte(byte value) // Length is limited by Capacity which should be a valid value. // Therefore this cast is safe. _sourceBuffer.Length = (uint)Length; - } - } // class WindowsRuntimeBufferUnmanagedMemoryStream - + } + } // class WindowsRuntimeBufferUnmanagedMemoryStream + private static unsafe Span GetSpanForCapacityUnsafe(this IBuffer buffer, uint offset) { Debug.Assert(0 <= offset); Debug.Assert(offset < buffer.Capacity); - if (!WindowsRuntimeMarshal.TryGetDataUnsafe(buffer, out IntPtr buffPtr)) + if (!WindowsRuntimeBufferMarshal.TryGetDataUnsafe(buffer, out byte* buffPtr)) { throw new InvalidCastException(); } - var span = new Span((byte*)buffPtr + offset, (int)(buffer.Capacity - offset)); - GC.KeepAlive(buffer); - return span; - } -#endregion Private plumbing - } // class WindowsRuntimeBufferExtensions -} // namespace - -// WindowsRuntimeBufferExtensions.cs + var span = new Span(buffPtr + offset, (int)(buffer.Capacity - offset)); + GC.KeepAlive(buffer); + return span; + } +#endregion Private plumbing + } // class WindowsRuntimeBufferExtensions +} // namespace + +// WindowsRuntimeBufferExtensions.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs similarity index 71% rename from src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs rename to src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs index 6324350db..9cb1eace8 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs @@ -1,146 +1,138 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace System.Runtime.InteropServices.WindowsRuntime -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using global::Windows.Foundation; - using global::Windows.Storage.Streams; - using WinRT; - -#nullable enable - /// - /// An unsafe class that provides a set of methods to access the underlying data representations of WinRT types. - /// -#if EMBED || !NET // for netstandard this type conflicts with the type in the BCL so make it internal - internal -#else - public -#endif - static partial class WindowsRuntimeMarshal - { - /// - /// Returns a pointer to the underlying data representation of the . - /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. - /// - /// The buffer to get the data pointer for. - /// The pointer to the underlying data representation of the buffer. - /// Whether the data was successfully retrieved. - /// Thrown if invoking IBufferByteAccess::Buffer on the input buffer fails. - public static unsafe bool TryGetDataUnsafe( -#if NET - [NotNullWhen(true)] -#endif - IBuffer? buffer, out IntPtr dataPtr) - { - if (buffer == null) - { - dataPtr = IntPtr.Zero; - return false; - } - - if (ComWrappersSupport.TryUnwrapObject(buffer, out var unwrapped) && - unwrapped.TryAs(global::WinRT.Interop.IID.IID_IBufferByteAccess, out IntPtr ThisPtr) >= 0) - { - try - { - IntPtr __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval)); - dataPtr = __retval; - return true; - } - finally - { - Marshal.Release(ThisPtr); - } - } - - if (buffer is IBufferByteAccess managedBuffer) - { - dataPtr = managedBuffer.Buffer; - return true; - } - - dataPtr = IntPtr.Zero; - return false; - } - - /// - /// Returns a pointer to the underlying data representation of the . - /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. - /// - /// The buffer to get the data pointer for. - /// The pointer to the underlying data representation of the buffer. - /// The capacity of the buffer. - /// Whether the data was successfully retrieved. - /// Thrown if invoking IMemoryBufferByteAccess::Buffer on the input buffer fails. - public static unsafe bool TryGetDataUnsafe( -#if NET - [NotNullWhen(true)] -#endif - IMemoryBufferReference? buffer, out IntPtr dataPtr, out uint capacity) - { - if (buffer == null) - { - dataPtr = IntPtr.Zero; - capacity = 0; - return false; - } - - if (ComWrappersSupport.TryUnwrapObject(buffer, out var unwrapped) && - unwrapped.TryAs(global::WinRT.Interop.IID.IID_IMemoryBufferByteAccess, out IntPtr ThisPtr) >= 0) - { - try - { - IntPtr __retval = default; - uint __capacity = 0; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval, &__capacity)); - dataPtr = __retval; - capacity = __capacity; - return true; - } - finally - { - Marshal.Release(ThisPtr); - } - } - - dataPtr = IntPtr.Zero; - capacity = 0; - return false; - } - - /// - /// Tries to get an array segment from the underlying buffer. The return value indicates the success of the operation. - /// - /// The buffer to get the array segment for. - /// When this method returns, contains the array segment retrieved from the underlying buffer. If the method fails, the method returns a default array segment. - public static bool TryGetArray( -#if NET - [NotNullWhen(true)] -# endif - IBuffer? buffer, out ArraySegment array) - { - if (buffer == null) - { - array = default; - return false; - } - - // If buffer is backed by a managed array, return it - if (buffer.TryGetUnderlyingData(out byte[]? srcDataArr, out int srcDataOffs)) - { - array = new ArraySegment(srcDataArr, offset: srcDataOffs, count: (int)buffer.Length); - return true; - } - - array = default; - return false; - } - } // class WindowsRuntimeMarshal -#nullable restore -} // namespace - -// WindowsRuntimeMarshal.cs +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using global::Windows.Foundation; + using global::Windows.Storage.Streams; + +#nullable enable + /// + /// An unsafe class that provides a set of methods to access the underlying data representations of WinRT types. + /// + public static partial class WindowsRuntimeBufferMarshal + { + /// + /// Returns a pointer to the underlying data representation of the . + /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. + /// + /// The buffer to get the data pointer for. + /// The pointer to the underlying data representation of the buffer. + /// Whether the data was successfully retrieved. + /// Thrown if invoking IBufferByteAccess::Buffer on the input buffer fails. + public static unsafe bool TryGetDataUnsafe( + [NotNullWhen(true)] + IBuffer? buffer, + out byte* dataPtr) + { + if (buffer == null) + { + dataPtr = null; + return false; + } + + if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && + unwrapped.TryAsUnsafe(global::ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IBufferByteAccess, out nint thisPtr)) + { + try + { + byte* __retval = null; + RestrictedErrorInfo.ThrowExceptionForHR(global::ABI.Windows.Storage.Streams.IBufferByteAccessVftbl.GetBufferUnsafe((void*)thisPtr, &__retval)); + dataPtr = __retval; + return true; + } + finally + { + Marshal.Release(thisPtr); + } + } + + if (buffer is IBufferByteAccess managedBuffer) + { + dataPtr = managedBuffer.Buffer; + return true; + } + + dataPtr = null; + return false; + } + + /// + /// Returns a pointer to the underlying data representation of the . + /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. + /// + /// The buffer to get the data pointer for. + /// The pointer to the underlying data representation of the buffer. + /// The capacity of the buffer. + /// Whether the data was successfully retrieved. + /// Thrown if invoking IMemoryBufferByteAccess::Buffer on the input buffer fails. + public static unsafe bool TryGetDataUnsafe( + [NotNullWhen(true)] + IMemoryBufferReference? buffer, + out byte* dataPtr, + out uint capacity) + { + if (buffer == null) + { + dataPtr = null; + capacity = 0; + return false; + } + + if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && + unwrapped.TryAsUnsafe(global::ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IMemoryBufferByteAccess, out nint thisPtr)) + { + try + { + byte* __retval = null; + uint __capacity = 0; + RestrictedErrorInfo.ThrowExceptionForHR(global::ABI.Windows.Storage.Streams.IMemoryBufferByteAccessVftbl.GetBufferUnsafe((void*)thisPtr, &__retval, &__capacity)); + dataPtr = __retval; + capacity = __capacity; + return true; + } + finally + { + Marshal.Release(thisPtr); + } + } + + dataPtr = null; + capacity = 0; + return false; + } + + /// + /// Tries to get an array segment from the underlying buffer. The return value indicates the success of the operation. + /// + /// The buffer to get the array segment for. + /// When this method returns, contains the array segment retrieved from the underlying buffer. If the method fails, the method returns a default array segment. + public static bool TryGetArray( + [NotNullWhen(true)] + IBuffer? buffer, + out ArraySegment array) + { + if (buffer == null) + { + array = default; + return false; + } + + // If buffer is backed by a managed array, return it + if (buffer.TryGetUnderlyingData(out byte[]? srcDataArr, out int srcDataOffs)) + { + array = new ArraySegment(srcDataArr, offset: srcDataOffs, count: (int)buffer.Length); + return true; + } + + array = default; + return false; + } + } // class WindowsRuntimeMarshal +#nullable restore +} // namespace + +// WindowsRuntimeMarshal.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs index ed99aca1a..5171b8a7b 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs @@ -7,9 +7,7 @@ namespace System.IO { using System.ComponentModel; using System.Diagnostics; -#if NET using System.Diagnostics.CodeAnalysis; -#endif using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -20,12 +18,7 @@ namespace System.IO /// Contains extension methods for conversion between WinRT streams and managed streams. /// This class is the public facade for the stream adapters library. ///
-#if EMBED - internal -#else - public -#endif - static class WindowsRuntimeStreamExtensions + public static class WindowsRuntimeStreamExtensions { #region Constants and static Fields @@ -39,9 +32,6 @@ private static readonly ConditionalWeakTable #endregion Constants and static Fields - -#if NET - #region Helpers #if DEBUG @@ -344,8 +334,6 @@ private static NetFxToWinRtStreamAdapter AsWindowsRuntimeStreamInternalFactoryHe } #endregion NetFx-to-WinRt conversion -#endif - } // class WindowsRuntimeStreamExtensions } // namespace diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs index c3f5d9540..7f78e5990 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs @@ -10,11 +10,18 @@ namespace ABI.Windows.Storage // Available in 14393 (RS1) and later internal static class IStorageFolderHandleAccessMethods { -#if NET - public static global::System.Guid IID { get; } = new(new global::System.ReadOnlySpan(new byte[] { 0x8F, 0x93, 0x19, 0xDF, 0x62, 0x54, 0xA0, 0x48, 0xBE, 0x65, 0xD2, 0xA3, 0x27, 0x1A, 0x08, 0xD6 })); -#else - public static global::System.Guid IID { get; } = new(0xDF19938F, 0x5462, 0x48A0, 0xBE, 0x65, 0xD2, 0xA3, 0x27, 0x1A, 0x08, 0xD6); -#endif + private static ref readonly Guid IID_IStorageFolderHandleAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x8F, 0x93, 0x19, 0xDF, 0x62, 0x54, 0xA0, 0x48, 0xBE, 0x65, 0xD2, 0xA3, 0x27, 0x1A, 0x08, 0xD6 + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } public static unsafe SafeFileHandle Create( global::Windows.Storage.IStorageFolder storageFolder, @@ -25,33 +32,27 @@ public static unsafe SafeFileHandle Create( global::Windows.Storage.HANDLE_OPTIONS options, IntPtr oplockBreakingHandler) { - global::WinRT.ObjectReferenceValue obj = default; - try + if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(storageFolder, out WindowsRuntimeObjectReference unwrapped) && + unwrapped.TryAsUnsafe(IID_IStorageFolderHandleAccess, out void* thisPtr)) { - obj = global::WinRT.MarshalInspectable.CreateMarshaler2(storageFolder, IID); + SafeFileHandle interopHandle = default; + IntPtr _interopHandle = default; + try + { + fixed (char* fileNamePtr = fileName) + { + RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3] + (thisPtr, (IntPtr)fileNamePtr, creationOptions, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); + } + } + finally + { + interopHandle = new SafeFileHandle(_interopHandle, true); + global::WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeObjectMarshaller.Free(thisPtr); + } + return interopHandle; } - catch (Exception) - { - return null; - } - - SafeFileHandle interopHandle = default; - IntPtr _interopHandle = default; - try - { - var ThisPtr = obj.GetAbi(); - fixed (char* fileNamePtr = fileName) - { - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3] - (ThisPtr, (IntPtr)fileNamePtr, creationOptions, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); - } - } - finally - { - interopHandle = new SafeFileHandle(_interopHandle, true); - obj.Dispose(); - } - return interopHandle; + return null; } } } diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs index 7946e0186..3f964a704 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs @@ -1,51 +1,52 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace ABI.Windows.Storage -{ - using global::Microsoft.Win32.SafeHandles; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace ABI.Windows.Storage +{ + using global::Microsoft.Win32.SafeHandles; // Available in 14393 (RS1) and later - internal static class IStorageItemHandleAccessMethods + internal static class IStorageItemHandleAccessMethods { -#if NET - public static global::System.Guid IID { get; } = new(new global::System.ReadOnlySpan(new byte[] { 0xB2, 0x96, 0xA2, 0x5C, 0x25, 0x2C, 0x22, 0x4D, 0xB7, 0x85, 0xB8, 0x85, 0xC8, 0x20, 0x1E, 0x6A })); -#else - public static global::System.Guid IID { get; } = new(0x5CA296B2, 0x2C25, 0x4D22, 0xB7, 0x85, 0xB8, 0x85, 0xC8, 0x20, 0x1E, 0x6A); -#endif - - public static unsafe SafeFileHandle Create( - global::Windows.Storage.IStorageFile storageFile, - global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, - global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, - global::Windows.Storage.HANDLE_OPTIONS options, - IntPtr oplockBreakingHandler) - { - global::WinRT.ObjectReferenceValue obj = default; - try - { - obj = global::WinRT.MarshalInspectable.CreateMarshaler2(storageFile, IID); - } - catch (Exception) + private static ref readonly Guid IID_IStorageItemHandleAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - return null; + ReadOnlySpan data = + [ + 0xB2, 0x96, 0xA2, 0x5C, 0x25, 0x2C, 0x22, 0x4D, 0xB7, 0x85, 0xB8, 0x85, 0xC8, 0x20, 0x1E, 0x6A + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); } + } - SafeFileHandle interopHandle = default; - IntPtr _interopHandle = default; - try + public static unsafe SafeFileHandle Create( + global::Windows.Storage.IStorageFile storageFile, + global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, + global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, + global::Windows.Storage.HANDLE_OPTIONS options, + IntPtr oplockBreakingHandler) + { + if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(storageFile, out WindowsRuntimeObjectReference unwrapped) && + unwrapped.TryAsUnsafe(IID_IStorageItemHandleAccess, out void* thisPtr)) { - var ThisPtr = obj.GetAbi(); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3] - (ThisPtr, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); - } - finally - { - interopHandle = new SafeFileHandle(_interopHandle, true); - obj.Dispose(); - } - return interopHandle; - } - } -} + SafeFileHandle interopHandle = default; + IntPtr _interopHandle = default; + try + { + RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3] + (thisPtr, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); + } + finally + { + interopHandle = new SafeFileHandle(_interopHandle, true); + global::WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeObjectMarshaller.Free(thisPtr); + } + return interopHandle; + } + return null; + } + } +} diff --git a/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs index 6bb942a8d..9fc7c3019 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs @@ -1,297 +1,289 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.IO -{ - using System.Diagnostics; - using System.Threading.Tasks; - using Microsoft.Win32.SafeHandles; - using global::Windows.Storage; - using global::Windows.Storage.FileProperties; - using global::Windows.Storage.Streams; - using System.Runtime.InteropServices; - - /// - /// Contains extension methods that provide convenience helpers for WinRT IO. - /// -#if EMBED - internal -#else - public -#endif - static class WindowsRuntimeStorageExtensions +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.IO +{ + using System.Diagnostics; + using System.Threading.Tasks; + using Microsoft.Win32.SafeHandles; + using global::Windows.Storage; + using global::Windows.Storage.FileProperties; + using global::Windows.Storage.Streams; + using System.Runtime.InteropServices; + + /// + /// Contains extension methods that provide convenience helpers for WinRT IO. + /// + public static class WindowsRuntimeStorageExtensions { - // Net5-specific extension methods -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - public static Task OpenStreamForReadAsync(this IStorageFile windowsRuntimeFile) - { - if (windowsRuntimeFile == null) - throw new ArgumentNullException(nameof(windowsRuntimeFile)); - - return OpenStreamForReadAsyncCore(windowsRuntimeFile); - } - + public static Task OpenStreamForReadAsync(this IStorageFile windowsRuntimeFile) + { + if (windowsRuntimeFile == null) + throw new ArgumentNullException(nameof(windowsRuntimeFile)); + + return OpenStreamForReadAsyncCore(windowsRuntimeFile); + } + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - private static async Task OpenStreamForReadAsyncCore(this IStorageFile windowsRuntimeFile) - { - Debug.Assert(windowsRuntimeFile != null); - - try - { - IRandomAccessStream windowsRuntimeStream = await windowsRuntimeFile.OpenAsync(FileAccessMode.Read) - .AsTask().ConfigureAwait(continueOnCapturedContext: false); - Stream managedStream = windowsRuntimeStream.AsStreamForRead(); - return managedStream; - } - catch (Exception ex) - { - // From this API, managed dev expect IO exceptions for "something wrong": - WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); - return null; - } + private static async Task OpenStreamForReadAsyncCore(this IStorageFile windowsRuntimeFile) + { + Debug.Assert(windowsRuntimeFile != null); + + try + { + IRandomAccessStream windowsRuntimeStream = await windowsRuntimeFile.OpenAsync(FileAccessMode.Read) + .AsTask().ConfigureAwait(continueOnCapturedContext: false); + Stream managedStream = windowsRuntimeStream.AsStreamForRead(); + return managedStream; + } + catch (Exception ex) + { + // From this API, managed dev expect IO exceptions for "something wrong": + WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); + return null; + } } [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - public static Task OpenStreamForWriteAsync(this IStorageFile windowsRuntimeFile) - { - if (windowsRuntimeFile == null) - throw new ArgumentNullException(nameof(windowsRuntimeFile)); - - return OpenStreamForWriteAsyncCore(windowsRuntimeFile, 0); + public static Task OpenStreamForWriteAsync(this IStorageFile windowsRuntimeFile) + { + if (windowsRuntimeFile == null) + throw new ArgumentNullException(nameof(windowsRuntimeFile)); + + return OpenStreamForWriteAsyncCore(windowsRuntimeFile, 0); } [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - private static async Task OpenStreamForWriteAsyncCore(this IStorageFile windowsRuntimeFile, long offset) - { - Debug.Assert(windowsRuntimeFile != null); - Debug.Assert(offset >= 0); - - try - { - IRandomAccessStream windowsRuntimeStream = await windowsRuntimeFile.OpenAsync(FileAccessMode.ReadWrite) - .AsTask().ConfigureAwait(continueOnCapturedContext: false); - Stream managedStream = windowsRuntimeStream.AsStreamForWrite(); - managedStream.Position = offset; - return managedStream; - } - catch (Exception ex) - { - // From this API, managed dev expect IO exceptions for "something wrong": - WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); - return null; - } - } - + private static async Task OpenStreamForWriteAsyncCore(this IStorageFile windowsRuntimeFile, long offset) + { + Debug.Assert(windowsRuntimeFile != null); + Debug.Assert(offset >= 0); + + try + { + IRandomAccessStream windowsRuntimeStream = await windowsRuntimeFile.OpenAsync(FileAccessMode.ReadWrite) + .AsTask().ConfigureAwait(continueOnCapturedContext: false); + Stream managedStream = windowsRuntimeStream.AsStreamForWrite(); + managedStream.Position = offset; + return managedStream; + } + catch (Exception ex) + { + // From this API, managed dev expect IO exceptions for "something wrong": + WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); + return null; + } + } + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - public static Task OpenStreamForReadAsync(this IStorageFolder rootDirectory, string relativePath) - { - if (rootDirectory == null) - throw new ArgumentNullException(nameof(rootDirectory)); - - if (relativePath == null) - throw new ArgumentNullException(nameof(relativePath)); - - if (string.IsNullOrWhiteSpace(relativePath)) - throw new ArgumentException(global::Windows.Storage.SR.Argument_RelativePathMayNotBeWhitespaceOnly, nameof(relativePath)); - - return OpenStreamForReadAsyncCore(rootDirectory, relativePath); - } - + public static Task OpenStreamForReadAsync(this IStorageFolder rootDirectory, string relativePath) + { + if (rootDirectory == null) + throw new ArgumentNullException(nameof(rootDirectory)); + + if (relativePath == null) + throw new ArgumentNullException(nameof(relativePath)); + + if (string.IsNullOrWhiteSpace(relativePath)) + throw new ArgumentException(global::Windows.Storage.SR.Argument_RelativePathMayNotBeWhitespaceOnly, nameof(relativePath)); + + return OpenStreamForReadAsyncCore(rootDirectory, relativePath); + } + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - private static async Task OpenStreamForReadAsyncCore(this IStorageFolder rootDirectory, string relativePath) - { - Debug.Assert(rootDirectory != null); - Debug.Assert(!string.IsNullOrWhiteSpace(relativePath)); - - try - { - IStorageFile windowsRuntimeFile = await rootDirectory.GetFileAsync(relativePath) - .AsTask().ConfigureAwait(continueOnCapturedContext: false); - Stream managedStream = await windowsRuntimeFile.OpenStreamForReadAsync() - .ConfigureAwait(continueOnCapturedContext: false); - return managedStream; - } - catch (Exception ex) - { - // From this API, managed dev expect IO exceptions for "something wrong": - WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); - return null; - } - } - + private static async Task OpenStreamForReadAsyncCore(this IStorageFolder rootDirectory, string relativePath) + { + Debug.Assert(rootDirectory != null); + Debug.Assert(!string.IsNullOrWhiteSpace(relativePath)); + + try + { + IStorageFile windowsRuntimeFile = await rootDirectory.GetFileAsync(relativePath) + .AsTask().ConfigureAwait(continueOnCapturedContext: false); + Stream managedStream = await windowsRuntimeFile.OpenStreamForReadAsync() + .ConfigureAwait(continueOnCapturedContext: false); + return managedStream; + } + catch (Exception ex) + { + // From this API, managed dev expect IO exceptions for "something wrong": + WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); + return null; + } + } + [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - public static Task OpenStreamForWriteAsync(this IStorageFolder rootDirectory, string relativePath, - CreationCollisionOption creationCollisionOption) - { - if (rootDirectory == null) - throw new ArgumentNullException(nameof(rootDirectory)); - - if (relativePath == null) - throw new ArgumentNullException(nameof(relativePath)); - - if (string.IsNullOrWhiteSpace(relativePath)) - throw new ArgumentException(global::Windows.Storage.SR.Argument_RelativePathMayNotBeWhitespaceOnly, nameof(relativePath)); - - return OpenStreamForWriteAsyncCore(rootDirectory, relativePath, creationCollisionOption); + public static Task OpenStreamForWriteAsync(this IStorageFolder rootDirectory, string relativePath, + CreationCollisionOption creationCollisionOption) + { + if (rootDirectory == null) + throw new ArgumentNullException(nameof(rootDirectory)); + + if (relativePath == null) + throw new ArgumentNullException(nameof(relativePath)); + + if (string.IsNullOrWhiteSpace(relativePath)) + throw new ArgumentException(global::Windows.Storage.SR.Argument_RelativePathMayNotBeWhitespaceOnly, nameof(relativePath)); + + return OpenStreamForWriteAsyncCore(rootDirectory, relativePath, creationCollisionOption); } [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] - private static async Task OpenStreamForWriteAsyncCore(this IStorageFolder rootDirectory, string relativePath, - CreationCollisionOption creationCollisionOption) - { - Debug.Assert(rootDirectory != null); - Debug.Assert(!string.IsNullOrWhiteSpace(relativePath)); - - Debug.Assert(creationCollisionOption == CreationCollisionOption.FailIfExists - || creationCollisionOption == CreationCollisionOption.GenerateUniqueName - || creationCollisionOption == CreationCollisionOption.OpenIfExists - || creationCollisionOption == CreationCollisionOption.ReplaceExisting, - "The specified creationCollisionOption has a value that is not a value we considered when devising the" - + " policy about Append-On-OpenIfExists used in this method. Apparently a new enum value was added to the" - + " CreationCollisionOption type and we need to make sure that the policy still makes sense."); - - try - { - // Open file and set up default options for opening it: - - IStorageFile windowsRuntimeFile = await rootDirectory.CreateFileAsync(relativePath, creationCollisionOption) - .AsTask().ConfigureAwait(continueOnCapturedContext: false); - long offset = 0; - - // If the specified creationCollisionOption was OpenIfExists, then we will try to APPEND, otherwise we will OVERWRITE: - - if (creationCollisionOption == CreationCollisionOption.OpenIfExists) - { - BasicProperties fileProperties = await windowsRuntimeFile.GetBasicPropertiesAsync() - .AsTask().ConfigureAwait(continueOnCapturedContext: false); - ulong fileSize = fileProperties.Size; - - Debug.Assert(fileSize <= long.MaxValue, ".NET streams assume that file sizes are not larger than Int64.MaxValue," - + " so we are not supporting the situation where this is not the case."); - offset = checked((long)fileSize); - } - - // Now open a file with the correct options: - - Stream managedStream = await OpenStreamForWriteAsyncCore(windowsRuntimeFile, offset).ConfigureAwait(continueOnCapturedContext: false); - return managedStream; - } - catch (Exception ex) - { - // From this API, managed dev expect IO exceptions for "something wrong": - WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); - return null; - } + private static async Task OpenStreamForWriteAsyncCore(this IStorageFolder rootDirectory, string relativePath, + CreationCollisionOption creationCollisionOption) + { + Debug.Assert(rootDirectory != null); + Debug.Assert(!string.IsNullOrWhiteSpace(relativePath)); + + Debug.Assert(creationCollisionOption == CreationCollisionOption.FailIfExists + || creationCollisionOption == CreationCollisionOption.GenerateUniqueName + || creationCollisionOption == CreationCollisionOption.OpenIfExists + || creationCollisionOption == CreationCollisionOption.ReplaceExisting, + "The specified creationCollisionOption has a value that is not a value we considered when devising the" + + " policy about Append-On-OpenIfExists used in this method. Apparently a new enum value was added to the" + + " CreationCollisionOption type and we need to make sure that the policy still makes sense."); + + try + { + // Open file and set up default options for opening it: + + IStorageFile windowsRuntimeFile = await rootDirectory.CreateFileAsync(relativePath, creationCollisionOption) + .AsTask().ConfigureAwait(continueOnCapturedContext: false); + long offset = 0; + + // If the specified creationCollisionOption was OpenIfExists, then we will try to APPEND, otherwise we will OVERWRITE: + + if (creationCollisionOption == CreationCollisionOption.OpenIfExists) + { + BasicProperties fileProperties = await windowsRuntimeFile.GetBasicPropertiesAsync() + .AsTask().ConfigureAwait(continueOnCapturedContext: false); + ulong fileSize = fileProperties.Size; + + Debug.Assert(fileSize <= long.MaxValue, ".NET streams assume that file sizes are not larger than Int64.MaxValue," + + " so we are not supporting the situation where this is not the case."); + offset = checked((long)fileSize); + } + + // Now open a file with the correct options: + + Stream managedStream = await OpenStreamForWriteAsyncCore(windowsRuntimeFile, offset).ConfigureAwait(continueOnCapturedContext: false); + return managedStream; + } + catch (Exception ex) + { + // From this API, managed dev expect IO exceptions for "something wrong": + WinRtIOHelper.NativeExceptionToIOExceptionInfo(ex).Throw(); + return null; + } + } + + public static SafeFileHandle CreateSafeFileHandle( + this IStorageFile windowsRuntimeFile, + FileAccess access = FileAccess.ReadWrite, + FileShare share = FileShare.Read, + FileOptions options = FileOptions.None) + { + if (windowsRuntimeFile == null) + throw new ArgumentNullException(nameof(windowsRuntimeFile)); + + HANDLE_ACCESS_OPTIONS accessOptions = FileAccessToHandleAccessOptions(access); + HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); + HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); + + return global::ABI.Windows.Storage.IStorageItemHandleAccessMethods.Create( + windowsRuntimeFile, + accessOptions, + sharingOptions, + handleOptions, + IntPtr.Zero); + } + + public static SafeFileHandle CreateSafeFileHandle( + this IStorageFolder rootDirectory, + string relativePath, + FileMode mode) + { + return rootDirectory.CreateSafeFileHandle(relativePath, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite)); + } + + public static SafeFileHandle CreateSafeFileHandle( + this IStorageFolder rootDirectory, + string relativePath, + FileMode mode, + FileAccess access, + FileShare share = FileShare.Read, + FileOptions options = FileOptions.None) + { + if (rootDirectory == null) + throw new ArgumentNullException(nameof(rootDirectory)); + if (relativePath == null) + throw new ArgumentNullException(nameof(relativePath)); + + HANDLE_CREATION_OPTIONS creationOptions = FileModeToCreationOptions(mode); + HANDLE_ACCESS_OPTIONS accessOptions = FileAccessToHandleAccessOptions(access); + HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); + HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); + + return global::ABI.Windows.Storage.IStorageFolderHandleAccessMethods.Create( + rootDirectory, + relativePath, + creationOptions, + accessOptions, + sharingOptions, + handleOptions, + IntPtr.Zero); + } + + private static HANDLE_ACCESS_OPTIONS FileAccessToHandleAccessOptions(FileAccess access) => + access switch + { + FileAccess.ReadWrite => HANDLE_ACCESS_OPTIONS.HAO_READ | HANDLE_ACCESS_OPTIONS.HAO_WRITE, + FileAccess.Read => HANDLE_ACCESS_OPTIONS.HAO_READ, + FileAccess.Write => HANDLE_ACCESS_OPTIONS.HAO_WRITE, + _ => throw new ArgumentOutOfRangeException(nameof(access), access, null), + }; + + private static HANDLE_SHARING_OPTIONS FileShareToHandleSharingOptions(FileShare share) + { + if ((share & FileShare.Inheritable) != 0) + throw new NotSupportedException(global::Windows.Storage.SR.NotSupported_Inheritable); + if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) + throw new ArgumentOutOfRangeException(nameof(share), share, null); + + HANDLE_SHARING_OPTIONS sharingOptions = HANDLE_SHARING_OPTIONS.HSO_SHARE_NONE; + if ((share & FileShare.Read) != 0) + sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_READ; + if ((share & FileShare.Write) != 0) + sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_WRITE; + if ((share & FileShare.Delete) != 0) + sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_DELETE; + + return sharingOptions; + } + + private static HANDLE_OPTIONS FileOptionsToHandleOptions(FileOptions options) + { + if ((options & FileOptions.Encrypted) != 0) + throw new NotSupportedException(global::Windows.Storage.SR.NotSupported_Encrypted); + if (options != FileOptions.None && (options & + ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | + FileOptions.SequentialScan | (FileOptions)0x20000000 /* NoBuffering */)) != 0) + throw new ArgumentOutOfRangeException(nameof(options), options, null); + + return (HANDLE_OPTIONS)options; + } + + private static HANDLE_CREATION_OPTIONS FileModeToCreationOptions(FileMode mode) + { + if (mode < FileMode.CreateNew || mode > FileMode.Append) + throw new ArgumentOutOfRangeException(nameof(mode), mode, null); + + if (mode == FileMode.Append) + return HANDLE_CREATION_OPTIONS.HCO_CREATE_ALWAYS; + + return (HANDLE_CREATION_OPTIONS)mode; } -#endif - - public static SafeFileHandle CreateSafeFileHandle( - this IStorageFile windowsRuntimeFile, - FileAccess access = FileAccess.ReadWrite, - FileShare share = FileShare.Read, - FileOptions options = FileOptions.None) - { - if (windowsRuntimeFile == null) - throw new ArgumentNullException(nameof(windowsRuntimeFile)); - - HANDLE_ACCESS_OPTIONS accessOptions = FileAccessToHandleAccessOptions(access); - HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); - HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); - - return global::ABI.Windows.Storage.IStorageItemHandleAccessMethods.Create( - windowsRuntimeFile, - accessOptions, - sharingOptions, - handleOptions, - IntPtr.Zero); - } - - public static SafeFileHandle CreateSafeFileHandle( - this IStorageFolder rootDirectory, - string relativePath, - FileMode mode) - { - return rootDirectory.CreateSafeFileHandle(relativePath, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite)); - } - - public static SafeFileHandle CreateSafeFileHandle( - this IStorageFolder rootDirectory, - string relativePath, - FileMode mode, - FileAccess access, - FileShare share = FileShare.Read, - FileOptions options = FileOptions.None) - { - if (rootDirectory == null) - throw new ArgumentNullException(nameof(rootDirectory)); - if (relativePath == null) - throw new ArgumentNullException(nameof(relativePath)); - - HANDLE_CREATION_OPTIONS creationOptions = FileModeToCreationOptions(mode); - HANDLE_ACCESS_OPTIONS accessOptions = FileAccessToHandleAccessOptions(access); - HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); - HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); - - return global::ABI.Windows.Storage.IStorageFolderHandleAccessMethods.Create( - rootDirectory, - relativePath, - creationOptions, - accessOptions, - sharingOptions, - handleOptions, - IntPtr.Zero); - } - - private static HANDLE_ACCESS_OPTIONS FileAccessToHandleAccessOptions(FileAccess access) => - access switch - { - FileAccess.ReadWrite => HANDLE_ACCESS_OPTIONS.HAO_READ | HANDLE_ACCESS_OPTIONS.HAO_WRITE, - FileAccess.Read => HANDLE_ACCESS_OPTIONS.HAO_READ, - FileAccess.Write => HANDLE_ACCESS_OPTIONS.HAO_WRITE, - _ => throw new ArgumentOutOfRangeException(nameof(access), access, null), - }; - - private static HANDLE_SHARING_OPTIONS FileShareToHandleSharingOptions(FileShare share) - { - if ((share & FileShare.Inheritable) != 0) - throw new NotSupportedException(global::Windows.Storage.SR.NotSupported_Inheritable); - if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) - throw new ArgumentOutOfRangeException(nameof(share), share, null); - - HANDLE_SHARING_OPTIONS sharingOptions = HANDLE_SHARING_OPTIONS.HSO_SHARE_NONE; - if ((share & FileShare.Read) != 0) - sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_READ; - if ((share & FileShare.Write) != 0) - sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_WRITE; - if ((share & FileShare.Delete) != 0) - sharingOptions |= HANDLE_SHARING_OPTIONS.HSO_SHARE_DELETE; - - return sharingOptions; - } - - private static HANDLE_OPTIONS FileOptionsToHandleOptions(FileOptions options) - { - if ((options & FileOptions.Encrypted) != 0) - throw new NotSupportedException(global::Windows.Storage.SR.NotSupported_Encrypted); - if (options != FileOptions.None && (options & - ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | - FileOptions.SequentialScan | (FileOptions)0x20000000 /* NoBuffering */)) != 0) - throw new ArgumentOutOfRangeException(nameof(options), options, null); - - return (HANDLE_OPTIONS)options; - } - - private static HANDLE_CREATION_OPTIONS FileModeToCreationOptions(FileMode mode) - { - if (mode < FileMode.CreateNew || mode > FileMode.Append) - throw new ArgumentOutOfRangeException(nameof(mode), mode, null); - - if (mode == FileMode.Append) - return HANDLE_CREATION_OPTIONS.HCO_CREATE_ALWAYS; - - return (HANDLE_CREATION_OPTIONS)mode; - } - } -} + } +} From 0bb93897dbcc34cfbb9d24e60d0adc6d72c814b7 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 26 Oct 2025 14:45:02 -0700 Subject: [PATCH 28/43] Add file --- src/cswinrt/cswinrt.vcxproj.filters | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 0f47585ce..0bda6bb94 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -106,7 +106,10 @@ strings\additions\Windows.Storage.Streams - + + strings\additions\Windows.Storage.Streams + + strings\additions\Windows.Storage From f9a95c70348c3bbab19f760e19b2b42972533f1f Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 26 Oct 2025 19:15:57 -0700 Subject: [PATCH 29/43] Fix COM interop helpers --- src/cswinrt/main.cpp | 2 +- src/cswinrt/strings/ComInteropHelpers.cs | 401 ++++++++++------------- 2 files changed, 170 insertions(+), 233 deletions(-) diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index 3416035c6..ceb34c7d9 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -538,7 +538,7 @@ ComWrappersSupport.RegisterAuthoringMetadataTypeLookup(new Func(GetM { for (auto&& string : strings::base) { - if (std::string(string.name) == "ComInteropHelpers" /* && !settings.filter.includes("Windows") */) + if (std::string(string.name) == "ComInteropHelpers"&& !settings.filter.includes("Windows")) { continue; } diff --git a/src/cswinrt/strings/ComInteropHelpers.cs b/src/cswinrt/strings/ComInteropHelpers.cs index 162c335b7..d841a7989 100644 --- a/src/cswinrt/strings/ComInteropHelpers.cs +++ b/src/cswinrt/strings/ComInteropHelpers.cs @@ -44,7 +44,10 @@ #error Unsupported Universal API Contract version #endif +#pragma warning disable CSWINRT3001 + using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Windows.Foundation; using Windows.Security.Credentials; @@ -55,65 +58,66 @@ namespace WinRT.Interop { internal static class IWindowNativeMethods { - internal static readonly Guid IWindowNativeIID = new(0xEECDBF0E, 0xBAE9, 0x4CB6, 0xA6, 0x8E, 0x95, 0x98, 0xE1, 0xCB, 0x57, 0xBB); + private static ref readonly Guid IID_IWindowNative + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x0E, 0xBF, 0xCD, 0xEE, 0xE9, 0xBA, 0xB6, 0x4C, 0xA6, 0x8E, 0x95, 0x98, 0xE1, 0xCB, 0x57, 0xBB + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } public static unsafe global::System.IntPtr get_WindowHandle(object _obj) { - var asObjRef = global::WinRT.MarshalInspectable.CreateMarshaler2(_obj, IWindowNativeIID); - var ThisPtr = asObjRef.GetAbi(); - global::System.IntPtr __retval = default; + using global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReferenceValue objRefValue = WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(_obj, IID_IWindowNative); - try - { - // We use 3 here because IWindowNative only implements IUnknown, whose only functions are AddRef, Release and QI - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval)); - return __retval; - } - finally - { - asObjRef.Dispose(); - } + void* thisPtr = objRefValue.GetThisPtrUnsafe(); + + global::System.IntPtr __retval = default; + // We use 3 here because IWindowNative only implements IUnknown, whose only functions are AddRef, Release and QI + global::WindowsRuntime.InteropServices.RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3](thisPtr, &__retval)); + return __retval; } } -#if EMBED - internal -#else - public -#endif - static class WindowNative + public static class WindowNative { public static IntPtr GetWindowHandle(object target) => IWindowNativeMethods.get_WindowHandle(target); } internal static class IInitializeWithWindowMethods { - internal static readonly Guid IInitializeWithWindowIID = new(0x3E68D4BD, 0x7135, 0x4D10, 0x80, 0x18, 0x9F, 0xB6, 0xD9, 0xF3, 0x3F, 0xA1); + private static ref readonly Guid IID_IInitializeWithWindow + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xBD, 0xD4, 0x68, 0x3E, 0x35, 0x71, 0x10, 0x4D, 0x80, 0x18, 0x9F, 0xB6, 0xD9, 0xF3, 0x3F, 0xA1 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } public static unsafe void Initialize(object _obj, IntPtr window) { - var asObjRef = global::WinRT.MarshalInspectable.CreateMarshaler2(_obj, IInitializeWithWindowIID); - var ThisPtr = asObjRef.GetAbi(); - global::System.IntPtr __retval = default; + using global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReferenceValue objRefValue = WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeInterfaceMarshaller.ConvertToUnmanaged(_obj, IID_IInitializeWithWindow); - try - { - // IInitializeWithWindow inherits IUnknown (3 functions) and provides Initialize giving a total of 4 functions - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, window)); - } - finally - { - asObjRef.Dispose(); - } + void* thisPtr = objRefValue.GetThisPtrUnsafe(); + + // IInitializeWithWindow inherits IUnknown (3 functions) and provides Initialize giving a total of 4 functions + global::WindowsRuntime.InteropServices.RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3](thisPtr, window)); } } -#if EMBED - internal -#else - public -#endif - static class InitializeWithWindow + public static class InitializeWithWindow { public static void Initialize(object target, IntPtr hwnd) { @@ -126,22 +130,14 @@ public static void Initialize(object target, IntPtr hwnd) namespace Windows.ApplicationModel.DataTransfer.DragDrop.Core { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class DragDropManagerInterop + public static class DragDropManagerInterop { - private static IDragDropManagerInterop dragDropManagerInterop = CoreDragDropManager.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.ApplicationModel.DataTransfer.DragDrop.Core.CoreDragDropManager.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IDragDropManagerInterop); public static CoreDragDropManager GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(ICoreDragDropManager)); - return (CoreDragDropManager)dragDropManagerInterop.GetForWindow(appWindow, iid); + return (CoreDragDropManager) global::ABI.WinRT.Interop.IDragDropManagerInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_ApplicationModel_DataTransfer_DragDrop_Core_ICoreDragDropManager); } } #endif @@ -150,28 +146,33 @@ public static CoreDragDropManager GetForWindow(IntPtr appWindow) namespace Windows.Graphics.Printing { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class PrintManagerInterop + public static class PrintManagerInterop { - private static IPrintManagerInterop printManagerInterop = PrintManager.As(); + private static ref readonly Guid IID_IAsyncOperation_bool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xB3, 0xEF, 0xB5, 0xCD, 0x88, 0x57, 0x9D, 0x50, 0x9B, 0xE1, 0x71, 0xCC, 0xB8, 0xA3, 0x36, 0x2A + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Graphics.Printing.PrintManager.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IPrintManagerInterop); public static PrintManager GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IPrintManager)); - return (PrintManager)printManagerInterop.GetForWindow(appWindow, iid); + return (PrintManager) global::ABI.WinRT.Interop.IPrintManagerInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_Graphics_Printing_IPrintManager); } public static IAsyncOperation ShowPrintUIForWindowAsync(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IAsyncOperation)); - return (IAsyncOperation)printManagerInterop.ShowPrintUIForWindowAsync(appWindow, iid); + return (IAsyncOperation) global::ABI.WinRT.Interop.IPrintManagerInteropMethods.ShowPrintUIForWindowAsync(objectReference, appWindow, IID_IAsyncOperation_bool); } } #endif @@ -180,22 +181,14 @@ public static IAsyncOperation ShowPrintUIForWindowAsync(IntPtr appWindow) namespace Windows.Media { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class SystemMediaTransportControlsInterop + public static class SystemMediaTransportControlsInterop { - private static ISystemMediaTransportControlsInterop systemMediaTransportControlsInterop = SystemMediaTransportControls.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Media.SystemMediaTransportControls.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_ISystemMediaTransportControlsInterop); public static SystemMediaTransportControls GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(ISystemMediaTransportControls)); - return (SystemMediaTransportControls)systemMediaTransportControlsInterop.GetForWindow(appWindow, iid); + return (SystemMediaTransportControls) global::ABI.WinRT.Interop.ISystemMediaTransportControlsInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_Media_ISystemMediaTransportControls); } } #endif @@ -204,27 +197,19 @@ public static SystemMediaTransportControls GetForWindow(IntPtr appWindow) namespace Windows.Media.PlayTo { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class PlayToManagerInterop + public static class PlayToManagerInterop { - private static IPlayToManagerInterop playToManagerInterop = PlayToManager.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Media.PlayTo.PlayToManager.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IPlayToManagerInterop); public static PlayToManager GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IPlayToManager)); - return (PlayToManager)playToManagerInterop.GetForWindow(appWindow, iid); + return (PlayToManager) global::ABI.WinRT.Interop.IPlayToManagerInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_Media_PlayTo_IPlayToManager); } public static void ShowPlayToUIForWindow(IntPtr appWindow) { - playToManagerInterop.ShowPlayToUIForWindow(appWindow); + global::ABI.WinRT.Interop.IPlayToManagerInteropMethods.ShowPlayToUIForWindow(objectReference, appWindow); } } #endif @@ -233,22 +218,27 @@ public static void ShowPlayToUIForWindow(IntPtr appWindow) namespace Windows.Security.Credentials.UI { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class UserConsentVerifierInterop + public static class UserConsentVerifierInterop { - private static IUserConsentVerifierInterop userConsentVerifierInterop = UserConsentVerifier.As(); + private static ref readonly Guid IID_IAsyncOperation_UserConsentVerificationResult + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xFD, 0x6F, 0x59, 0xFD, 0x18, 0x23, 0x8F, 0x55, 0x9D, 0xBE, 0xD2, 0x1D, 0xF4, 0x37, 0x64, 0xA5 + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Security.Credentials.UI.UserConsentVerifier.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IUserConsentVerifierInterop); public static IAsyncOperation RequestVerificationForWindowAsync(IntPtr appWindow, string message) { - var iid = GuidGenerator.CreateIID(typeof(IAsyncOperation)); - return (IAsyncOperation)userConsentVerifierInterop.RequestVerificationForWindowAsync(appWindow, message, iid); + return (IAsyncOperation) global::ABI.WinRT.Interop.IUserConsentVerifierInteropMethods.RequestVerificationForWindowAsync(objectReference, appWindow, message, IID_IAsyncOperation_UserConsentVerificationResult); } } #endif @@ -257,28 +247,32 @@ public static IAsyncOperation RequestVerification namespace Windows.Security.Authentication.Web.Core { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class WebAuthenticationCoreManagerInterop + public static class WebAuthenticationCoreManagerInterop { - private static IWebAuthenticationCoreManagerInterop webAuthenticationCoreManagerInterop = WebAuthenticationCoreManager.As(); + private static ref readonly Guid IID_IAsyncOperation_WebTokenRequestResult + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x52, 0x58, 0x81, 0x0A, 0x44, 0x7C, 0x74, 0x56, 0xB3, 0xD2, 0xFA, 0x2E, 0x4C, 0x1E, 0x46, 0xC9 + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Security.Authentication.Web.Core.WebAuthenticationCoreManager.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IWebAuthenticationCoreManagerInterop); public static IAsyncOperation RequestTokenForWindowAsync(IntPtr appWindow, WebTokenRequest request) { - var iid = GuidGenerator.CreateIID(typeof(IAsyncOperation)); - return (IAsyncOperation)webAuthenticationCoreManagerInterop.RequestTokenForWindowAsync(appWindow, request, iid); + return (IAsyncOperation) global::ABI.WinRT.Interop.IWebAuthenticationCoreManagerInteropMethods.RequestTokenForWindowAsync(objectReference, appWindow, request, IID_IAsyncOperation_WebTokenRequestResult); } public static IAsyncOperation RequestTokenWithWebAccountForWindowAsync(IntPtr appWindow, WebTokenRequest request, WebAccount webAccount) { - var iid = GuidGenerator.CreateIID(typeof(IAsyncOperation)); - return (IAsyncOperation)webAuthenticationCoreManagerInterop.RequestTokenWithWebAccountForWindowAsync(appWindow, request, webAccount, iid); + return (IAsyncOperation) global::ABI.WinRT.Interop.IWebAuthenticationCoreManagerInteropMethods.RequestTokenWithWebAccountForWindowAsync(objectReference, appWindow, request, webAccount, IID_IAsyncOperation_WebTokenRequestResult); } } #endif @@ -287,34 +281,24 @@ public static IAsyncOperation RequestTokenWithWebAccountF namespace Windows.UI.ApplicationSettings { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class AccountsSettingsPaneInterop + public static class AccountsSettingsPaneInterop { - private static IAccountsSettingsPaneInterop accountsSettingsPaneInterop = AccountsSettingsPane.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.ApplicationSettings.AccountsSettingsPane.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IAccountsSettingsPaneInterop); public static AccountsSettingsPane GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IAccountsSettingsPane)); - return (AccountsSettingsPane)accountsSettingsPaneInterop.GetForWindow(appWindow, iid); + return (AccountsSettingsPane) global::ABI.WinRT.Interop.IAccountsSettingsPaneInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_UI_ApplicationSettings_IAccountsSettingsPane); } public static IAsyncAction ShowManageAccountsForWindowAsync(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IAsyncAction)); - return (IAsyncAction)accountsSettingsPaneInterop.ShowManageAccountsForWindowAsync(appWindow, iid); + return (IAsyncAction) global::ABI.WinRT.Interop.IAccountsSettingsPaneInteropMethods.ShowManageAccountsForWindowAsync(objectReference, appWindow, global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_Windows_Foundation_IAsyncAction); } public static IAsyncAction ShowAddAccountForWindowAsync(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IAsyncAction)); - return (IAsyncAction)accountsSettingsPaneInterop.ShowAddAccountForWindowAsync(appWindow, iid); + return (IAsyncAction) global::ABI.WinRT.Interop.IAccountsSettingsPaneInteropMethods.ShowAddAccountForWindowAsync(objectReference, appWindow, global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_Windows_Foundation_IAsyncAction); } } #endif @@ -323,42 +307,25 @@ public static IAsyncAction ShowAddAccountForWindowAsync(IntPtr appWindow) namespace Windows.UI.Input { #if UAC_VERSION_3 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.14393.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class RadialControllerConfigurationInterop + public static class RadialControllerConfigurationInterop { - private static IRadialControllerConfigurationInterop radialControllerConfigurationInterop - = RadialControllerConfiguration.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.Input.RadialControllerConfiguration.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IRadialControllerConfigurationInterop); public static RadialControllerConfiguration GetForWindow(IntPtr hwnd) { - Guid iid = GuidGenerator.CreateIID(typeof(IRadialControllerConfiguration)); - return (RadialControllerConfiguration)radialControllerConfigurationInterop.GetForWindow(hwnd, iid); + return (RadialControllerConfiguration) global::ABI.WinRT.Interop.IRadialControllerConfigurationInteropMethods.GetForWindow(objectReference, hwnd, global::ABI.InterfaceIIDs.IID_Windows_UI_Input_IRadialControllerConfiguration); } } -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.14393.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class RadialControllerInterop + public static class RadialControllerInterop { - private static IRadialControllerInterop radialControllerInterop = RadialController.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.Input.RadialController.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IRadialControllerInterop); public static RadialController CreateForWindow(IntPtr hwnd) { - Guid iid = GuidGenerator.CreateIID(typeof(IRadialController)); - return (RadialController)radialControllerInterop.CreateForWindow(hwnd, iid); + return (RadialController) global::ABI.WinRT.Interop.IRadialControllerInteropMethods.CreateForWindow(objectReference, hwnd, global::ABI.InterfaceIIDs.IID_Windows_UI_Input_IRadialController); } } #endif @@ -367,23 +334,14 @@ public static RadialController CreateForWindow(IntPtr hwnd) namespace Windows.UI.Input.Core { #if UAC_VERSION_4 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.15063.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class RadialControllerIndependentInputSourceInterop + public static class RadialControllerIndependentInputSourceInterop { - private static IRadialControllerIndependentInputSourceInterop radialControllerIndependentInputSourceInterop - = RadialControllerIndependentInputSource.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.Input.Core.RadialControllerIndependentInputSource.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IRadialControllerIndependentInputSourceInterop); public static RadialControllerIndependentInputSource CreateForWindow(IntPtr hwnd) { - Guid iid = GuidGenerator.CreateIID(typeof(IRadialControllerIndependentInputSource)); - return (RadialControllerIndependentInputSource)radialControllerIndependentInputSourceInterop.CreateForWindow(hwnd, iid); + return (RadialControllerIndependentInputSource) global::ABI.WinRT.Interop.IRadialControllerIndependentInputSourceInteropMethods.CreateForWindow(objectReference, hwnd, global::ABI.InterfaceIIDs.IID_Windows_UI_Input_Core_IRadialControllerIndependentInputSource); } } #endif @@ -392,22 +350,14 @@ public static RadialControllerIndependentInputSource CreateForWindow(IntPtr hwnd namespace Windows.UI.Input.Spatial { #if UAC_VERSION_2 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10586.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class SpatialInteractionManagerInterop + public static class SpatialInteractionManagerInterop { - private static ISpatialInteractionManagerInterop spatialInteractionManagerInterop = SpatialInteractionManager.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.Input.Spatial.SpatialInteractionManager.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_ISpatialInteractionManagerInterop); public static SpatialInteractionManager GetForWindow(IntPtr window) { - Guid iid = GuidGenerator.CreateIID(typeof(ISpatialInteractionManager)); - return (SpatialInteractionManager)spatialInteractionManagerInterop.GetForWindow(window, iid); + return (SpatialInteractionManager) global::ABI.WinRT.Interop.ISpatialInteractionManagerInteropMethods.GetForWindow(objectReference, window, global::ABI.InterfaceIIDs.IID_Windows_UI_Input_Spatial_ISpatialInteractionManager); } } #endif @@ -416,41 +366,25 @@ public static SpatialInteractionManager GetForWindow(IntPtr window) namespace Windows.UI.ViewManagement { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class InputPaneInterop + public static class InputPaneInterop { - private static IInputPaneInterop inputPaneInterop = InputPane.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.ViewManagement.InputPane.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IInputPaneInterop); public static InputPane GetForWindow(IntPtr appWindow) { - Guid iid = GuidGenerator.CreateIID(typeof(IInputPane)); - return (InputPane)inputPaneInterop.GetForWindow(appWindow, iid); + return (InputPane) global::ABI.WinRT.Interop.IInputPaneInteropMethods.GetForWindow(objectReference, appWindow, global::ABI.InterfaceIIDs.IID_Windows_UI_ViewManagement_IInputPane); } } -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class UIViewSettingsInterop + public static class UIViewSettingsInterop { - private static IUIViewSettingsInterop uIViewSettingsInterop = UIViewSettings.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.UI.ViewManagement.UIViewSettings.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IUIViewSettingsInterop); public static UIViewSettings GetForWindow(IntPtr hwnd) { - var iid = GuidGenerator.CreateIID(typeof(IUIViewSettings)); - return (UIViewSettings)uIViewSettingsInterop.GetForWindow(hwnd, iid); + return (UIViewSettings) global::ABI.WinRT.Interop.IUIViewSettingsInteropMethods.GetForWindow(objectReference, hwnd, global::ABI.InterfaceIIDs.IID_Windows_UI_ViewManagement_IUIViewSettings); } } #endif @@ -459,28 +393,19 @@ public static UIViewSettings GetForWindow(IntPtr hwnd) namespace Windows.Graphics.Display { #if UAC_VERSION_15 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.22621.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class DisplayInformationInterop + public static class DisplayInformationInterop { - private static IDisplayInformationStaticsInterop displayInformationInterop = DisplayInformation.As(); + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.Graphics.Display.DisplayInformation.RuntimeClassName, global::ABI.InterfaceIIDs.IID_WinRT_Interop_IDisplayInformationStaticsInterop); public static DisplayInformation GetForWindow(IntPtr window) { - Guid iid = GuidGenerator.CreateIID(typeof(IDisplayInformation)); - return (DisplayInformation)displayInformationInterop.GetForWindow(window, iid); + return (DisplayInformation) global::ABI.WinRT.Interop.IDisplayInformationStaticsInteropMethods.GetForWindow(objectReference, window, global::ABI.InterfaceIIDs.IID_Windows_Graphics_Display_IDisplayInformation); } public static DisplayInformation GetForMonitor(IntPtr monitor) { - Guid iid = GuidGenerator.CreateIID(typeof(IDisplayInformation)); - return (DisplayInformation)displayInformationInterop.GetForMonitor(monitor, iid); + return (DisplayInformation) global::ABI.WinRT.Interop.IDisplayInformationStaticsInteropMethods.GetForMonitor(objectReference, monitor, global::ABI.InterfaceIIDs.IID_Windows_Graphics_Display_IDisplayInformation); } } #endif @@ -489,27 +414,27 @@ public static DisplayInformation GetForMonitor(IntPtr monitor) namespace Windows.ApplicationModel.DataTransfer { #if UAC_VERSION_1 -#if NET [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] -#endif -#if EMBED - internal -#else - public -#endif - static class DataTransferManagerInterop + public static class DataTransferManagerInterop { - private static readonly global::System.Guid riid = new global::System.Guid(0xA5CAEE9B, 0x8708, 0x49D1, 0x8D, 0x36, 0x67, 0xD2, 0x5A, 0x8D, 0xA0, 0x0C); + private static ref readonly Guid Riid + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x9B, 0xEE, 0xCA, 0xA5, 0x08, 0x87, 0xD1, 0x49, 0x8D, 0x36, 0x67, 0xD2, 0x5A, 0x8D, 0xA0, 0x0C + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } -#if NET - private static global::WinRT.IObjectReference objectReference = global::WinRT.ActivationFactory.Get("Windows.ApplicationModel.DataTransfer.DataTransferManager", new global::System.Guid(0x3A3DCD6C, 0x3EAB, 0x43DC, 0xBC, 0xDE, 0x45, 0x67, 0x1C, 0xE8, 0x00, 0xC8)); -#else - private static global::WinRT.ObjectReference objectReference = global::WinRT.ActivationFactory.Get("Windows.ApplicationModel.DataTransfer.DataTransferManager", new global::System.Guid(0x3A3DCD6C, 0x3EAB, 0x43DC, 0xBC, 0xDE, 0x45, 0x67, 0x1C, 0xE8, 0x00, 0xC8)); -#endif + private static readonly global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference objectReference = global::WindowsRuntime.InteropServices.WindowsRuntimeActivationFactory.GetActivationFactory(ABI.Windows.ApplicationModel.DataTransfer.DataTransferManager.RuntimeClassName, IDataTransferManagerInteropMethods.IID); public static global::Windows.ApplicationModel.DataTransfer.DataTransferManager GetForWindow(global::System.IntPtr appWindow) { - return IDataTransferManagerInteropMethods.GetForWindow(objectReference, appWindow, riid); + return IDataTransferManagerInteropMethods.GetForWindow(objectReference, appWindow, Riid); } public static void ShowShareUIForWindow(global::System.IntPtr appWindow) @@ -521,33 +446,45 @@ public static void ShowShareUIForWindow(global::System.IntPtr appWindow) internal static class IDataTransferManagerInteropMethods { - internal static unsafe global::Windows.ApplicationModel.DataTransfer.DataTransferManager GetForWindow(global::WinRT.IObjectReference _obj, global::System.IntPtr appWindow, in global::System.Guid riid) + internal static ref readonly Guid IID { - global::System.IntPtr thisPtr = _obj.ThisPtr; - global::System.IntPtr ptr = default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0x6C, 0xCD, 0x3D, 0x3A, 0xAB, 0x3E, 0xDC, 0x43, 0xBC, 0xDE, 0x45, 0x67, 0x1C, 0xE8, 0x00, 0xC8 + ]; + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + internal static unsafe global::Windows.ApplicationModel.DataTransfer.DataTransferManager GetForWindow(global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference _obj, global::System.IntPtr appWindow, in global::System.Guid riid) + { + using global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReferenceValue activationFactoryValue = _obj.AsValue(); + void* thisPtr = activationFactoryValue.GetThisPtrUnsafe(); + void* ptr = null; try { // IDataTransferManagerInterop inherits IUnknown (3 functions) and provides GetForWindow giving a total of 5 functions fixed(Guid* _riid = &riid) - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3](thisPtr, appWindow, _riid, &ptr)); - GC.KeepAlive(_obj); - return global::WinRT.MarshalInspectable.FromAbi(ptr); + global::WindowsRuntime.InteropServices.RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3](thisPtr, appWindow, _riid, &ptr)); + return global::ABI.Windows.ApplicationModel.DataTransfer.DataTransferManagerMarshaller.ConvertToManaged(ptr); } finally { - global::WinRT.MarshalInspectable.DisposeAbi(ptr); + global::WindowsRuntime.InteropServices.Marshalling.WindowsRuntimeObjectMarshaller.Free(ptr); } } - internal static unsafe void ShowShareUIForWindow(global::WinRT.IObjectReference _obj, global::System.IntPtr appWindow) + internal static unsafe void ShowShareUIForWindow(global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReference _obj, global::System.IntPtr appWindow) { - global::System.IntPtr thisPtr = _obj.ThisPtr; + using global::WindowsRuntime.InteropServices.WindowsRuntimeObjectReferenceValue activationFactoryValue = _obj.AsValue(); + void* thisPtr = activationFactoryValue.GetThisPtrUnsafe(); // IDataTransferManagerInterop inherits IUnknown (3 functions) and provides ShowShareUIForWindow giving a total of 5 functions - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[4](thisPtr, appWindow)); - GC.KeepAlive(_obj); + global::WindowsRuntime.InteropServices.RestrictedErrorInfo.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[4](thisPtr, appWindow)); } } } From d3420fce0058a5a239b29a960f8dfac588c0cc7e Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 26 Oct 2025 22:03:34 -0700 Subject: [PATCH 30/43] Fix boxing --- .../Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index 250c62e2b..e3e11c394 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -427,6 +427,7 @@ public static void InterfaceImpl( { { Ldarg_0 }, { Callvirt, interopReferences.IEnumerator1get_Current(elementType).Import(module) }, + { Box, elementType.Import(module).ToTypeDefOrRef() }, { Ret } } }; From 762e9481f2d52dc8f6634e35be569e37c5eb654f Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 27 Oct 2025 18:17:48 -0700 Subject: [PATCH 31/43] Fix boxing logic to scope to value types --- .../InteropTypeDefinitionBuilder.IEnumerator1.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index e3e11c394..61705d6ca 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -427,11 +427,18 @@ public static void InterfaceImpl( { { Ldarg_0 }, { Callvirt, interopReferences.IEnumerator1get_Current(elementType).Import(module) }, - { Box, elementType.Import(module).ToTypeDefOrRef() }, - { Ret } } }; + // If the element type is a value type, we need to box it + if (elementType.IsValueType) + { + _ = get_IEnumeratorCurrentMethod.CilMethodBody.Instructions.Add(Box, elementType.Import(module).ToTypeDefOrRef()); + } + + // Add the return + _ = get_IEnumeratorCurrentMethod.CilMethodBody.Instructions.Add(Ret); + // Create the 'IEnumerator.Current' property PropertyDefinition enumeratorCurrentProperty = new( name: "System.Collections.IEnumerator.Current"u8, From cd06d353e46ead001710c4a7db4acc047e8d4ec5 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 28 Oct 2025 19:29:08 -0700 Subject: [PATCH 32/43] Try to fix build --- .../Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs | 2 +- .../Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index 61705d6ca..c61b910ba 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -98,7 +98,7 @@ public static void IIteratorMethods( // [2]: 'void*' (the native value that was retrieved) CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); - CilLocalVariable loc_2_currentNative = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_currentNative = enumeratorType.TypeArguments[0].IsValueType ? new(enumeratorType.TypeArguments[0].Import(module)) : new(module.CorLibTypeFactory.Void.MakePointerType()); // Jump labels CilInstruction ldloca_s_0_tryStart = new(Ldloca_S, loc_0_thisValue); diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs index fcd72b4eb..6a3e8c9e5 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.KeyValuePair.cs @@ -89,6 +89,7 @@ static MethodDefinition GetKeyOrValuePropertyAccessorMethod( { int typeArgumentIndex = name == "get_Key" ? 0 : 1; TypeSignature typeArgument = keyValuePairType.TypeArguments[typeArgumentIndex]; + TypeSignature typeArgumentAbiType = typeArgument.IsValueType ? typeArgument.Import(module).MakePointerType() : module.CorLibTypeFactory.Void.MakePointerType(); // Define the method as follows: // @@ -101,7 +102,7 @@ static MethodDefinition GetKeyOrValuePropertyAccessorMethod( returnType: module.CorLibTypeFactory.Int32, parameterTypes: [ module.CorLibTypeFactory.Void.MakePointerType(), - typeArgument.Import(module).MakePointerType()])) + typeArgumentAbiType])) { CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } }; @@ -172,7 +173,8 @@ static MethodDefinition GetKeyOrValuePropertyAccessorMethod( _ = instructions.Add(Ldarg_1); _ = instructions.Add(Ldloca_S, loc_1_keyValuePair); _ = instructions.Add(Call, get_MethodRef); - _ = instructions.Add(Stind_I); + // TODO call marshaler + // _ = instructions.Add(Stind_I); _ = instructions.Add(Ldc_I4_0); _ = instructions.Add(Stloc_0); _ = instructions.Add(Leave_S, ldloc_0_returnHResult.CreateLabel()); From 7afeec8ad355c1a0932fa934bceebb482f2aebcd Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Thu, 30 Oct 2025 08:52:53 -0700 Subject: [PATCH 33/43] PR feedback --- .../DispatcherQueueProxyHandler.cs | 56 ++----------------- .../ProjectionDllExports}/TokenizerHelper.cs | 7 +-- .../WellKnownInterfaceIIDs.g.cs | 49 ++++++++++------ .../InteropServices/WellKnownInterfaceIIDs.tt | 4 +- .../Windows.Foundation/Point.cs | 1 + src/WinRT.Runtime2/Windows.Foundation/Rect.cs | 1 + src/cswinrt/cswinrt.vcxproj | 1 - ....UI.Xaml.Media.Animation.RepeatBehavior.cs | 12 ++-- ...icrosoft.UI.Xaml.Media.Media3D.Matrix3D.cs | 44 +++++++++++---- .../Microsoft.UI.Xaml.Media.Matrix.cs | 24 ++++---- .../Microsoft.UI.Xaml.CornerRadius.cs | 2 +- .../Microsoft.UI.Xaml.Thickness.cs | 2 +- .../WellKnownStreamInterfaceIIDs.cs | 45 --------------- .../WindowsRuntimeBuffer.cs | 4 +- .../WindowsRuntimeBufferMarshal.cs | 4 +- ....UI.Xaml.Media.Animation.RepeatBehavior.cs | 12 ++-- .../Windows.UI.Xaml.Media.Media3D.Matrix3D.cs | 44 +++++++++++---- .../Windows.UI.Xaml.Media.Matrix.cs | 24 ++++---- .../Windows.UI.Xaml.CornerRadius.cs | 2 +- .../Windows.UI.Xaml.Thickness.cs | 2 +- .../additions/Windows.UI/Windows.UI.Color.cs | 33 +++++++---- 21 files changed, 177 insertions(+), 196 deletions(-) rename src/WinRT.Runtime2/{ABI/Windows.Foundation => InteropServices/ProjectionDllExports}/TokenizerHelper.cs (86%) delete mode 100644 src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs diff --git a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs index 2c0739b78..b8b5cc69d 100644 --- a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs +++ b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using WindowsRuntime.InteropServices; +#pragma warning disable CSWINRT3001 + #nullable enable namespace ABI.Windows.System; @@ -143,64 +145,14 @@ private static ref readonly Guid IID_IDispatcherQueueHandler } } - /// The IID for IUnknown (00000000-0000-0000-C000-000000000046). - private static ref readonly Guid IID_IUnknown - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - 0x00, 0x00, - 0xC0, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x46 - ]; - - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } - } - - /// The IID for IAgileObject (94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90). - private static ref readonly Guid IID_IAgileObject - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - 0x94, 0x2B, 0xEA, 0x94, - 0xCC, 0xE9, - 0xE0, 0x49, - 0xC0, - 0xFF, - 0xEE, - 0x64, - 0xCA, - 0x8F, - 0x5B, - 0x90 - ]; - - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } - } - /// /// Implements IUnknown.QueryInterface(REFIID, void**). /// [UnmanagedCallersOnly] public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, void** ppvObject) { - if (riid->Equals(IID_IUnknown) || - riid->Equals(IID_IAgileObject) || + if (riid->Equals(WellKnownInterfaceIIDs.IID_IUnknown) || + riid->Equals(WellKnownInterfaceIIDs.IID_IAgileObject) || riid->Equals(IID_IDispatcherQueueHandler)) { Interlocked.Increment(ref @this->referenceCount); diff --git a/src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs b/src/WinRT.Runtime2/InteropServices/ProjectionDllExports/TokenizerHelper.cs similarity index 86% rename from src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs rename to src/WinRT.Runtime2/InteropServices/ProjectionDllExports/TokenizerHelper.cs index 4b0c013cf..64563e25a 100644 --- a/src/WinRT.Runtime2/ABI/Windows.Foundation/TokenizerHelper.cs +++ b/src/WinRT.Runtime2/InteropServices/ProjectionDllExports/TokenizerHelper.cs @@ -4,9 +4,8 @@ using System; using System.ComponentModel; using System.Globalization; -using WindowsRuntime; -namespace ABI.Windows.Foundation; +namespace WindowsRuntime.InteropServices; /// /// Helper class for tokenizing used by generated projections. @@ -31,8 +30,8 @@ public static char GetNumericListSeparator(IFormatProvider? provider) // If the decimal separator is ',', use ';', otherwise use ',' return - (numberFormat.NumberDecimalSeparator.Length > 0) && - (numberFormat.NumberDecimalSeparator[0] == CommaSeparator) + numberFormat.NumberDecimalSeparator.Length > 0 && + numberFormat.NumberDecimalSeparator[0] == CommaSeparator ? SemicolonSeparator : CommaSeparator; } diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs index 5c8cb74f4..68098d70b 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs @@ -30,6 +30,13 @@ public static ref readonly Guid IID_IAgileObject get => ref WellKnownWindowsInterfaceIIDs.IID_IAgileObject; } + /// The IID for IBufferByteAccess. + public static ref readonly Guid IID_IBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IBufferByteAccess; + } + /// The IID for IMarshal. public static ref readonly Guid IID_IMarshal { @@ -66,125 +73,133 @@ public static ref readonly Guid IID_IWeakReferenceSource } /// The IID for Windows.UI.Xaml.Interop.IBindableIterable (mapped to . + /// The IID for IMemoryBufferByteAccess. + public static ref readonly Guid IID_IMemoryBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IMemoryBufferByteAccess; + } + + /// The IID for Windows.UI.Xaml.Interop.IBindableIterable (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableIterable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterable; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterable (mapped to . + /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterable (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableIterable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterable; } - /// The IID for Windows.UI.Xaml.Interop.IBindableIterator (mapped to . + /// The IID for Windows.UI.Xaml.Interop.IBindableIterator (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableIterator { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterator; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterator (mapped to . + /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterator (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableIterator { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterator; } - /// The IID for Windows.UI.Xaml.Interop.IBindableVector (mapped to . + /// The IID for Windows.UI.Xaml.Interop.IBindableVector (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableVector { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableVector; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableVector (mapped to . + /// The IID for Microsoft.UI.Xaml.Interop.IBindableVector (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableVector { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableVector; } - /// The IID for Windows.UI.Xaml.Data.INotifyPropertyChanged (mapped to . + /// The IID for Windows.UI.Xaml.Data.INotifyPropertyChanged (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Data_INotifyPropertyChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_WUX_INotifyPropertyChanged; } - /// The IID for Microsoft.UI.Xaml.Data.INotifyPropertyChanged (mapped to . + /// The IID for Microsoft.UI.Xaml.Data.INotifyPropertyChanged (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Data_INotifyPropertyChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_MUX_INotifyPropertyChanged; } - /// The IID for Microsoft.UI.Xaml.Data.INotifyDataErrorInfo (mapped to . + /// The IID for Microsoft.UI.Xaml.Data.INotifyDataErrorInfo (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Data_INotifyDataErrorInfo { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_INotifyDataErrorInfo; } - /// The IID for Windows.UI.Xaml.Interop.INotifyCollectionChanged (mapped to . + /// The IID for Windows.UI.Xaml.Interop.INotifyCollectionChanged (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Interop_INotifyCollectionChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_WUX_INotifyCollectionChanged; } - /// The IID for Microsoft.UI.Xaml.Interop.INotifyCollectionChanged (mapped to . + /// The IID for Microsoft.UI.Xaml.Interop.INotifyCollectionChanged (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_INotifyCollectionChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_MUX_INotifyCollectionChanged; } - /// The IID for Windows.Foundation.IClosable (mapped to . + /// The IID for Windows.Foundation.IClosable (mapped to ). public static ref readonly Guid IID_Windows_Foundation_IClosable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IClosable; } - /// The IID for Microsoft.UI.Xaml.IXamlServiceProvider (mapped to . + /// The IID for Microsoft.UI.Xaml.IXamlServiceProvider (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_IXamlServiceProvider { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IXamlServiceProvider; } - /// The IID for Windows.UI.Xaml.Input.ICommand (mapped to . + /// The IID for Windows.UI.Xaml.Input.ICommand (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Input_ICommand { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_ICommand; } - /// The IID for Microsoft.UI.Xaml.Input.ICommand (mapped to . + /// The IID for Microsoft.UI.Xaml.Input.ICommand (mapped to ). public static ref readonly Guid IID_Microsoft_UI_Xaml_Input_ICommand { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_ICommand; } - /// The IID for Windows.Foundation.Collections.IVectorChangedEventArgs (mapped to . + /// The IID for Windows.Foundation.Collections.IVectorChangedEventArgs (mapped to ). public static ref readonly Guid IID_Windows_Foundation_Collections_IVectorChangedEventArgs { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IVectorChangedEventArgs; } - /// The IID for Windows.Foundation.IAsyncAction (mapped to . + /// The IID for Windows.Foundation.IAsyncAction (mapped to ). public static ref readonly Guid IID_Windows_Foundation_IAsyncAction { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IAsyncAction; } - /// The IID for Windows.Foundation.IAsyncInfo (mapped to . + /// The IID for Windows.Foundation.IAsyncInfo (mapped to ). public static ref readonly Guid IID_Windows_Foundation_IAsyncInfo { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt index 0725918b5..5a8540e66 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt @@ -25,7 +25,9 @@ var entries = new (string ProjectedFullyQualifiedName, string WindowsRuntimeFull { (null, "IUnknown", false), (null, "IAgileObject", false), + (null, "IBufferByteAccess", false), (null, "IMarshal", false), + (null, "IMemoryBufferByteAccess", false), (null, "IInspectable", false), (null, "IStringable", false), (null, "IPropertyValue", false), @@ -77,7 +79,7 @@ for (int i = 0; i < entries.Length; i++) ? $"IID_{iidPrefix}_{windowsRuntimeName}" : $"IID_{windowsRuntimeName}"; #> - /// The IID for <#=windowsRuntimeFullyQualifiedName#> (mapped to . + /// The IID for <#=windowsRuntimeFullyQualifiedName#><#=mappedTo#>. public static ref readonly Guid IID_<#=windowsRuntimeFullyQualifiedName.Replace(".", "_")#> { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/WinRT.Runtime2/Windows.Foundation/Point.cs b/src/WinRT.Runtime2/Windows.Foundation/Point.cs index 6823c15d5..9998f413d 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Point.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Point.cs @@ -9,6 +9,7 @@ using System.Runtime.Versioning; using Windows.Foundation.Metadata; using WindowsRuntime; +using WindowsRuntime.InteropServices; namespace Windows.Foundation; diff --git a/src/WinRT.Runtime2/Windows.Foundation/Rect.cs b/src/WinRT.Runtime2/Windows.Foundation/Rect.cs index 552552d2e..e9ad1e8af 100644 --- a/src/WinRT.Runtime2/Windows.Foundation/Rect.cs +++ b/src/WinRT.Runtime2/Windows.Foundation/Rect.cs @@ -9,6 +9,7 @@ using System.Runtime.Versioning; using Windows.Foundation.Metadata; using WindowsRuntime; +using WindowsRuntime.InteropServices; #pragma warning disable IDE0046 diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index d2c7e2b62..6339773fa 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -90,7 +90,6 @@ - diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs index 26cbcec3a..5df258e92 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -105,14 +105,10 @@ internal string InternalToString(string format, IFormatProvider formatProvider) case RepeatBehaviorType.Count: - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); - - sb.AppendFormat( - formatProvider, - "{0:" + format + "}x", - Count); - - return sb.ToString(); + DefaultInterpolatedStringHandler handler = new(1, 1, formatProvider, stackalloc char[64]); + handler.AppendFormatted(Count, format); + handler.AppendLiteral("x"); + return handler.ToStringAndClear(); case RepeatBehaviorType.Duration: diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs index 84c8ec206..5fb8c968e 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -411,16 +411,40 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); - return string.Format(provider, - "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + - "}{0}{6:" + format + "}{0}{7:" + format + "}{0}{8:" + format + "}{0}{9:" + format + "}{0}{10:" + format + - "}{0}{11:" + format + "}{0}{12:" + format + "}{0}{13:" + format + "}{0}{14:" + format + "}{0}{15:" + format + "}{0}{16:" + format + "}", - separator, - _m11, _m12, _m13, _m14, - _m21, _m22, _m23, _m24, - _m31, _m32, _m33, _m34, - _offsetX, _offsetY, _offsetZ, _m44); + char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); + DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[64]); + handler.AppendFormatted(_m11, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m12, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m13, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m14, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m21, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m22, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m23, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m24, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m31, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m32, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m33, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m34, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetX, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetY, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetZ, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m44, format); + return handler.ToStringAndClear(); } public override int GetHashCode() diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs index 6656640b0..da4ef1488 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs @@ -50,16 +50,20 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); - return string.Format(provider, - "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", - separator, - M11, - M12, - M21, - M22, - OffsetX, - OffsetY); + char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); + DefaultInterpolatedStringHandler handler = new(0, 11, provider, stackalloc char[64]); + handler.AppendFormatted(M11, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M12, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M21, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M22, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(OffsetX, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(OffsetY, format); + return handler.ToStringAndClear(); } public Point Transform(Point point) diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs index d9f2cf2e2..aa33c0d96 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs @@ -52,7 +52,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs index e4add31a4..1b4794432 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs @@ -17,7 +17,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs deleted file mode 100644 index 424ba1e9a..000000000 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WellKnownStreamInterfaceIIDs.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace ABI.Windows.Storage.Streams -{ - internal static class WellKnownStreamInterfaceIIDs - { - public static ref readonly Guid IID_IBufferByteAccess - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - 0xEF, 0x0F, 0x5A, 0x90, 0x53, 0xBC, 0xDF, 0x11, 0x8C, 0x49, 0x00, 0x1E, 0x4F, 0xC6, 0x86, 0xDA - ]; - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } - } - - public static ref readonly Guid IID_IMarshal - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 - ]; - - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } - } - - public static ref readonly Guid IID_IMemoryBufferByteAccess - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ReadOnlySpan data = - [ - 0x35, 0x32, 0x0D, 0x5B, 0xBA, 0x4D, 0x44, 0x4D, 0x86, 0x5E, 0x8F, 0x1D, 0x0E, 0x4F, 0xD0, 0x4D - ]; - return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); - } - } - } -} diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs index 7d3934be7..bc76346ae 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs @@ -246,13 +246,13 @@ static WindowsRuntimeBufferInterfaceEntriesImpl() { Entries.IBuffer.IID = ABI.InterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; Entries.IBuffer.Vtable = ABI.Windows.Storage.Streams.IBufferImpl.Vtable; - Entries.IBufferByteAccess.IID = ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IBufferByteAccess; + Entries.IBufferByteAccess.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IBufferByteAccess; Entries.IBufferByteAccess.Vtable = ABI.Windows.Storage.Streams.IBufferByteAccessImpl.Vtable; Entries.IStringable.IID = IStringableImpl.IID; Entries.IStringable.Vtable = IStringableImpl.Vtable; Entries.IWeakReferenceSource.IID = IWeakReferenceSourceImpl.IID; Entries.IWeakReferenceSource.Vtable = IWeakReferenceSourceImpl.Vtable; - Entries.IMarshal.IID = ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IMarshal; + Entries.IMarshal.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMarshal; Entries.IMarshal.Vtable = ABI.Windows.Storage.Streams.IBufferMarshalImpl.Vtable; Entries.IAgileObject.IID = IAgileObjectImpl.IID; Entries.IAgileObject.Vtable = IAgileObjectImpl.Vtable; diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs index 9cb1eace8..a4d4475a3 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs @@ -35,7 +35,7 @@ public static unsafe bool TryGetDataUnsafe( } if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && - unwrapped.TryAsUnsafe(global::ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IBufferByteAccess, out nint thisPtr)) + unwrapped.TryAsUnsafe(global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IBufferByteAccess, out nint thisPtr)) { try { @@ -83,7 +83,7 @@ public static unsafe bool TryGetDataUnsafe( } if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && - unwrapped.TryAsUnsafe(global::ABI.Windows.Storage.Streams.WellKnownStreamInterfaceIIDs.IID_IMemoryBufferByteAccess, out nint thisPtr)) + unwrapped.TryAsUnsafe(global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMemoryBufferByteAccess, out nint thisPtr)) { try { diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs index e69a3e458..a89c4c030 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -105,14 +105,10 @@ internal string InternalToString(string format, IFormatProvider formatProvider) case RepeatBehaviorType.Count: - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); - - sb.AppendFormat( - formatProvider, - "{0:" + format + "}x", - Count); - - return sb.ToString(); + DefaultInterpolatedStringHandler handler = new(1, 1, formatProvider, stackalloc char[64]); + handler.AppendFormatted(Count, format); + handler.AppendLiteral("x"); + return handler.ToStringAndClear(); case RepeatBehaviorType.Duration: diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs index c80c8bf89..83b7f55ad 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -411,16 +411,40 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); - return string.Format(provider, - "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + - "}{0}{6:" + format + "}{0}{7:" + format + "}{0}{8:" + format + "}{0}{9:" + format + "}{0}{10:" + format + - "}{0}{11:" + format + "}{0}{12:" + format + "}{0}{13:" + format + "}{0}{14:" + format + "}{0}{15:" + format + "}{0}{16:" + format + "}", - separator, - _m11, _m12, _m13, _m14, - _m21, _m22, _m23, _m24, - _m31, _m32, _m33, _m34, - _offsetX, _offsetY, _offsetZ, _m44); + char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); + DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[64]); + handler.AppendFormatted(_m11, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m12, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m13, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m14, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m21, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m22, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m23, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m24, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m31, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m32, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m33, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m34, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetX, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetY, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_offsetZ, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(_m44, format); + return handler.ToStringAndClear(); } public override int GetHashCode() diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs index f97d049d1..60b950839 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs @@ -50,16 +50,20 @@ private string ConvertToString(string format, IFormatProvider provider) } // Helper to get the numeric list separator for a given culture. - char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); - return string.Format(provider, - "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", - separator, - M11, - M12, - M21, - M22, - OffsetX, - OffsetY); + char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); + DefaultInterpolatedStringHandler handler = new(0, 11, provider, stackalloc char[64]); + handler.AppendFormatted(M11, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M12, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M21, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(M22, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(OffsetX, format); + handler.AppendFormatted(separator); + handler.AppendFormatted(OffsetY, format); + return handler.ToStringAndClear(); } public Point Transform(Point point) diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs index 35c93ac8f..238d01502 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs @@ -52,7 +52,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs index 225d369f3..d043b0f54 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs @@ -17,7 +17,7 @@ public override string ToString() internal string ToString(global::System.Globalization.CultureInfo cultureInfo) { - char listSeparator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(cultureInfo); + char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) diff --git a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs index fb7cde439..00b4c7377 100644 --- a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs +++ b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs @@ -38,26 +38,35 @@ string IFormattable.ToString(string format, IFormatProvider provider) internal string ConvertToString(string format, IFormatProvider provider) { - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); - if (format == null) { - sb.AppendFormat(provider, "#{0:X2}", A); - sb.AppendFormat(provider, "{0:X2}", R); - sb.AppendFormat(provider, "{0:X2}", G); - sb.AppendFormat(provider, "{0:X2}", B); + DefaultInterpolatedStringHandler handler = new(1, 4, provider, stackalloc char[32]); + handler.AppendLiteral("#"); + handler.AppendFormatted(A, "X2"); + handler.AppendFormatted(R, "X2"); + handler.AppendFormatted(G, "X2"); + handler.AppendFormatted(B, "X2"); + return handler.ToStringAndClear(); } else { // Helper to get the numeric list separator for a given culture. - char separator = global::ABI.Windows.Foundation.TokenizerHelper.GetNumericListSeparator(provider); + char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); - sb.AppendFormat(provider, - "sc#{1:" + format + "}{0} {2:" + format + "}{0} {3:" + format + "}{0} {4:" + format + "}", - separator, A, R, G, B); + DefaultInterpolatedStringHandler handler = new(6, 7, provider, stackalloc char[32]); + handler.AppendLiteral("sc#"); + handler.AppendFormatted(A, format); + handler.AppendFormatted(separator); + handler.AppendLiteral(" "); + handler.AppendFormatted(R, format); + handler.AppendFormatted(separator); + handler.AppendLiteral(" "); + handler.AppendFormatted(G, format); + handler.AppendFormatted(separator); + handler.AppendLiteral(" "); + handler.AppendFormatted(B, format); + return handler.ToStringAndClear(); } - - return sb.ToString(); } } } \ No newline at end of file From fdd5942f719534d435b6151906ddaa9399cdda8e Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Fri, 31 Oct 2025 02:00:38 -0700 Subject: [PATCH 34/43] PR feedback --- ...l.Controls.Primitives.GeneratorPosition.cs | 10 ++- ...crosoft.UI.Xaml.Media.Animation.KeyTime.cs | 17 ++--- ....UI.Xaml.Media.Animation.RepeatBehavior.cs | 27 ++++---- ...icrosoft.UI.Xaml.Media.Media3D.Matrix3D.cs | 34 +++++----- .../Microsoft.UI.Xaml.Media.Matrix.cs | 36 +++------- .../Microsoft.UI.Xaml.CornerRadius.cs | 55 ++++++++------- .../Microsoft.UI.Xaml.Duration.cs | 37 +++++----- .../Microsoft.UI.Xaml.GridLength.cs | 67 ++++++++----------- .../Microsoft.UI.Xaml.Thickness.cs | 36 +++++----- ...l.Controls.Primitives.GeneratorPosition.cs | 10 ++- ...Windows.UI.Xaml.Media.Animation.KeyTime.cs | 17 ++--- ....UI.Xaml.Media.Animation.RepeatBehavior.cs | 27 ++++---- .../Windows.UI.Xaml.Media.Media3D.Matrix3D.cs | 34 +++++----- .../Windows.UI.Xaml.Media.Matrix.cs | 36 +++------- .../Windows.UI.Xaml.CornerRadius.cs | 55 ++++++++------- .../Windows.UI.Xaml.Duration.cs | 37 +++++----- .../Windows.UI.Xaml.GridLength.cs | 67 ++++++++----------- .../Windows.UI.Xaml.Thickness.cs | 36 +++++----- .../additions/Windows.UI/Windows.UI.Color.cs | 17 ++--- 19 files changed, 305 insertions(+), 350 deletions(-) diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs index fd5f26790..c89b9f0bb 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition.cs @@ -5,9 +5,15 @@ namespace Microsoft.UI.Xaml.Controls.Primitives partial struct GeneratorPosition { - public override string ToString() + public readonly override string ToString() { - return string.Concat("GeneratorPosition (", Index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", Offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); + DefaultInterpolatedStringHandler handler = new(21, 2, global::System.Globalization.CultureInfo.InvariantCulture, stackalloc char[64]); + handler.AppendLiteral("GeneratorPosition ("); + handler.AppendFormatted(Index); + handler.AppendLiteral(","); + handler.AppendFormatted(Offset); + handler.AppendLiteral(")"); + return handler.ToStringAndClear(); } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs index b6a92832b..7756cc860 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.KeyTime.cs @@ -7,14 +7,11 @@ namespace Microsoft.UI.Xaml.Media.Animation [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Microsoft.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct KeyTime : IEquatable + public readonly struct KeyTime : IEquatable { public static KeyTime FromTimeSpan(TimeSpan timeSpan) { - if (timeSpan < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(timeSpan)); - } + ArgumentOutOfRangeException.ThrowIfLessThan(timeSpan, TimeSpan.Zero, nameof(timeSpan)); return new KeyTime() { TimeSpan = timeSpan }; } @@ -34,22 +31,22 @@ public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) return !KeyTime.Equals(keyTime1, keyTime2); } - public bool Equals(KeyTime value) + public readonly bool Equals(KeyTime value) { return KeyTime.Equals(this, value); } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - return value is KeyTime && this == (KeyTime)value; + return value is KeyTime keyTime && this == keyTime; } - public override int GetHashCode() + public readonly override int GetHashCode() { return TimeSpan.GetHashCode(); } - public override string ToString() + public readonly override string ToString() { return TimeSpan.ToString(); } diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs index 5df258e92..2e88be353 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -28,10 +28,7 @@ public RepeatBehavior(double count) public RepeatBehavior(TimeSpan duration) { - if (duration < new TimeSpan(0)) - { - throw new ArgumentOutOfRangeException(nameof(duration)); - } + ArgumentOutOfRangeException.ThrowIfLessThan(duration, new TimeSpan(0), nameof(duration)); Duration = duration; Count = 0.0; @@ -49,7 +46,7 @@ public static RepeatBehavior Forever } } - public bool HasCount + public readonly bool HasCount { get { @@ -57,7 +54,7 @@ public bool HasCount } } - public bool HasDuration + public readonly bool HasDuration { get { @@ -80,22 +77,22 @@ public RepeatBehaviorType Type readonly get; set; } - public override string ToString() + public readonly override string ToString() { return InternalToString(null, null); } - public string ToString(IFormatProvider formatProvider) + public readonly string ToString(IFormatProvider formatProvider) { return InternalToString(null, formatProvider); } - string IFormattable.ToString(string format, IFormatProvider formatProvider) + readonly string IFormattable.ToString(string format, IFormatProvider formatProvider) { return InternalToString(format, formatProvider); } - internal string InternalToString(string format, IFormatProvider formatProvider) + internal readonly string InternalToString(string format, IFormatProvider formatProvider) { switch (Type) { @@ -119,11 +116,11 @@ internal string InternalToString(string format, IFormatProvider formatProvider) } } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - if (value is RepeatBehavior) + if (value is RepeatBehavior behavior) { - return this.Equals((RepeatBehavior)value); + return Equals(behavior); } else { @@ -131,7 +128,7 @@ public override bool Equals(object value) } } - public bool Equals(RepeatBehavior repeatBehavior) + public readonly bool Equals(RepeatBehavior repeatBehavior) { if (Type == repeatBehavior.Type) { @@ -154,7 +151,7 @@ public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatB return repeatBehavior1.Equals(repeatBehavior2); } - public override int GetHashCode() + public readonly override int GetHashCode() { return Type switch { diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs index 5fb8c968e..68756303b 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -374,36 +374,36 @@ public static Matrix3D Identity } } - public bool IsIdentity + public readonly bool IsIdentity { get { - return (_m11 == 1 && _m12 == 0 && _m13 == 0 && _m14 == 0 && + return _m11 == 1 && _m12 == 0 && _m13 == 0 && _m14 == 0 && _m21 == 0 && _m22 == 1 && _m23 == 0 && _m24 == 0 && _m31 == 0 && _m32 == 0 && _m33 == 1 && _m34 == 0 && - _offsetX == 0 && _offsetY == 0 && _offsetZ == 0 && _m44 == 1); + _offsetX == 0 && _offsetY == 0 && _offsetZ == 0 && _m44 == 1; } } - public override string ToString() + public readonly override string ToString() { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, null /* format provider */); } - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, provider); } - string IFormattable.ToString(string format, IFormatProvider provider) + readonly string IFormattable.ToString(string format, IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(format, provider); } - private string ConvertToString(string format, IFormatProvider provider) + private readonly string ConvertToString(string format, IFormatProvider provider) { if (IsIdentity) { @@ -412,7 +412,7 @@ private string ConvertToString(string format, IFormatProvider provider) // Helper to get the numeric list separator for a given culture. char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); - DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[64]); + DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[256]); handler.AppendFormatted(_m11, format); handler.AppendFormatted(separator); handler.AppendFormatted(_m12, format); @@ -447,7 +447,7 @@ private string ConvertToString(string format, IFormatProvider provider) return handler.ToStringAndClear(); } - public override int GetHashCode() + public readonly override int GetHashCode() { // Perform field-by-field XOR of HashCodes return M11.GetHashCode() ^ @@ -468,12 +468,12 @@ public override int GetHashCode() M44.GetHashCode(); } - public override bool Equals(object o) + public readonly override bool Equals(object o) { - return o is Matrix3D && Matrix3D.Equals(this, (Matrix3D)o); + return o is Matrix3D matrix && Equals(this, matrix); } - public bool Equals(Matrix3D value) + public readonly bool Equals(Matrix3D value) { return Matrix3D.Equals(this, value); } @@ -577,7 +577,7 @@ public bool Equals(Matrix3D value) return matrix3D; } - public bool HasInverse + public readonly bool HasInverse { get { @@ -646,7 +646,7 @@ private static bool Equals(Matrix3D matrix1, Matrix3D matrix2) matrix1.M44.Equals(matrix2.M44); } - private double GetNormalizedAffineDeterminant() + private readonly double GetNormalizedAffineDeterminant() { double z20 = _m12 * _m23 - _m22 * _m13; double z10 = _m32 * _m13 - _m12 * _m33; @@ -655,15 +655,15 @@ private double GetNormalizedAffineDeterminant() return _m31 * z20 + _m21 * z10 + _m11 * z00; } - private bool IsAffine + private readonly bool IsAffine { get { - return (_m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0); + return _m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0; } } - private double Determinant + private readonly double Determinant { get { diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs index da4ef1488..4f3631c67 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.Matrix.cs @@ -16,33 +16,33 @@ public static Matrix Identity } } - public bool IsIdentity + public readonly bool IsIdentity { get { - return (M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0); + return M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0; } } - public override string ToString() + public readonly override string ToString() { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, null /* format provider */); } - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, provider); } - string IFormattable.ToString(string format, IFormatProvider provider) + readonly string IFormattable.ToString(string format, IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(format, provider); } - private string ConvertToString(string format, IFormatProvider provider) + private readonly string ConvertToString(string format, IFormatProvider provider) { if (IsIdentity) { @@ -66,7 +66,7 @@ private string ConvertToString(string format, IFormatProvider provider) return handler.ToStringAndClear(); } - public Point Transform(Point point) + public readonly Point Transform(Point point) { float x = (float)point.X; float y = (float)point.Y; @@ -77,26 +77,12 @@ public Point Transform(Point point) private static Matrix CreateIdentity() { - Matrix matrix = default; - matrix.SetMatrix(1, 0, - 0, 1, - 0, 0); - return matrix; + return new Matrix(1, 0, + 0, 1, + 0, 0); } - private void SetMatrix(double m11, double m12, - double m21, double m22, - double offsetX, double offsetY) - { - M11 = m11; - M12 = m12; - M21 = m21; - M22 = m22; - OffsetX = offsetX; - OffsetY = offsetY; - } - - private void MultiplyPoint(ref float x, ref float y) + private readonly void MultiplyPoint(ref float x, ref float y) { double num = (y * M21) + OffsetX; double num2 = (x * M12) + OffsetY; diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs index aa33c0d96..4459762ea 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.CornerRadius.cs @@ -45,53 +45,56 @@ private static void Validate(double topLeft, double topRight, double bottomRight throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); } - public override string ToString() + public readonly override string ToString() { return ToString(global::System.Globalization.CultureInfo.InvariantCulture); } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + private readonly string ToString(global::System.Globalization.CultureInfo cultureInfo) { char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(_TopLeft, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_TopRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomLeft, cultureInfo)); - return sb.ToString(); + // 3 = 3x separator characters + DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]); + InternalAddToHandler(_TopLeft, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_TopRight, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_BottomRight, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_BottomLeft, ref handler); + return handler.ToStringAndClear(); } - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + private static void InternalAddToHandler(double l, ref DefaultInterpolatedStringHandler handler) { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); + if (double.IsNaN(l)) + { + handler.AppendFormatted("Auto"); + } + else + { + handler.AppendFormatted(l); + } } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { - if (obj is CornerRadius) + if (obj is CornerRadius cornerRadius) { - CornerRadius otherObj = (CornerRadius)obj; - return (this == otherObj); + return this == cornerRadius; } - return (false); + return false; } - public bool Equals(CornerRadius cornerRadius) + public readonly bool Equals(CornerRadius cornerRadius) { - return (this == cornerRadius); + return this == cornerRadius; } - public override int GetHashCode() + public readonly override int GetHashCode() { return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); } @@ -103,7 +106,7 @@ public override int GetHashCode() public static bool operator !=(CornerRadius cr1, CornerRadius cr2) { - return (!(cr1 == cr2)); + return !(cr1 == cr2); } public double TopLeft diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs index e04bab35f..04a704653 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Duration.cs @@ -7,10 +7,10 @@ namespace Microsoft.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Microsoft.UI.Xaml.DurationComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Duration : IEquatable + public readonly struct Duration : IEquatable { private readonly TimeSpan _timeSpan; - private DurationType _durationType; + private readonly DurationType _durationType; public Duration(TimeSpan timeSpan) { @@ -18,6 +18,11 @@ public Duration(TimeSpan timeSpan) _timeSpan = timeSpan; } + private Duration(DurationType durationType) + { + _durationType = durationType; + } + public static implicit operator Duration(TimeSpan timeSpan) { return new Duration(timeSpan); @@ -177,7 +182,7 @@ public static int Compare(Duration t1, Duration t2) return duration; } - public bool HasTimeSpan + public readonly bool HasTimeSpan { get { @@ -189,10 +194,7 @@ public static Duration Automatic { get { - Duration duration = default; - duration._durationType = DurationType.Automatic; - - return duration; + return new Duration(DurationType.Automatic); } } @@ -200,14 +202,11 @@ public static Duration Forever { get { - Duration duration = default; - duration._durationType = DurationType.Forever; - - return duration; + return new Duration(DurationType.Forever); } } - public TimeSpan TimeSpan + public readonly TimeSpan TimeSpan { get { @@ -222,17 +221,17 @@ public TimeSpan TimeSpan } } - public Duration Add(Duration duration) + public readonly Duration Add(Duration duration) { return this + duration; } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - return value is Duration && Equals((Duration)value); + return value is Duration duration && Equals(duration); } - public bool Equals(Duration duration) + public readonly bool Equals(Duration duration) { if (HasTimeSpan) { @@ -256,7 +255,7 @@ public static bool Equals(Duration t1, Duration t2) return t1.Equals(t2); } - public override int GetHashCode() + public readonly override int GetHashCode() { if (HasTimeSpan) { @@ -268,12 +267,12 @@ public override int GetHashCode() } } - public Duration Subtract(Duration duration) + public readonly Duration Subtract(Duration duration) { return this - duration; } - public override string ToString() + public readonly override string ToString() { if (HasTimeSpan) { diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs index 104aaf4f2..aa901dabe 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.GridLength.cs @@ -7,13 +7,13 @@ namespace Microsoft.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Microsoft.UI.Xaml.GridLengthComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct GridLength : IEquatable + public readonly struct GridLength : IEquatable { private readonly double _unitValue; private readonly GridUnitType _unitType; private const double Default = 1.0; - private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); + private static readonly GridLength s_auto = new(Default, GridUnitType.Auto); public GridLength(double pixels) : this(pixels, GridUnitType.Pixel) @@ -41,77 +41,66 @@ public GridLength(double value, GridUnitType type) } - public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } - public GridUnitType GridUnitType { get { return (_unitType); } } + public readonly double Value { get { return (_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue; } } + public readonly GridUnitType GridUnitType { get { return _unitType; } } - public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } - public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } - public bool IsStar { get { return (_unitType == GridUnitType.Star); } } + public readonly bool IsAbsolute { get { return _unitType == GridUnitType.Pixel; } } + public readonly bool IsAuto { get { return _unitType == GridUnitType.Auto; } } + public readonly bool IsStar { get { return _unitType == GridUnitType.Star; } } public static GridLength Auto { - get { return (s_auto); } + get { return s_auto; } } - public static bool operator ==(GridLength gl1, GridLength gl2) { - return (gl1.GridUnitType == gl2.GridUnitType - && gl1.Value == gl2.Value); + return gl1.GridUnitType == gl2.GridUnitType + && gl1.Value == gl2.Value; } public static bool operator !=(GridLength gl1, GridLength gl2) { - return (gl1.GridUnitType != gl2.GridUnitType - || gl1.Value != gl2.Value); + return gl1.GridUnitType != gl2.GridUnitType + || gl1.Value != gl2.Value; } - public override bool Equals(object oCompare) + public readonly override bool Equals(object oCompare) { - if (oCompare is GridLength) + if (oCompare is GridLength gridLength) { - GridLength l = (GridLength)oCompare; - return (this == l); + return this == gridLength; } - else - return false; - } - public bool Equals(GridLength gridLength) - { - return (this == gridLength); + return false; } - public override int GetHashCode() + public readonly bool Equals(GridLength gridLength) { - return ((int)_unitValue + (int)_unitType); + return this == gridLength; } - public override string ToString() + public readonly override int GetHashCode() { - return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); + return (int)_unitValue + (int)_unitType; } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + public readonly override string ToString() { - // Initial capacity [64] is an estimate based on a sum of: - // 12 = 1x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 2 = 2x separator characters - if (_unitType == GridUnitType.Auto) { return "Auto"; } - else if (_unitType == GridUnitType.Pixel) - { - return Convert.ToString(_unitValue, cultureInfo); - } - else + + bool isStar = (_unitType == GridUnitType.Star); + DefaultInterpolatedStringHandler handler = new(isStar ? 1 : 0, 1, global::System.Globalization.CultureInfo.InvariantCulture, stackalloc char[32]); + handler.AppendFormatted(_unitValue); + if (isStar) { - return Convert.ToString(_unitValue, cultureInfo) + "*"; + handler.AppendLiteral("*"); } + return handler.ToStringAndClear(); } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs index 1b4794432..d902878b1 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.Thickness.cs @@ -10,35 +10,39 @@ public Thickness(double uniformLength) Left = Top = Right = Bottom = uniformLength; } - public override string ToString() + public readonly override string ToString() { return ToString(global::System.Globalization.CultureInfo.InvariantCulture); } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + private readonly string ToString(global::System.Globalization.CultureInfo cultureInfo) { char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(Left, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Top, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Right, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Bottom, cultureInfo)); - return sb.ToString(); + DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]); + InternalAddToHandler(Left, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Top, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Right, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Bottom, ref handler); + return handler.ToStringAndClear(); } - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + private static void InternalAddToHandler(double l, ref DefaultInterpolatedStringHandler handler) { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); + if (double.IsNaN(l)) + { + handler.AppendFormatted("Auto"); + } + else + { + handler.AppendFormatted(l); + } } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs index 99f9a49a3..46d931f76 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls.Primitives/Windows.UI.Xaml.Controls.Primitives.GeneratorPosition.cs @@ -5,9 +5,15 @@ namespace Windows.UI.Xaml.Controls.Primitives partial struct GeneratorPosition { - public override string ToString() + public readonly override string ToString() { - return string.Concat("GeneratorPosition (", Index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", Offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); + DefaultInterpolatedStringHandler handler = new(21, 2, global::System.Globalization.CultureInfo.InvariantCulture, stackalloc char[64]); + handler.AppendLiteral("GeneratorPosition ("); + handler.AppendFormatted(Index); + handler.AppendLiteral(","); + handler.AppendFormatted(Offset); + handler.AppendLiteral(")"); + return handler.ToStringAndClear(); } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs index 31a9da6c2..4d4151b0d 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.KeyTime.cs @@ -7,14 +7,11 @@ namespace Windows.UI.Xaml.Media.Animation [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.Media.Animation.KeyTimeComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct KeyTime : IEquatable + public readonly struct KeyTime : IEquatable { public static KeyTime FromTimeSpan(TimeSpan timeSpan) { - if (timeSpan < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(timeSpan)); - } + ArgumentOutOfRangeException.ThrowIfLessThan(timeSpan, TimeSpan.Zero, nameof(timeSpan)); return new KeyTime() { TimeSpan = timeSpan }; } @@ -34,22 +31,22 @@ public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) return !KeyTime.Equals(keyTime1, keyTime2); } - public bool Equals(KeyTime value) + public readonly bool Equals(KeyTime value) { return KeyTime.Equals(this, value); } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - return value is KeyTime && this == (KeyTime)value; + return value is KeyTime keyTime && this == keyTime; } - public override int GetHashCode() + public readonly override int GetHashCode() { return TimeSpan.GetHashCode(); } - public override string ToString() + public readonly override string ToString() { return TimeSpan.ToString(); } diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs index a89c4c030..84f04ec05 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.RepeatBehavior.cs @@ -28,10 +28,7 @@ public RepeatBehavior(double count) public RepeatBehavior(TimeSpan duration) { - if (duration < new TimeSpan(0)) - { - throw new ArgumentOutOfRangeException(nameof(duration)); - } + ArgumentOutOfRangeException.ThrowIfLessThan(duration, new TimeSpan(0), nameof(duration)); Duration = duration; Count = 0.0; @@ -49,7 +46,7 @@ public static RepeatBehavior Forever } } - public bool HasCount + public readonly bool HasCount { get { @@ -57,7 +54,7 @@ public bool HasCount } } - public bool HasDuration + public readonly bool HasDuration { get { @@ -80,22 +77,22 @@ public RepeatBehaviorType Type readonly get; set; } - public override string ToString() + public readonly override string ToString() { return InternalToString(null, null); } - public string ToString(IFormatProvider formatProvider) + public readonly string ToString(IFormatProvider formatProvider) { return InternalToString(null, formatProvider); } - string IFormattable.ToString(string format, IFormatProvider formatProvider) + readonly string IFormattable.ToString(string format, IFormatProvider formatProvider) { return InternalToString(format, formatProvider); } - internal string InternalToString(string format, IFormatProvider formatProvider) + internal readonly string InternalToString(string format, IFormatProvider formatProvider) { switch (Type) { @@ -119,11 +116,11 @@ internal string InternalToString(string format, IFormatProvider formatProvider) } } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - if (value is RepeatBehavior) + if (value is RepeatBehavior behavior) { - return this.Equals((RepeatBehavior)value); + return Equals(behavior); } else { @@ -131,7 +128,7 @@ public override bool Equals(object value) } } - public bool Equals(RepeatBehavior repeatBehavior) + public readonly bool Equals(RepeatBehavior repeatBehavior) { if (Type == repeatBehavior.Type) { @@ -154,7 +151,7 @@ public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatB return repeatBehavior1.Equals(repeatBehavior2); } - public override int GetHashCode() + public readonly override int GetHashCode() { return Type switch { diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs index 83b7f55ad..6ce09403a 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.Matrix3D.cs @@ -374,36 +374,36 @@ public static Matrix3D Identity } } - public bool IsIdentity + public readonly bool IsIdentity { get { - return (_m11 == 1 && _m12 == 0 && _m13 == 0 && _m14 == 0 && + return _m11 == 1 && _m12 == 0 && _m13 == 0 && _m14 == 0 && _m21 == 0 && _m22 == 1 && _m23 == 0 && _m24 == 0 && _m31 == 0 && _m32 == 0 && _m33 == 1 && _m34 == 0 && - _offsetX == 0 && _offsetY == 0 && _offsetZ == 0 && _m44 == 1); + _offsetX == 0 && _offsetY == 0 && _offsetZ == 0 && _m44 == 1; } } - public override string ToString() + public readonly override string ToString() { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, null /* format provider */); } - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, provider); } - string IFormattable.ToString(string format, IFormatProvider provider) + readonly string IFormattable.ToString(string format, IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(format, provider); } - private string ConvertToString(string format, IFormatProvider provider) + private readonly string ConvertToString(string format, IFormatProvider provider) { if (IsIdentity) { @@ -412,7 +412,7 @@ private string ConvertToString(string format, IFormatProvider provider) // Helper to get the numeric list separator for a given culture. char separator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(provider); - DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[64]); + DefaultInterpolatedStringHandler handler = new(0, 31, provider, stackalloc char[256]); handler.AppendFormatted(_m11, format); handler.AppendFormatted(separator); handler.AppendFormatted(_m12, format); @@ -447,7 +447,7 @@ private string ConvertToString(string format, IFormatProvider provider) return handler.ToStringAndClear(); } - public override int GetHashCode() + public readonly override int GetHashCode() { // Perform field-by-field XOR of HashCodes return M11.GetHashCode() ^ @@ -468,12 +468,12 @@ public override int GetHashCode() M44.GetHashCode(); } - public override bool Equals(object o) + public readonly override bool Equals(object o) { - return o is Matrix3D && Matrix3D.Equals(this, (Matrix3D)o); + return o is Matrix3D matrix && Equals(this, matrix); } - public bool Equals(Matrix3D value) + public readonly bool Equals(Matrix3D value) { return Matrix3D.Equals(this, value); } @@ -577,7 +577,7 @@ public bool Equals(Matrix3D value) return matrix3D; } - public bool HasInverse + public readonly bool HasInverse { get { @@ -646,7 +646,7 @@ private static bool Equals(Matrix3D matrix1, Matrix3D matrix2) matrix1.M44.Equals(matrix2.M44); } - private double GetNormalizedAffineDeterminant() + private readonly double GetNormalizedAffineDeterminant() { double z20 = _m12 * _m23 - _m22 * _m13; double z10 = _m32 * _m13 - _m12 * _m33; @@ -655,15 +655,15 @@ private double GetNormalizedAffineDeterminant() return _m31 * z20 + _m21 * z10 + _m11 * z00; } - private bool IsAffine + private readonly bool IsAffine { get { - return (_m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0); + return _m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0; } } - private double Determinant + private readonly double Determinant { get { diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs index 60b950839..5ed950be4 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.Matrix.cs @@ -16,33 +16,33 @@ public static Matrix Identity } } - public bool IsIdentity + public readonly bool IsIdentity { get { - return (M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0); + return M11 == 1 && M12 == 0 && M21 == 0 && M22 == 1 && OffsetX == 0 && OffsetY == 0; } } - public override string ToString() + public readonly override string ToString() { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, null /* format provider */); } - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null /* format string */, provider); } - string IFormattable.ToString(string format, IFormatProvider provider) + readonly string IFormattable.ToString(string format, IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(format, provider); } - private string ConvertToString(string format, IFormatProvider provider) + private readonly string ConvertToString(string format, IFormatProvider provider) { if (IsIdentity) { @@ -66,7 +66,7 @@ private string ConvertToString(string format, IFormatProvider provider) return handler.ToStringAndClear(); } - public Point Transform(Point point) + public readonly Point Transform(Point point) { float x = (float)point.X; float y = (float)point.Y; @@ -77,26 +77,12 @@ public Point Transform(Point point) private static Matrix CreateIdentity() { - Matrix matrix = default; - matrix.SetMatrix(1, 0, - 0, 1, - 0, 0); - return matrix; + return new Matrix(1, 0, + 0, 1, + 0, 0); } - private void SetMatrix(double m11, double m12, - double m21, double m22, - double offsetX, double offsetY) - { - M11 = m11; - M12 = m12; - M21 = m21; - M22 = m22; - OffsetX = offsetX; - OffsetY = offsetY; - } - - private void MultiplyPoint(ref float x, ref float y) + private readonly void MultiplyPoint(ref float x, ref float y) { double num = (y * M21) + OffsetX; double num2 = (x * M12) + OffsetY; diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs index 238d01502..d622579de 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.CornerRadius.cs @@ -45,53 +45,56 @@ private static void Validate(double topLeft, double topRight, double bottomRight throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); } - public override string ToString() + public readonly override string ToString() { return ToString(global::System.Globalization.CultureInfo.InvariantCulture); } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + private readonly string ToString(global::System.Globalization.CultureInfo cultureInfo) { char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(_TopLeft, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_TopRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomRight, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(_BottomLeft, cultureInfo)); - return sb.ToString(); + // 3 = 3x separator characters + DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]); + InternalAddToHandler(_TopLeft, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_TopRight, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_BottomRight, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(_BottomLeft, ref handler); + return handler.ToStringAndClear(); } - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + private static void InternalAddToHandler(double l, ref DefaultInterpolatedStringHandler handler) { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); + if (double.IsNaN(l)) + { + handler.AppendFormatted("Auto"); + } + else + { + handler.AppendFormatted(l); + } } - public override bool Equals(object obj) + public readonly override bool Equals(object obj) { - if (obj is CornerRadius) + if (obj is CornerRadius cornerRadius) { - CornerRadius otherObj = (CornerRadius)obj; - return (this == otherObj); + return this == cornerRadius; } - return (false); + return false; } - public bool Equals(CornerRadius cornerRadius) + public readonly bool Equals(CornerRadius cornerRadius) { - return (this == cornerRadius); + return this == cornerRadius; } - public override int GetHashCode() + public readonly override int GetHashCode() { return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); } @@ -103,7 +106,7 @@ public override int GetHashCode() public static bool operator !=(CornerRadius cr1, CornerRadius cr2) { - return (!(cr1 == cr2)); + return !(cr1 == cr2); } public double TopLeft diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs index eeee7b095..5f0545960 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Duration.cs @@ -7,10 +7,10 @@ namespace Windows.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.DurationComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct Duration : IEquatable + public readonly struct Duration : IEquatable { private readonly TimeSpan _timeSpan; - private DurationType _durationType; + private readonly DurationType _durationType; public Duration(TimeSpan timeSpan) { @@ -18,6 +18,11 @@ public Duration(TimeSpan timeSpan) _timeSpan = timeSpan; } + private Duration(DurationType durationType) + { + _durationType = durationType; + } + public static implicit operator Duration(TimeSpan timeSpan) { return new Duration(timeSpan); @@ -177,7 +182,7 @@ public static int Compare(Duration t1, Duration t2) return duration; } - public bool HasTimeSpan + public readonly bool HasTimeSpan { get { @@ -189,10 +194,7 @@ public static Duration Automatic { get { - Duration duration = default; - duration._durationType = DurationType.Automatic; - - return duration; + return new Duration(DurationType.Automatic); } } @@ -200,14 +202,11 @@ public static Duration Forever { get { - Duration duration = default; - duration._durationType = DurationType.Forever; - - return duration; + return new Duration(DurationType.Forever); } } - public TimeSpan TimeSpan + public readonly TimeSpan TimeSpan { get { @@ -222,17 +221,17 @@ public TimeSpan TimeSpan } } - public Duration Add(Duration duration) + public readonly Duration Add(Duration duration) { return this + duration; } - public override bool Equals(object value) + public readonly override bool Equals(object value) { - return value is Duration && Equals((Duration)value); + return value is Duration duration && Equals(duration); } - public bool Equals(Duration duration) + public readonly bool Equals(Duration duration) { if (HasTimeSpan) { @@ -256,7 +255,7 @@ public static bool Equals(Duration t1, Duration t2) return t1.Equals(t2); } - public override int GetHashCode() + public readonly override int GetHashCode() { if (HasTimeSpan) { @@ -268,12 +267,12 @@ public override int GetHashCode() } } - public Duration Subtract(Duration duration) + public readonly Duration Subtract(Duration duration) { return this - duration; } - public override string ToString() + public readonly override string ToString() { if (HasTimeSpan) { diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs index 96a9d933b..24dcdfb19 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.GridLength.cs @@ -7,13 +7,13 @@ namespace Windows.UI.Xaml [WindowsRuntimeClassName("Windows.Foundation.IReference")] [ABI.Windows.UI.Xaml.GridLengthComWrappersMarshaller] [StructLayout(LayoutKind.Sequential)] - public struct GridLength : IEquatable + public readonly struct GridLength : IEquatable { private readonly double _unitValue; private readonly GridUnitType _unitType; private const double Default = 1.0; - private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); + private static readonly GridLength s_auto = new(Default, GridUnitType.Auto); public GridLength(double pixels) : this(pixels, GridUnitType.Pixel) @@ -41,77 +41,66 @@ public GridLength(double value, GridUnitType type) } - public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } - public GridUnitType GridUnitType { get { return (_unitType); } } + public readonly double Value { get { return (_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue; } } + public readonly GridUnitType GridUnitType { get { return _unitType; } } - public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } - public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } - public bool IsStar { get { return (_unitType == GridUnitType.Star); } } + public readonly bool IsAbsolute { get { return _unitType == GridUnitType.Pixel; } } + public readonly bool IsAuto { get { return _unitType == GridUnitType.Auto; } } + public readonly bool IsStar { get { return _unitType == GridUnitType.Star; } } public static GridLength Auto { - get { return (s_auto); } + get { return s_auto; } } - public static bool operator ==(GridLength gl1, GridLength gl2) { - return (gl1.GridUnitType == gl2.GridUnitType - && gl1.Value == gl2.Value); + return gl1.GridUnitType == gl2.GridUnitType + && gl1.Value == gl2.Value; } public static bool operator !=(GridLength gl1, GridLength gl2) { - return (gl1.GridUnitType != gl2.GridUnitType - || gl1.Value != gl2.Value); + return gl1.GridUnitType != gl2.GridUnitType + || gl1.Value != gl2.Value; } - public override bool Equals(object oCompare) + public readonly override bool Equals(object oCompare) { - if (oCompare is GridLength) + if (oCompare is GridLength gridLength) { - GridLength l = (GridLength)oCompare; - return (this == l); + return this == gridLength; } - else - return false; - } - public bool Equals(GridLength gridLength) - { - return (this == gridLength); + return false; } - public override int GetHashCode() + public readonly bool Equals(GridLength gridLength) { - return ((int)_unitValue + (int)_unitType); + return this == gridLength; } - public override string ToString() + public readonly override int GetHashCode() { - return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); + return (int)_unitValue + (int)_unitType; } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + public readonly override string ToString() { - // Initial capacity [64] is an estimate based on a sum of: - // 12 = 1x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) - // 2 = 2x separator characters - if (_unitType == GridUnitType.Auto) { return "Auto"; } - else if (_unitType == GridUnitType.Pixel) - { - return Convert.ToString(_unitValue, cultureInfo); - } - else + + bool isStar = (_unitType == GridUnitType.Star); + DefaultInterpolatedStringHandler handler = new(isStar ? 1 : 0, 1, global::System.Globalization.CultureInfo.InvariantCulture, stackalloc char[32]); + handler.AppendFormatted(_unitValue); + if (isStar) { - return Convert.ToString(_unitValue, cultureInfo) + "*"; + handler.AppendLiteral("*"); } + return handler.ToStringAndClear(); } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs index d043b0f54..cc67420d3 100644 --- a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.Thickness.cs @@ -10,35 +10,39 @@ public Thickness(double uniformLength) Left = Top = Right = Bottom = uniformLength; } - public override string ToString() + public readonly override string ToString() { return ToString(global::System.Globalization.CultureInfo.InvariantCulture); } - internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + private readonly string ToString(global::System.Globalization.CultureInfo cultureInfo) { char listSeparator = global::WindowsRuntime.InteropServices.TokenizerHelper.GetNumericListSeparator(cultureInfo); // Initial capacity [64] is an estimate based on a sum of: // 48 = 4x double (twelve digits is generous for the range of values likely) - // 8 = 4x Unit Type string (approx two characters) // 4 = 4x separator characters - global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); - - sb.Append(InternalToString(Left, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Top, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Right, cultureInfo)); - sb.Append(listSeparator); - sb.Append(InternalToString(Bottom, cultureInfo)); - return sb.ToString(); + DefaultInterpolatedStringHandler handler = new(0, 7, cultureInfo, stackalloc char[64]); + InternalAddToHandler(Left, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Top, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Right, ref handler); + handler.AppendFormatted(listSeparator); + InternalAddToHandler(Bottom, ref handler); + return handler.ToStringAndClear(); } - internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + private static void InternalAddToHandler(double l, ref DefaultInterpolatedStringHandler handler) { - if (double.IsNaN(l)) return "Auto"; - return Convert.ToString(l, cultureInfo); + if (double.IsNaN(l)) + { + handler.AppendFormatted("Auto"); + } + else + { + handler.AppendFormatted(l); + } } } } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs index 00b4c7377..f66ad04b7 100644 --- a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs +++ b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.Color.cs @@ -8,35 +8,28 @@ partial struct Color : IFormattable { public static Color FromArgb(byte a, byte r, byte g, byte b) { - Color c1 = default; - - c1.A = a; - c1.R = r; - c1.G = g; - c1.B = b; - - return c1; + return new Color(a, r, g, b); } - public override string ToString() + public readonly override string ToString() { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null, null); } - public string ToString(IFormatProvider provider) + public readonly string ToString(IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(null, provider); } - string IFormattable.ToString(string format, IFormatProvider provider) + readonly string IFormattable.ToString(string format, IFormatProvider provider) { // Delegate to the internal method which implements all ToString calls. return ConvertToString(format, provider); } - internal string ConvertToString(string format, IFormatProvider provider) + private readonly string ConvertToString(string format, IFormatProvider provider) { if (format == null) { From c8d3906327a5e1ecb0f42751bbaee75666e0f2f8 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Fri, 31 Oct 2025 02:12:50 -0700 Subject: [PATCH 35/43] Fix unsafe acessors --- src/cswinrt/code_writers.h | 76 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index e2a8c5283..2a63e9d84 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2926,7 +2926,7 @@ IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetEnumerator")] -static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerableMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerableMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); )", element, interop_method_name_prefix ); @@ -2993,16 +2993,16 @@ object IEnumerator.Current => Current; w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "MoveNext")] -static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Reset")] -static extern void %Reset([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Reset([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] -static extern void %Dispose([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Dispose([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Current")] -static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); )", interop_method_name_prefix, interop_method_name_prefix, @@ -3036,22 +3036,22 @@ visibility, element, self, interop_method_name_prefix, objref_name); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); )", key, interop_method_name_prefix, key, value, // Keys value, interop_method_name_prefix, key, value, // Values @@ -3120,46 +3120,46 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`1<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods`2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); )", key, interop_method_name_prefix, key, value, // Keys value, interop_method_name_prefix, key, value, // Values @@ -3275,10 +3275,10 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); )", interop_method_name_prefix, element, // Count element, interop_method_name_prefix, element // Item @@ -3471,37 +3471,37 @@ element, enumerable_type, target); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "IndexOf")] -static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Insert")] -static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % item); +static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RemoveAt")] -static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods`1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); )", interop_method_name_prefix, element, // Count element, interop_method_name_prefix, element, // Item get From cc5792881f68a1f9e89647977d9fd56977b72969 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 1 Nov 2025 01:22:56 -0700 Subject: [PATCH 36/43] Optimizations --- src/cswinrt/code_writers.h | 44 +++++++++++++++++++++++--------------- src/cswinrt/type_writers.h | 1 + 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 2a63e9d84..38c7805c6 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -550,15 +550,16 @@ namespace cswinrt } // This is used to handle scenarios where we do boxing with parameters passed to the constructor. - // This means for pass_array which we project as a ReadOnlySpan, we need to convert it to an array - // to be able to box it. + // This means for pass_array which we project as a ReadOnlySpan, we need to box the address of it. void write_constructor_parameter_name_with_modifier(writer& w, method_signature::param_t const& param) { - write_parameter_name_with_modifier(w, param); - if (get_param_category(param) == param_category::pass_array) { - w.write(".ToArray()"); + w.write("(nint)(&%)", bind(param)); + } + else + { + write_parameter_name_with_modifier(w, param); } } @@ -590,16 +591,16 @@ namespace cswinrt } // This is similar to write_constructor_parameter_name_with_modifier but used in the callback. - void write_constructor_parameter_type(writer& w, method_signature::param_t const& param) + void write_constructor_parameter_cast(writer& w, method_signature::param_t const& param) { if (get_param_category(param) == param_category::pass_array) { auto semantics = get_type_semantics(param.second->Type()); - w.write("%[]", bind(semantics)); + w.write("*(ReadOnlySpan<%>*)(nint)", bind(semantics)); } else { - write_projection_parameter_type(w, param); + w.write("(%)", bind(param)); } } @@ -2615,7 +2616,8 @@ static extern Guid %([UnsafeAccessorType("ABI.InterfaceIIDs, WinRT.Interop.dll") } auto objrefname = bind(semantics); - bool useInner = replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); + bool isDefaultInterface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); + bool useInner = replaceDefaultByInner && isDefaultInterface; if (!useInner) { @@ -2636,7 +2638,7 @@ private WindowsRuntimeObjectReference % } return field ?? MakeObjectReference(); - } + }% } )", [&](writer& w) { @@ -2646,7 +2648,13 @@ private WindowsRuntimeObjectReference % } }, objrefname, - bind(ifaceType)); + bind(ifaceType), + [&](writer& w) { + if(isDefaultInterface) + { + w.write("\n init;"); + } + }); /* TODO handle fast ABI @@ -2702,6 +2710,7 @@ private WindowsRuntimeObjectReference % %% %%(%) :base(%) { +% = NativeObjectReference; %} )", platform_attribute, @@ -2727,6 +2736,7 @@ private WindowsRuntimeObjectReference % bind_list(", ", params_without_objects)); } }), + bind(default_type_semantics), gc_pressure); // Write activation callback method if the constructor has parameters. @@ -5950,10 +5960,10 @@ finally auto const& params = method_sig.params(); for (size_t i = 0; i < params.size(); i++) { - w.write("% % = (%)additionalParameters[%];\n", - bind(params[i]), + w.write("% % = %additionalParameters[%];\n", + bind(params[i]), bind(params[i]), - bind(params[i]), + bind(params[i]), i); } }; @@ -5999,10 +6009,10 @@ void* ThisPtr = activationFactoryValue.GetThisPtrUnsafe(); auto const& params = method_sig.params(); for (size_t i = 0; i < params.size() - 2; i++) { - w.write("% % = (%)additionalParameters[%];\n", - bind(params[i]), + w.write("% % = %additionalParameters[%];\n", + bind(params[i]), bind(params[i]), - bind(params[i]), + bind(params[i]), i); } }; diff --git a/src/cswinrt/type_writers.h b/src/cswinrt/type_writers.h index 63a2c2ded..c420de537 100644 --- a/src/cswinrt/type_writers.h +++ b/src/cswinrt/type_writers.h @@ -58,6 +58,7 @@ using static System.Runtime.InteropServices.ComWrappers; #pragma warning disable CS0649 // "Field '...' is never assigned to" #pragma warning disable CA2207, CA1063, CA1033, CA1001, CA2213 #pragma warning disable CSWINRT3001 // "Type or member '...' is a private implementation detail" +#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type )", VERSION_STRING); } From 08efcbdf6496d86c60c2a084e9ccb8b44348509a Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 1 Nov 2025 01:38:35 -0700 Subject: [PATCH 37/43] Apply optimization to internal constructor --- src/cswinrt/code_writers.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 38c7805c6..1902d1c43 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -8340,7 +8340,7 @@ return MarshalInspectable<%>.FromAbi(thisPtr); % %(WindowsRuntimeObjectReference nativeObjectReference) : base(nativeObjectReference) { -%} +%%} % % @@ -8364,6 +8364,13 @@ return MarshalInspectable<%>.FromAbi(thisPtr); type.Flags().Sealed() ? "internal" : "protected internal", type_name, [&](writer& w) + { + if (!type.Flags().Sealed()) + { + w.write("% = NativeObjectReference;\n", bind(get_type_semantics(get_default_interface(type)))); + } + }, + [&](writer& w) { if (!gc_pressure_amount) return; w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); From e185e84bd0a1b4cfeb352d9ea922c23299470d42 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 1 Nov 2025 19:27:15 -0700 Subject: [PATCH 38/43] Fix test --- src/cswinrt/code_writers.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 1902d1c43..8b72f1750 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2710,7 +2710,10 @@ private WindowsRuntimeObjectReference % %% %%(%) :base(%) { +if (HasUnwrappableNativeObjectReference) +{ % = NativeObjectReference; +} %} )", platform_attribute, From 37275bab33ff0035b44e5a76f1f1738e63d4efde Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 1 Nov 2025 21:58:21 -0700 Subject: [PATCH 39/43] Fix build --- .../WellKnownInterfaceIIDs.g.cs | 49 +++++++++---------- .../InteropServices/WellKnownInterfaceIIDs.tt | 2 +- .../WindowsRuntimeBuffer.cs | 28 +++++------ .../WindowsRuntimeBufferMarshal.cs | 4 +- .../IStorageFolderHandleAccess.cs | 2 +- .../IStorageItemHandleAccess.cs | 2 +- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs index 68098d70b..d6c6cdc4d 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.g.cs @@ -44,6 +44,13 @@ public static ref readonly Guid IID_IMarshal get => ref WellKnownWindowsInterfaceIIDs.IID_IMarshal; } + /// The IID for IMemoryBufferByteAccess. + public static ref readonly Guid IID_IMemoryBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref WellKnownWindowsInterfaceIIDs.IID_IMemoryBufferByteAccess; + } + /// The IID for IInspectable. public static ref readonly Guid IID_IInspectable { @@ -73,133 +80,125 @@ public static ref readonly Guid IID_IWeakReferenceSource } /// The IID for Windows.UI.Xaml.Interop.IBindableIterable (mapped to . - /// The IID for IMemoryBufferByteAccess. - public static ref readonly Guid IID_IMemoryBufferByteAccess - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref WellKnownWindowsInterfaceIIDs.IID_IMemoryBufferByteAccess; - } - - /// The IID for Windows.UI.Xaml.Interop.IBindableIterable (mapped to ). public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableIterable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterable; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterable (mapped to ). + /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterable (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableIterable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterable; } - /// The IID for Windows.UI.Xaml.Interop.IBindableIterator (mapped to ). + /// The IID for Windows.UI.Xaml.Interop.IBindableIterator (mapped to . public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableIterator { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterator; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterator (mapped to ). + /// The IID for Microsoft.UI.Xaml.Interop.IBindableIterator (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableIterator { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableIterator; } - /// The IID for Windows.UI.Xaml.Interop.IBindableVector (mapped to ). + /// The IID for Windows.UI.Xaml.Interop.IBindableVector (mapped to . public static ref readonly Guid IID_Windows_UI_Xaml_Interop_IBindableVector { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableVector; } - /// The IID for Microsoft.UI.Xaml.Interop.IBindableVector (mapped to ). + /// The IID for Microsoft.UI.Xaml.Interop.IBindableVector (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_IBindableVector { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IBindableVector; } - /// The IID for Windows.UI.Xaml.Data.INotifyPropertyChanged (mapped to ). + /// The IID for Windows.UI.Xaml.Data.INotifyPropertyChanged (mapped to . public static ref readonly Guid IID_Windows_UI_Xaml_Data_INotifyPropertyChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_WUX_INotifyPropertyChanged; } - /// The IID for Microsoft.UI.Xaml.Data.INotifyPropertyChanged (mapped to ). + /// The IID for Microsoft.UI.Xaml.Data.INotifyPropertyChanged (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Data_INotifyPropertyChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_MUX_INotifyPropertyChanged; } - /// The IID for Microsoft.UI.Xaml.Data.INotifyDataErrorInfo (mapped to ). + /// The IID for Microsoft.UI.Xaml.Data.INotifyDataErrorInfo (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Data_INotifyDataErrorInfo { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_INotifyDataErrorInfo; } - /// The IID for Windows.UI.Xaml.Interop.INotifyCollectionChanged (mapped to ). + /// The IID for Windows.UI.Xaml.Interop.INotifyCollectionChanged (mapped to . public static ref readonly Guid IID_Windows_UI_Xaml_Interop_INotifyCollectionChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_WUX_INotifyCollectionChanged; } - /// The IID for Microsoft.UI.Xaml.Interop.INotifyCollectionChanged (mapped to ). + /// The IID for Microsoft.UI.Xaml.Interop.INotifyCollectionChanged (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Interop_INotifyCollectionChanged { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_MUX_INotifyCollectionChanged; } - /// The IID for Windows.Foundation.IClosable (mapped to ). + /// The IID for Windows.Foundation.IClosable (mapped to . public static ref readonly Guid IID_Windows_Foundation_IClosable { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IClosable; } - /// The IID for Microsoft.UI.Xaml.IXamlServiceProvider (mapped to ). + /// The IID for Microsoft.UI.Xaml.IXamlServiceProvider (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_IXamlServiceProvider { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IXamlServiceProvider; } - /// The IID for Windows.UI.Xaml.Input.ICommand (mapped to ). + /// The IID for Windows.UI.Xaml.Input.ICommand (mapped to . public static ref readonly Guid IID_Windows_UI_Xaml_Input_ICommand { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_ICommand; } - /// The IID for Microsoft.UI.Xaml.Input.ICommand (mapped to ). + /// The IID for Microsoft.UI.Xaml.Input.ICommand (mapped to . public static ref readonly Guid IID_Microsoft_UI_Xaml_Input_ICommand { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_ICommand; } - /// The IID for Windows.Foundation.Collections.IVectorChangedEventArgs (mapped to ). + /// The IID for Windows.Foundation.Collections.IVectorChangedEventArgs (mapped to . public static ref readonly Guid IID_Windows_Foundation_Collections_IVectorChangedEventArgs { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IVectorChangedEventArgs; } - /// The IID for Windows.Foundation.IAsyncAction (mapped to ). + /// The IID for Windows.Foundation.IAsyncAction (mapped to . public static ref readonly Guid IID_Windows_Foundation_IAsyncAction { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref WellKnownWindowsInterfaceIIDs.IID_IAsyncAction; } - /// The IID for Windows.Foundation.IAsyncInfo (mapped to ). + /// The IID for Windows.Foundation.IAsyncInfo (mapped to . public static ref readonly Guid IID_Windows_Foundation_IAsyncInfo { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt index 5a8540e66..32da8d6fe 100644 --- a/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt +++ b/src/WinRT.Runtime2/InteropServices/WellKnownInterfaceIIDs.tt @@ -79,7 +79,7 @@ for (int i = 0; i < entries.Length; i++) ? $"IID_{iidPrefix}_{windowsRuntimeName}" : $"IID_{windowsRuntimeName}"; #> - /// The IID for <#=windowsRuntimeFullyQualifiedName#><#=mappedTo#>. + /// The IID for <#=windowsRuntimeFullyQualifiedName#> (mapped to . public static ref readonly Guid IID_<#=windowsRuntimeFullyQualifiedName.Replace(".", "_")#> { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs index bc76346ae..32cdf30ea 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs @@ -244,22 +244,22 @@ file static class WindowsRuntimeBufferInterfaceEntriesImpl /// static WindowsRuntimeBufferInterfaceEntriesImpl() { - Entries.IBuffer.IID = ABI.InterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; - Entries.IBuffer.Vtable = ABI.Windows.Storage.Streams.IBufferImpl.Vtable; + Entries.IBuffer.IID = global::ABI.InterfaceIIDs.IID_Windows_Storage_Streams_IBuffer; + Entries.IBuffer.Vtable = global::ABI.Windows.Storage.Streams.IBufferImpl.Vtable; Entries.IBufferByteAccess.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IBufferByteAccess; - Entries.IBufferByteAccess.Vtable = ABI.Windows.Storage.Streams.IBufferByteAccessImpl.Vtable; - Entries.IStringable.IID = IStringableImpl.IID; - Entries.IStringable.Vtable = IStringableImpl.Vtable; - Entries.IWeakReferenceSource.IID = IWeakReferenceSourceImpl.IID; - Entries.IWeakReferenceSource.Vtable = IWeakReferenceSourceImpl.Vtable; + Entries.IBufferByteAccess.Vtable = global::ABI.Windows.Storage.Streams.IBufferByteAccessImpl.Vtable; + Entries.IStringable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IStringable; + Entries.IStringable.Vtable = global::WindowsRuntime.InteropServices.IStringableImpl.Vtable; + Entries.IWeakReferenceSource.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IWeakReferenceSource; + Entries.IWeakReferenceSource.Vtable = global::WindowsRuntime.InteropServices.IWeakReferenceSourceImpl.Vtable; Entries.IMarshal.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMarshal; - Entries.IMarshal.Vtable = ABI.Windows.Storage.Streams.IBufferMarshalImpl.Vtable; - Entries.IAgileObject.IID = IAgileObjectImpl.IID; - Entries.IAgileObject.Vtable = IAgileObjectImpl.Vtable; - Entries.IInspectable.IID = IInspectableImpl.IID; - Entries.IInspectable.Vtable = IInspectableImpl.Vtable; - Entries.IUnknown.IID = IUnknownImpl.IID; - Entries.IUnknown.Vtable = IUnknownImpl.Vtable; + Entries.IMarshal.Vtable = global::ABI.Windows.Storage.Streams.IBufferMarshalImpl.Vtable; + Entries.IAgileObject.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IAgileObject; + Entries.IAgileObject.Vtable = global::WindowsRuntime.InteropServices.IAgileObjectImpl.Vtable; + Entries.IInspectable.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IInspectable; + Entries.IInspectable.Vtable = global::WindowsRuntime.InteropServices.IInspectableImpl.Vtable; + Entries.IUnknown.IID = global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IUnknown; + Entries.IUnknown.Vtable = global::WindowsRuntime.InteropServices.IUnknownImpl.Vtable; } } diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs index a4d4475a3..9f6ac9adb 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferMarshal.cs @@ -34,7 +34,7 @@ public static unsafe bool TryGetDataUnsafe( return false; } - if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && + if (global::WindowsRuntime.InteropServices.WindowsRuntimeComWrappersMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && unwrapped.TryAsUnsafe(global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IBufferByteAccess, out nint thisPtr)) { try @@ -82,7 +82,7 @@ public static unsafe bool TryGetDataUnsafe( return false; } - if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && + if (global::WindowsRuntime.InteropServices.WindowsRuntimeComWrappersMarshal.TryUnwrapObjectReference(buffer, out WindowsRuntimeObjectReference? unwrapped) && unwrapped.TryAsUnsafe(global::WindowsRuntime.InteropServices.WellKnownInterfaceIIDs.IID_IMemoryBufferByteAccess, out nint thisPtr)) { try diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs index 7f78e5990..2e4cf5e89 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs @@ -32,7 +32,7 @@ public static unsafe SafeFileHandle Create( global::Windows.Storage.HANDLE_OPTIONS options, IntPtr oplockBreakingHandler) { - if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(storageFolder, out WindowsRuntimeObjectReference unwrapped) && + if (global::WindowsRuntime.InteropServices.WindowsRuntimeComWrappersMarshal.TryUnwrapObjectReference(storageFolder, out WindowsRuntimeObjectReference unwrapped) && unwrapped.TryAsUnsafe(IID_IStorageFolderHandleAccess, out void* thisPtr)) { SafeFileHandle interopHandle = default; diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs index 3f964a704..d52a45f45 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs @@ -29,7 +29,7 @@ public static unsafe SafeFileHandle Create( global::Windows.Storage.HANDLE_OPTIONS options, IntPtr oplockBreakingHandler) { - if (global::WindowsRuntime.InteropServices.WindowsRuntimeMarshal.TryUnwrapObjectReference(storageFile, out WindowsRuntimeObjectReference unwrapped) && + if (global::WindowsRuntime.InteropServices.WindowsRuntimeComWrappersMarshal.TryUnwrapObjectReference(storageFile, out WindowsRuntimeObjectReference unwrapped) && unwrapped.TryAsUnsafe(IID_IStorageItemHandleAccess, out void* thisPtr)) { SafeFileHandle interopHandle = default; From 7de7e30c1d999340efc85ea997f85c9b94f2ec04 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sat, 1 Nov 2025 22:50:42 -0700 Subject: [PATCH 40/43] Fix build --- .../Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs index 4c248b9b3..46b207e73 100644 --- a/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs +++ b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs @@ -2,6 +2,8 @@ using System.Threading; using WindowsRuntime.InteropServices; +#pragma warning disable CSWINRT3001 + #nullable enable namespace Windows.System; @@ -26,7 +28,7 @@ public DispatcherQueueSynchronizationContext(global::Windows.System.DispatcherQu { ArgumentNullException.ThrowIfNull(dispatcherQueue); - if (!WindowsRuntimeMarshal.TryUnwrapObjectReference(dispatcherQueue, out _objectReference!)) + if (!WindowsRuntimeComWrappersMarshal.TryUnwrapObjectReference(dispatcherQueue, out _objectReference!)) { throw new ArgumentException(null, nameof(dispatcherQueue)); } From 53cc4280ddda773c79bcf3b180fc3bb03d7a4165 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 2 Nov 2025 21:38:16 -0800 Subject: [PATCH 41/43] Fix interop type name --- src/cswinrt/code_writers.h | 406 ++++++++++++++++++++++++++----------- src/cswinrt/helpers.h | 11 +- 2 files changed, 300 insertions(+), 117 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 8b72f1750..a8833cdb4 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -415,16 +415,182 @@ namespace cswinrt return w.write_temp(format, bind(type, nameType, false)); } - void write_interop_dll_type_name(writer& w, type_semantics const& /*semantics*/) + void write_interop_assembly_name(writer& w, TypeDef const& type) { - // TODO - w.write("ABI.WindowsRuntime.InteropServices.<#CsWinRT>EventHandlerEventSource`1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>"); + auto typeNamespace = type.TypeNamespace(); + if (auto proj = get_mapped_type(typeNamespace, type.TypeName())) + { + typeNamespace = proj->mapped_namespace; + if (starts_with(typeNamespace, "System")) + { + w.write("<#corlib>"); + return; + } + else if (!proj->emit_abi) + { + w.write("<#CsWinRT>"); + return; + } + else if (starts_with(typeNamespace, "Windows")) + { + w.write("<#%Windows>"); + return; + } + } - /*for_typedef(w, semantics, [&](auto type) + if (starts_with(typeNamespace, "Windows")) { - write_typedef_name(w, type, nameType, forceWriteNamespace); - write_type_params(w, type); - });*/ + w.write("<#Windows>"); + } + else + { + std::filesystem::path db_path(type.get_database().path()); + auto metadata = db_path.stem().string(); + + // Replace namespace seperator with _ within the generic due to that is what interop dll uses. + std::replace(metadata.begin(), metadata.end(), '.', '-'); + + w.write("<%>", metadata); + } + } + + void write_interop_dll_type_name(writer& w, type_semantics const& semantics, typedef_name_type const& nameType); + void write_interop_dll_type_name_for_typedef(writer& w, type_definition const& type, typedef_name_type const& nameType, std::vector generic_args = {}) + { + if (nameType != typedef_name_type::Projected) + { + w.write("ABI."); + } + + if (nameType == typedef_name_type::EventSource && type.TypeNamespace() == "Windows.Foundation") + { + w.write("WindowsRuntime.InteropServices.<#CsWinRT>EventHandlerEventSource'%", size(type.GenericParam())); + } + else + { + auto typeNamespace = std::string(type.TypeNamespace()); + auto typeName = std::string(type.TypeName()); + if (auto proj = get_mapped_type(typeNamespace, typeName)) + { + typeNamespace = std::string(proj->mapped_namespace); + typeName = std::string(proj->mapped_name); + } + + // Replace generic arity with ' due to that is what interop dll uses. + std::replace(typeName.begin(), typeName.end(), '`', '\''); + + // Arrays types are enclosed in <> + if (nameType == typedef_name_type::ArrayMarshaller) + { + w.write("<"); + } + + if (nameType == typedef_name_type::Projected) + { + // Replace namespace seperator with _ within the generic due to that is what interop dll uses. + std::replace(typeNamespace.begin(), typeNamespace.end(), '.', '-'); + w.write("%%-%", bind(type), typeNamespace, typeName); + } + else + { + w.write("%.%%", typeNamespace, bind(type), typeName); + } + } + + if (size(type.GenericParam()) != 0) + { + if (generic_args.size() != 0) + { + w.write("<%>", + bind_list("|", generic_args, typedef_name_type::Projected)); + } + else + { + separator s{ w, "|" }; + int index = 0; + w.write("<%>", + bind_each([&](writer& w, GenericParam const& /*gp*/) + { + s(); + write_interop_dll_type_name(w, w.get_generic_arg_scope(index++).first, typedef_name_type::Projected); + }, type.GenericParam())); + } + } + + // Arrays types are enclosed in <> + if (nameType == typedef_name_type::ArrayMarshaller) + { + w.write(">"); + } + } + + void write_interop_dll_type_name(writer& w, type_semantics const& semantics, typedef_name_type const& nameType) + { + call(semantics, + [&](object_type) + { + if (nameType == typedef_name_type::Projected) + { + w.write("object"); + } + else + { + w.write("ABI.System.<<#corlib>Object>"); + } + }, + [&](guid_type) + { + if (nameType == typedef_name_type::Projected) + { + w.write("System-Guid"); + } + else + { + w.write("ABI.System.<<#corlib>Guid>"); + } + }, + [&](type_type) + { + if (nameType == typedef_name_type::Projected) + { + w.write("Type"); + } + else + { + w.write("ABI.System.<<#corlib>Type>"); + } + }, + [&](type_definition const& type) { write_interop_dll_type_name_for_typedef(w, type, nameType); }, + [&](generic_type_instance const& type) + { + auto guard{ w.push_generic_args(type) }; + write_interop_dll_type_name_for_typedef(w, type.generic_type, nameType, type.generic_args); + }, + [&](fundamental_type const& type) + { + if (nameType == typedef_name_type::Projected) + { + write_fundamental_type(w, type); + } + else + { + w.write("ABI.System.<<#corlib>%>", bind(type)); + } + }, + [&](auto const&) {}); + + if (nameType == typedef_name_type::StaticAbiClass) + { + w.write("Methods"); + } + else if (nameType == typedef_name_type::Marshaller) + { + w.write("Marshaller"); + } + else if (nameType == typedef_name_type::ArrayMarshaller) + { + w.write("ArrayMarshaller"); + } } void write_static_abi_class_generic_instantiation_type(writer& w, type_semantics const& semantics) @@ -1518,12 +1684,12 @@ namespace cswinrt unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), method_name); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference%%); +static extern % %([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference%%); )", method_name, return_type, unsafe_accessor.value(), - bind(parent), + bind(parent, typedef_name_type::StaticAbiClass), signature.params().size() != 0 ? ", " : "", bind_list(", ", signature.params())); } @@ -1808,20 +1974,20 @@ static extern % %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, unsafe_accessor = w.write_temp("%_%", escape_type_name_for_identifier(parent_type_name, true), external_prop_name); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference); +static extern % %([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern void %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference, % value); +static extern void %([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference thisReference, % value); )", // get external_prop_name, prop_type, unsafe_accessor.value(), - bind(params_for_static_getter.value().first), + bind(params_for_static_getter.value().first, typedef_name_type::StaticAbiClass), // set external_prop_name, unsafe_accessor.value(), - bind(params_for_static_getter.value().first), + bind(params_for_static_getter.value().first, typedef_name_type::StaticAbiClass), prop_type); } } @@ -1939,13 +2105,13 @@ static extern void %([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object auto parent_type_name = w.write_temp("%", bind(parent_type, typedef_name_type::Projected, true)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "%")] -static extern % %_%([UnsafeAccessorType("%Methods, WinRT.Interop.dll")] object _, object thisObject, WindowsRuntimeObjectReference thisReference); +static extern % %_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, object thisObject, WindowsRuntimeObjectReference thisReference); )", external_event_name, bind(get_type_semantics(event.EventType()), typedef_name_type::EventSource, false), escape_type_name_for_identifier(parent_type_name, true), external_event_name, - bind(parent_type)); + bind(parent_type, typedef_name_type::StaticAbiClass)); } w.write(R"( @@ -2936,12 +3102,15 @@ IEnumerator IEnumerable.GetEnumerator() => %.GetEnumerator(); auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerable<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; auto interop_method_name_prefix = w.write_temp("IEnumerableMethods_%_", escape_type_name_for_identifier(element, true)); + auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetEnumerator")] -static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerableMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern IEnumerator<%> %GetEnumerator([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerable'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); )", -element, interop_method_name_prefix + element, + interop_method_name_prefix, + element_interop_type_name ); w.write(R"( @@ -3003,25 +3172,30 @@ object IEnumerator.Current => Current; auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerator<%>.", element) : ""; auto visibility = emit_explicit ? "" : "public "; auto interop_method_name_prefix = w.write_temp("IEnumeratorMethods_%_", escape_type_name_for_identifier(element, true)); + auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "MoveNext")] -static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern bool %MoveNext([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerator'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Reset")] -static extern void %Reset([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Reset([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerator'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] -static extern void %Dispose([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Dispose([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerator'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Current")] -static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumeratorMethods'1<<#corlib>System-ComponentModel-DataErrorsChangedEventArgs>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern % %Current([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IEnumerator'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); )", interop_method_name_prefix, +element_interop_type_name, interop_method_name_prefix, +element_interop_type_name, interop_method_name_prefix, +element_interop_type_name, element, -interop_method_name_prefix +interop_method_name_prefix, +element_interop_type_name ); w.write(R"( @@ -3046,32 +3220,34 @@ visibility, element, self, interop_method_name_prefix, objref_name); auto visibility = emit_explicit ? "" : "public "; auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; auto interop_method_name_prefix = w.write_temp("IReadOnlyDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); + auto key_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); + auto value_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(1).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); -)", -key, interop_method_name_prefix, key, value, // Keys -value, interop_method_name_prefix, key, value, // Values -interop_method_name_prefix, key, value, // Count -value, interop_method_name_prefix, key, value, key, // Item -interop_method_name_prefix, key, value, key, // ContainsKey -interop_method_name_prefix, key, value, key, value // TryGetValue +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); +)", +key, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Keys +value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Values +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Count +value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Item +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // ContainsKey +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value // TryGetValue ); w.write(R"( @@ -3130,64 +3306,66 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto visibility = emit_explicit ? "" : "public "; auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; auto interop_method_name_prefix = w.write_temp("IDictionaryMethods_%_%_", escape_type_name_for_identifier(key, true), escape_type_name_for_identifier(value, true)); + auto key_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); + auto value_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(1).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Keys")] -static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Keys([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Values")] -static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern ICollection<%> %Values([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ContainsKey")] -static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %ContainsKey([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryGetValue")] -static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); +static extern bool %TryGetValue([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % key, out % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, WindowsRuntimeObjectReference enumObjRef, KeyValuePair<%, %>[] array, int arrayIndex); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionaryMethods'2<%, %>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); -)", -key, interop_method_name_prefix, key, value, // Keys -value, interop_method_name_prefix, key, value, // Values -interop_method_name_prefix, key, value, // Count -value, interop_method_name_prefix, key, value, key, // Item get -interop_method_name_prefix, key, value, key, value, // Item set -interop_method_name_prefix, key, value, key, value, // Add -interop_method_name_prefix, key, value, key, // ContainsKey -interop_method_name_prefix, key, value, key, // Remove -interop_method_name_prefix, key, value, key, value, // TryGetValue -interop_method_name_prefix, key, value, key, value, // Add -interop_method_name_prefix, key, value, // Clear -interop_method_name_prefix, key, value, key, value, // Contains -interop_method_name_prefix, key, value, key, value, // CopyTo -interop_method_name_prefix, key, value, key, value // Remove +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IDictionary'2<%|%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, KeyValuePair<%, %> item); +)", +key, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Keys +value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Values +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Count +value, interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Item get +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Item set +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Add +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // ContainsKey +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, // Remove +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // TryGetValue +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Add +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, // Clear +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // Contains +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value, // CopyTo +interop_method_name_prefix, key_interop_type_name, value_interop_type_name, key, value // Remove ); w.write(R"( @@ -3285,16 +3463,17 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto visibility = emit_explicit ? "" : "public "; auto objRefName = w.write_temp("%", objref_name); auto interop_method_name_prefix = w.write_temp("IReadOnlyListMethods_%_", escape_type_name_for_identifier(element, true)); + auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IReadOnlyList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); )", -interop_method_name_prefix, element, // Count -element, interop_method_name_prefix, element // Item +interop_method_name_prefix, element_interop_type_name, // Count +element, interop_method_name_prefix, element_interop_type_name // Item ); w.write(R"( @@ -3481,52 +3660,53 @@ element, enumerable_type, target); auto visibility = emit_explicit ? "" : "public "; auto objRefName = w.write_temp("%", objref_name); auto interop_method_name_prefix = w.write_temp("IListMethods_%_", escape_type_name_for_identifier(element, true)); + auto element_interop_type_name = w.write_temp("%", bind(w.get_generic_arg_scope(0).first, typedef_name_type::Projected)); w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Count")] -static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern int %Count([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern % %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Item")] -static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); +static extern void %Item([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % value); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "IndexOf")] -static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern int %IndexOf([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Insert")] -static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % item); +static extern void %Insert([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RemoveAt")] -static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); +static extern void %RemoveAt([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, int index); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Add")] -static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern void %Add([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Clear")] -static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); +static extern void %Clear([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Contains")] -static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +static extern bool %Contains([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyTo")] -static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); +static extern void %CopyTo([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, %[] array, int arrayIndex); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Remove")] -static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IListMethods'1<%>, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); -)", -interop_method_name_prefix, element, // Count -element, interop_method_name_prefix, element, // Item get -interop_method_name_prefix, element, element, // Item set -interop_method_name_prefix, element, element, // IndexOf -interop_method_name_prefix, element, element, // Insert -interop_method_name_prefix, element, // RemoveAt -interop_method_name_prefix, element, element, // Add -interop_method_name_prefix, element, // Clear -interop_method_name_prefix, element, element, // Contains -interop_method_name_prefix, element, element, // CopyTo -interop_method_name_prefix, element, element // Remove +static extern bool %Remove([UnsafeAccessorType("ABI.System.Collections.Generic.<#CsWinRT>IList'1<%>Methods, WinRT.Interop.dll")] object _, WindowsRuntimeObjectReference objRef, % item); +)", +interop_method_name_prefix, element_interop_type_name, // Count +element, interop_method_name_prefix, element_interop_type_name, // Item get +interop_method_name_prefix, element_interop_type_name, element, // Item set +interop_method_name_prefix, element_interop_type_name, element, // IndexOf +interop_method_name_prefix, element_interop_type_name, element, // Insert +interop_method_name_prefix, element_interop_type_name, // RemoveAt +interop_method_name_prefix, element_interop_type_name, element, // Add +interop_method_name_prefix, element_interop_type_name, // Clear +interop_method_name_prefix, element_interop_type_name, element, // Contains +interop_method_name_prefix, element_interop_type_name, element, // CopyTo +interop_method_name_prefix, element_interop_type_name, element // Remove ); w.write(R"( @@ -4797,7 +4977,7 @@ Vtable = %.AbiToProjectionVftablePtr [return: UnsafeAccessorType("%, WinRT.Interop.dll")] static extern object ctor(WindowsRuntimeObjectReference nativeObjectReference, int index); )", -bind(semantics) +bind(semantics, typedef_name_type::EventSource) ); } @@ -4998,7 +5178,7 @@ event % %;)", { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToManaged")] -static extern %[] ConvertToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); +static extern %[] ConvertToManaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, uint length, void** data); )", param_type, @@ -5025,7 +5205,7 @@ interop_dll_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Dispose")] -static extern void Dispose_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); +static extern void Dispose_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, uint length, void** data); )", param_name, @@ -5039,7 +5219,7 @@ interop_dll_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "Free")] -static extern void Free_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data); +static extern void Free_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, uint length, void** data); )", param_name, @@ -5055,7 +5235,7 @@ interop_dll_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); +static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); )", param_name, @@ -5093,7 +5273,7 @@ param_name); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] -static extern void CopyToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); +static extern void CopyToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); )", param_name, @@ -5111,7 +5291,7 @@ param_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] -static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); +static extern void CopyToManaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); )", param_name, @@ -5618,7 +5798,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); if (m.is_array()) { m.is_pinnable = is_type_blittable(semantics, true) && !m.is_out(); - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.marshaler_type += "<" + m.param_type + ">"; m.local_type = w.write_temp("%", bind(semantics)); } @@ -5654,7 +5834,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.marshaler_type = w.write_temp("%Marshaller", bind(type, typedef_name_type::ABI, true)); if (m.is_array()) { - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.local_type = w.write_temp("%", bind(semantics)); if (m.local_type == "void*") { @@ -5668,7 +5848,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.interface_guid = w.write_temp("%", bind(type)); if (distance(type.GenericParam()) > 0) { - m.interop_dll_type = w.write_temp("%", bind(type)); + m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); } } @@ -5677,7 +5857,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); if (m.is_array()) { - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.local_type = "nint"; } else @@ -5690,7 +5870,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.marshaler_type = w.write_temp("%Marshaller", bind(semantics, typedef_name_type::ABI, true)); if (m.is_array()) { - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.local_type = "nint"; } else @@ -5699,7 +5879,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.local_type = m.is_out() ? "void*" : "ObjectReferenceValue"; if (distance(type.GenericParam()) > 0) { - m.interop_dll_type = w.write_temp("%", bind(type)); + m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); } } break; @@ -5714,7 +5894,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); m.marshaler_type = "WindowsRuntimeObjectMarshaller"; if (m.is_array()) { - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.local_type = "nint"; } else @@ -5786,7 +5966,7 @@ global::System.Buffers.ArrayPool<%>.Shared.Return(__%_arrayFromPool); else { m.is_pinnable = is_type_blittable(semantics, true) && !m.is_out(); - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); m.marshaler_type = is_type_blittable(semantics, true) ? "MarshalBlittable" : "MarshalNonBlittable"; m.marshaler_type += "<" + m.param_type + ">"; m.local_type = w.write_temp("%", bind(semantics)); @@ -6820,7 +7000,7 @@ interop_dll_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ConvertToUnmanaged")] -static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); +static extern WindowsRuntimeObjectReferenceValue ConvertToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, out uint length, out void** data); )", param_name, @@ -6847,7 +7027,7 @@ param_type); { w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToManaged")] -static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); +static extern void CopyToManaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, uint length, void** data, Span<%> span); )", param_name, @@ -6923,7 +7103,7 @@ static extern void CopyToManaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT. w.write(R"( [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "CopyToUnmanaged")] -static extern void CopyToUnmanaged_%([UnsafeAccessorType("%ArrayMarshaller, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); +static extern void CopyToUnmanaged_%([UnsafeAccessorType("%, WinRT.Interop.dll")] object _, ReadOnlySpan<%> span, uint length, void** data); CopyToUnmanaged_%(null, __%, __%Size, (void**)%); @@ -7042,7 +7222,7 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); m.marshal_by_object_reference_value = true; if (distance(type.GenericParam()) > 0) { - m.interop_dll_type = w.write_temp("%", bind(type)); + m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); } break; case category::class_type: @@ -7056,7 +7236,7 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); m.marshal_by_object_reference_value = true; if (distance(type.GenericParam()) > 0) { - m.interop_dll_type = w.write_temp("%", bind(type)); + m.interop_dll_type = w.write_temp("%", bind(type, typedef_name_type::Marshaller)); } break; } @@ -7115,7 +7295,7 @@ CopyToUnmanaged_%(null, __%, __%Size, (void**)%); m.marshaler_type += "<" + m.param_type + ">"; } m.local_type = (m.local_type.empty() ? m.param_type : m.local_type) + "[]"; - m.interop_dll_type = w.write_temp("%", bind(semantics)); + m.interop_dll_type = w.write_temp("%", bind(semantics, typedef_name_type::ArrayMarshaller)); } }; @@ -7413,7 +7593,7 @@ static extern % ConvertToManaged([UnsafeAccessorType("%, WinRT.Interop.dll")] ob var __handler = ConvertToManaged(null, %); )", bind(semantics, typedef_name_type::Projected, false), - bind(semantics), + bind(semantics, typedef_name_type::Marshaller), handler_parameter_name ); } diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 4aef9f7b8..571835387 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -782,7 +782,7 @@ namespace cswinrt { "AsyncActionCompletedHandler", "Windows.Foundation", "AsyncActionCompletedHandler" }, { "AsyncStatus", "Windows.Foundation", "AsyncStatus" }, { "DateTime", "System", "DateTimeOffset", true }, - { "EventHandler`1", "System", "EventHandler", false }, + { "EventHandler`1", "System", "EventHandler`1", false }, { "EventRegistrationToken", "WindowsRuntime.InteropServices", "EventRegistrationToken", false }, { "FoundationContract", "Windows.Foundation", "FoundationContract"}, { "HResult", "System", "Exception", true }, @@ -791,13 +791,13 @@ namespace cswinrt { "IClosable", "System", "IDisposable", true, true }, { "IPropertyValue", "Windows.Foundation", "IPropertyValue", true }, { "IReferenceArray`1", "Windows.Foundation", "IReferenceArray", true }, - { "IReference`1", "System", "Nullable", true }, + { "IReference`1", "System", "Nullable`1", true }, { "Point", "Windows.Foundation", "Point" }, { "PropertyType", "Windows.Foundation", "PropertyType" }, { "Rect", "Windows.Foundation", "Rect" }, { "Size", "Windows.Foundation", "Size" }, { "TimeSpan", "System", "TimeSpan", true }, - { "TypedEventHandler`2", "System", "EventHandler", false }, + { "TypedEventHandler`2", "System", "EventHandler`2", false }, { "Uri", "System", "Uri", true } } }, @@ -1265,7 +1265,10 @@ namespace cswinrt ABI, NonProjected, StaticAbiClass, - EventSource + EventSource, + // Used only with interop dll name + Marshaller, + ArrayMarshaller }; std::string get_mapped_element_type(ElementType elementType) From e5ee952413187d04bc453e7d5bed64f697a2bf7b Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 3 Nov 2025 17:02:16 -0800 Subject: [PATCH 42/43] Fix objref to not have global in its identifier name --- src/cswinrt/code_writers.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index a8833cdb4..ae595625d 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2755,8 +2755,7 @@ public %() void write_objref_type_name(writer& w, type_semantics const& ifaceTypeSemantics) { auto objRefTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); - std::string objRefTypeName = "_objRef_" + objRefTypeCode; - w.write("%", escape_type_name_for_identifier(objRefTypeName)); + w.write("_objRef_%", escape_type_name_for_identifier(objRefTypeCode, true)); } void write_unsafe_accessor_for_iid(writer& w, TypeDef ifaceType, bool isInNullableContext = false) From ecf2a600f266659c8bfc0a1c472c262674b3baa5 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Tue, 4 Nov 2025 17:35:49 -0800 Subject: [PATCH 43/43] PR feedback --- .../Collections/IObservableMapMethodsImpl{TKey, TValue}.cs | 3 +-- .../Collections/IObservableVectorMethodsImpl{T}.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs index 89205737e..fe11fdf91 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IObservableMapMethodsImpl{TKey, TValue}.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using ABI.Windows.Foundation.Collections; namespace WindowsRuntime.InteropServices; @@ -25,5 +24,5 @@ public interface IObservableMapMethodsImpl /// The instance to use to invoke the native method. /// The instance associated with . /// - static abstract MapChangedEventHandlerEventSource MapChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference); + static abstract global::ABI.Windows.Foundation.Collections.MapChangedEventHandlerEventSource MapChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference); } diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs index b4c0cca12..e76b5400e 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IObservableVectorMethodsImpl{T}.cs @@ -3,7 +3,6 @@ using System; using System.ComponentModel; -using ABI.Windows.Foundation.Collections; namespace WindowsRuntime.InteropServices; @@ -24,5 +23,5 @@ public interface IObservableVectorMethodsImpl /// The instance to use to invoke the native method. /// The instance associated with . /// - static abstract VectorChangedEventHandlerEventSource VectorChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference); + static abstract global::ABI.Windows.Foundation.Collections.VectorChangedEventHandlerEventSource VectorChanged(WindowsRuntimeObject thisObject, WindowsRuntimeObjectReference thisReference); }