diff --git a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets index 33287c367..7d4ad4bd1 100644 --- a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets +++ b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets @@ -42,7 +42,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage Include="@(RuntimeCopyLocalItems->HasMetadata('NuGetPackageVersion'))" /> <_CsWinRTDetectedPackages Include="%(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage.NuGetPackageId)\%(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage.NuGetPackageVersion)" Condition="@(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage->Count()) > 0" /> <_CsWinRTDetectedDistinctPackages Include="@(_CsWinRTDetectedPackages->Distinct())" /> - + diff --git a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets b/nuget/Microsoft.Windows.CsWinRT.Embedded.targets index ff38fa69c..5fdac7141 100644 --- a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets +++ b/nuget/Microsoft.Windows.CsWinRT.Embedded.targets @@ -69,7 +69,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. - + + diff --git a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs index d23359634..11d952e61 100644 --- a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs +++ b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs @@ -365,7 +365,7 @@ private static string ToFullyQualifiedString(ISymbol symbol) globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, - miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.ExpandNullable); var qualifiedString = symbol.ToDisplayString(symbolDisplayString); return qualifiedString.StartsWith("global::") ? qualifiedString[8..] : qualifiedString; @@ -410,7 +410,8 @@ private static string ToVtableLookupString(ISymbol symbol, List genericA var arity = symbol is INamedTypeSymbol namedType && namedType.Arity > 0 ? "`" + namedType.Arity : ""; var symbolDisplayString = new SymbolDisplayFormat( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable); return symbol.ToDisplayString(symbolDisplayString) + arity; } else @@ -640,9 +641,12 @@ void AddGenericInterfaceInstantiation(INamedTypeSymbol iface) List genericParameters = new(); foreach (var genericParameter in iface.TypeArguments) { + var isNullable = genericParameter.IsValueType && genericParameter.NullableAnnotation.HasFlag(NullableAnnotation.Annotated); + // Handle initialization of nested generics as they may not be // initialized already. - if (genericParameter is INamedTypeSymbol genericParameterIface && + if (!isNullable && + genericParameter is INamedTypeSymbol genericParameterIface && genericParameterIface.IsGenericType) { AddGenericInterfaceInstantiation(genericParameterIface); @@ -651,7 +655,7 @@ void AddGenericInterfaceInstantiation(INamedTypeSymbol iface) genericParameters.Add(new GenericParameter( ToFullyQualifiedString(genericParameter), GeneratorHelper.GetAbiType(genericParameter, mapper), - genericParameter.TypeKind)); + isNullable ? TypeKind.Interface : genericParameter.TypeKind)); } genericInterfacesToAddToVtable.Add(new GenericInterface( diff --git a/src/Authoring/WinRT.SourceGenerator/Helper.cs b/src/Authoring/WinRT.SourceGenerator/Helper.cs index 2d749d017..00d9195b7 100644 --- a/src/Authoring/WinRT.SourceGenerator/Helper.cs +++ b/src/Authoring/WinRT.SourceGenerator/Helper.cs @@ -708,7 +708,7 @@ public static string GetAbiType(ITypeSymbol type, TypeMapper mapper) return "ABI.System.Exception"; } - if (type.IsValueType) + if (type.IsValueType && !type.NullableAnnotation.HasFlag(NullableAnnotation.Annotated)) { string customTypeMapKey = string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName); if (mapper.HasMappingForType(customTypeMapKey)) @@ -912,8 +912,8 @@ public static string GetCreateMarshaler(GenericParameter genericParameter, strin public static string GetCreateMarshaler(string type, string abiType, TypeKind kind, string arg) { - if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType) || - type == "bool" || + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType) || + type == "bool" || type == "char") { return ""; @@ -923,6 +923,11 @@ public static string GetCreateMarshaler(string type, string abiType, TypeKind ki // TODO: Consider switching to pinning return $$"""__{{arg}} = MarshalString.CreateMarshaler({{arg}});"""; } + else if (kind == TypeKind.Struct) + { + string marshalerClass = GetMarshalerClass(type, abiType, kind, false); + return $$"""__{{arg}} = {{marshalerClass}}.CreateMarshaler({{arg}});"""; + } else { string marshalerClass = GetMarshalerClass(type, abiType, kind, false); @@ -1001,6 +1006,10 @@ public static string GetMarshalerDeclaration(string type, string abiType, TypeKi { return ""; } + else if (kind == TypeKind.Struct) + { + return $"{GetAbiMarshalerType(type, abiType, kind, false)}.Marshaler __{arg} = default;"; + } else { return $"{GetAbiMarshalerType(type, abiType, kind, false)} __{arg} = default;"; diff --git a/src/Tests/FunctionalTests/Async/Program.cs b/src/Tests/FunctionalTests/Async/Program.cs index 99288b4fb..3aec25171 100644 --- a/src/Tests/FunctionalTests/Async/Program.cs +++ b/src/Tests/FunctionalTests/Async/Program.cs @@ -5,6 +5,7 @@ using TestComponentCSharp; using Windows.Foundation; using Windows.Storage.Streams; +using Windows.Web.Http; using WinRT; var instance = new Class(); @@ -119,6 +120,20 @@ return 113; } +bool progressCalledWithExpectedResults = false; +var asyncProgressHandler = new AsyncActionProgressHandler((info, progress) => +{ + if (progress.BytesReceived == 3 && progress.TotalBytesToReceive == 4) + { + progressCalledWithExpectedResults = true; + } +}); +Class.UnboxAndCallProgressHandler(asyncProgressHandler); +if (!progressCalledWithExpectedResults) +{ + return 114; +} + return 100; static async Task InvokeAddAsync(Class instance, int lhs, int rhs) diff --git a/src/Tests/FunctionalTests/CCW/Program.cs b/src/Tests/FunctionalTests/CCW/Program.cs index 8d709df53..ed118594f 100644 --- a/src/Tests/FunctionalTests/CCW/Program.cs +++ b/src/Tests/FunctionalTests/CCW/Program.cs @@ -8,6 +8,8 @@ using System.Windows.Input; using System.Runtime.InteropServices; using System.Collections.Specialized; +using Windows.Foundation; +using Windows.Web.Http; var managedProperties = new ManagedProperties(42); var instance = new Class(); @@ -178,6 +180,15 @@ var notifyCollectionChangedActionList = new List(); instance.BindableIterableProperty = notifyCollectionChangedActionList; +var nullableDoubleList = new List(); +instance.BindableIterableProperty = nullableDoubleList; + +var nullableDoubleList2 = new List>(); +instance.BindableIterableProperty = nullableDoubleList2; + +var nullableHandleList = new List(); +instance.BindableIterableProperty = nullableHandleList; + var customCommand = new CustomCommand() as ICommand; ccw = MarshalInspectable.CreateMarshaler(customCommand); ccw.TryAs(ABI.System.Windows.Input.ICommandMethods.IID, out var commandCCW); diff --git a/src/Tests/FunctionalTests/Collections/Program.cs b/src/Tests/FunctionalTests/Collections/Program.cs index 2f20f3648..d0e1ac77e 100644 --- a/src/Tests/FunctionalTests/Collections/Program.cs +++ b/src/Tests/FunctionalTests/Collections/Program.cs @@ -241,6 +241,29 @@ return 101; } +var nullableDoubleList = new List() { 1, 2, null, 3, 4, null}; +var result = instance.Calculate(nullableDoubleList); +if (result != 10) +{ + return 101; +} + +sum = 0; + +var nullableIntList = instance.GetNullableIntList(); +foreach (var num in nullableIntList) +{ + if (num.HasValue) + { + sum += num.Value; + } +} + +if (sum != 3) +{ + return 101; +} + return 100; static bool SequencesEqual(IEnumerable x, params IEnumerable[] list) => list.All((y) => x.SequenceEqual(y)); diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp index 8b4e26b19..d0e9d7b11 100644 --- a/src/Tests/TestComponentCSharp/Class.cpp +++ b/src/Tests/TestComponentCSharp/Class.cpp @@ -1805,6 +1805,33 @@ namespace winrt::TestComponentCSharp::implementation throw hresult_not_implemented(); } + void Class::UnboxAndCallProgressHandler(WF::IInspectable const& httpProgressHandler) + { + Windows::Web::Http::HttpProgress progress; + progress.BytesReceived = 3; + progress.TotalBytesToReceive = 4; + + winrt::unbox_value>(httpProgressHandler)(nullptr, progress); + } + + double Class::Calculate(winrt::Windows::Foundation::Collections::IVector> const& values) + { + double result = 0; + for (auto val : values) + { + if (val) + { + result += val.Value(); + } + } + return result; + } + + winrt::Windows::Foundation::Collections::IVector> Class::GetNullableIntList() + { + return single_threaded_vector>({ 1, nullptr, 2 }); + } + TestComponentCSharp::IProperties1 Class::NativeProperties1() { struct native_properties1 : winrt::implements diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h index 341ca479a..0edaf321b 100644 --- a/src/Tests/TestComponentCSharp/Class.h +++ b/src/Tests/TestComponentCSharp/Class.h @@ -406,6 +406,10 @@ namespace winrt::TestComponentCSharp::implementation static com_array UnboxBooleanArray(IInspectable const& obj); static com_array UnboxStringArray(IInspectable const& obj); + static void UnboxAndCallProgressHandler(IInspectable const& httpProgressHandler); + double Calculate(winrt::Windows::Foundation::Collections::IVector> const& values); + winrt::Windows::Foundation::Collections::IVector> GetNullableIntList(); + static int GetPropertyType(Windows::Foundation::IInspectable const& obj); static hstring GetName(Windows::Foundation::IInspectable const& obj); diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl index 9a45e892c..96c0e29b7 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl @@ -423,6 +423,9 @@ namespace TestComponentCSharp // HResult->Exception type mapping Windows.Foundation.HResult HResultProperty; + Double Calculate(Windows.Foundation.Collections.IVector > values); + Windows.Foundation.Collections.IVector > GetNullableIntList(); + // Boxing static Int32 UnboxInt32(Object obj); static Boolean UnboxBoolean(Object obj); @@ -436,6 +439,7 @@ namespace TestComponentCSharp static Object BoxedDelegate{ get; }; static Object BoxedEnum{ get; }; static Object BoxedEventHandler{ get; }; + static void UnboxAndCallProgressHandler(Object httpProgressHandler); static Int32 GetPropertyType(Object obj); static String GetName(Object obj); diff --git a/src/Tests/TestComponentCSharp/pch.h b/src/Tests/TestComponentCSharp/pch.h index 3ac7f6ff5..b745c18df 100644 --- a/src/Tests/TestComponentCSharp/pch.h +++ b/src/Tests/TestComponentCSharp/pch.h @@ -10,6 +10,7 @@ #include #include #include +#include // TODO: Replace with latest Cpp/WinRT namespace winrt diff --git a/src/WinRT.Runtime/Marshalers.cs b/src/WinRT.Runtime/Marshalers.cs index c2e55f26e..d9581c9a5 100644 --- a/src/WinRT.Runtime/Marshalers.cs +++ b/src/WinRT.Runtime/Marshalers.cs @@ -2120,6 +2120,31 @@ static Marshaler() CopyManagedArray = MarshalGenericHelper.CopyManagedArray; DisposeMarshalerArray = MarshalInterface.DisposeMarshalerArray; DisposeAbiArray = MarshalInterface.DisposeAbiArray; +#if !NET + RefAbiType = AbiType.MakeByRefType(); +#endif + + return; + } + else if (typeof(T).IsNullableT()) + { + AbiType = typeof(IntPtr); + CreateMarshaler = (T value) => MarshalInterface.CreateMarshaler2(value); + CreateMarshaler2 = CreateMarshaler; + GetAbi = (object objRef) => objRef is ObjectReferenceValue objRefValue ? + MarshalInspectable.GetAbi(objRefValue) : MarshalInterface.GetAbi((IObjectReference)objRef); + FromAbi = (object value) => MarshalInterface.FromAbi((IntPtr)value); + FromManaged = (T value) => MarshalInterface.CreateMarshaler2(value).Detach(); + DisposeMarshaler = MarshalInterface.DisposeMarshaler; + DisposeAbi = (object box) => MarshalInterface.DisposeAbi((IntPtr)box); + CreateMarshalerArray = (T[] array) => MarshalInterface.CreateMarshalerArray(array); + GetAbiArray = MarshalInterface.GetAbiArray; + FromAbiArray = MarshalInterface.FromAbiArray; + FromManagedArray = MarshalInterface.FromManagedArray; + CopyManagedArray = MarshalInterface.CopyManagedArray; + DisposeMarshalerArray = MarshalInterface.DisposeMarshalerArray; + DisposeAbiArray = MarshalInterface.DisposeAbiArray; + #if !NET RefAbiType = AbiType.MakeByRefType(); #endif