From 2736da879fba81635d7e13ebd03d14fee681c5c5 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 30 Jun 2024 02:07:06 -0700 Subject: [PATCH 1/4] Fix interface not found when using Stream operation APIs implemented by the SDK projection --- src/Tests/FunctionalTests/Async/Program.cs | 32 + src/cswinrt/cswinrt.vcxproj | 1 + src/cswinrt/cswinrt.vcxproj.filters | 3 + .../StreamOperationsImplementation.cs | 9 +- .../StreamTaskAdaptersImplementation.cs | 608 ++++++++++++++++++ 5 files changed, 652 insertions(+), 1 deletion(-) create mode 100644 src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs diff --git a/src/Tests/FunctionalTests/Async/Program.cs b/src/Tests/FunctionalTests/Async/Program.cs index eb12a6dfb..99288b4fb 100644 --- a/src/Tests/FunctionalTests/Async/Program.cs +++ b/src/Tests/FunctionalTests/Async/Program.cs @@ -1,7 +1,11 @@ using System; using System.IO; +using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using TestComponentCSharp; +using Windows.Foundation; +using Windows.Storage.Streams; +using WinRT; var instance = new Class(); @@ -87,6 +91,34 @@ return 109; } +using var fileStream = File.OpenRead(folderPath + "\\Async.exe"); +var randomAccessStream = fileStream.AsRandomAccessStream(); +var ptr = MarshalInterface.FromManaged(randomAccessStream); +if (ptr == IntPtr.Zero) +{ + return 110; +} +var arr = new byte[100]; +var buffer = arr.AsBuffer(); +ptr = MarshalInterface.FromManaged(buffer); +if (ptr == IntPtr.Zero) +{ + return 111; +} + +var asyncOperation = randomAccessStream.ReadAsync(buffer, 50, InputStreamOptions.Partial); +ptr = MarshalInterface>.FromManaged(asyncOperation); +if (ptr == IntPtr.Zero) +{ + return 112; +} + +ptr = MarshalInterface.FromManaged(asyncOperation); +if (ptr == IntPtr.Zero) +{ + return 113; +} + return 100; static async Task InvokeAddAsync(Class instance, int lhs, int rhs) diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index d5cc68b7e..b6fa3cabc 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -91,6 +91,7 @@ + diff --git a/src/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 0f31137c2..c505683de 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -192,6 +192,9 @@ strings\additions\Windows.Storage.Streams + + strings\additions\Windows.Storage.Streams + diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs index e297596ba..018ec23e8 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs @@ -25,7 +25,14 @@ namespace Windows.Storage.Streams [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif internal static class StreamOperationsImplementation - { + { +#if NET + static StreamOperationsImplementation() + { + _ = StreamTaskAdaptersImplementation.Initialized; + } +#endif + #region ReadAsync implementations internal static IAsyncOperationWithProgress ReadAsync_MemoryStream(Stream stream, IBuffer buffer, uint count) diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs new file mode 100644 index 000000000..12078ed85 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs @@ -0,0 +1,608 @@ +#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 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 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 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)); + } + finally + { + MarshalInterface>.DisposeMarshaler(__asyncInfo); + + } + } + } + + private static class AsyncOperationWithProgressCompletedHandler_uint_uint + { + private static 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 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 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)); + } + finally + { + MarshalInterface>.DisposeMarshaler(__asyncInfo); + + } + } + } + + private static class AsyncOperationWithProgressCompletedHandler_IBuffer_uint + { + private static 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 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 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 From ed90dfcf3e1ffd2682d07db862c60871fa281490 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Sun, 30 Jun 2024 19:21:45 -0700 Subject: [PATCH 2/4] Make code fixer emit diagnostics as warnings by default and have a warning level to control that --- docs/aot.md | 8 ++-- nuget/Microsoft.Windows.CsWinRT.targets | 19 +++++++++- nuget/readme.md | 1 + src/Authoring/WinRT.SourceGenerator/Helper.cs | 38 +++++++++++++++++++ .../WinRTAotCodeFixer.cs | 13 +++++-- .../WinRT.SourceGenerator/WinRTRules.cs | 13 +++++-- src/Tests/FunctionalTests/CCW/Program.cs | 2 + 7 files changed, 81 insertions(+), 13 deletions(-) diff --git a/docs/aot.md b/docs/aot.md index c363f7c5d..843443d25 100644 --- a/docs/aot.md +++ b/docs/aot.md @@ -6,7 +6,7 @@ As of CsWinRT **2.1.0-preview** and Windows SDK projection 10.0..**35-p ## Consuming projected types and being AOT compatible -If your app or library has non-WinRT classes that implement C#/WinRT projected interfaces or built-in .NET interfaces that are mapped to WinRT types and are passed across the ABI to other WinRT functions, the class needs to be marked `partial` in order to be AOT compatible. Marking it `partial` allows the source generator distributed with C#/WinRT to add an attribute to the class which has the necessary logic to produce the WinRT vtable for it in a way that is both trimming and AOT compatible. +If your app or library has non-WinRT classes that implement C#/WinRT projected interfaces or built-in .NET interfaces that are mapped to WinRT types and are passed across the ABI to other WinRT functions, the class needs to be marked `partial` in order to be AOT compatible. Marking it `partial` allows the source generator distributed with C#/WinRT to add an attribute to the class which has the necessary logic to produce the WinRT vtable for it in a way that is both trimming and AOT compatible. To help with this, there is a `code fixer` that will warn you when such types are not marked `partial`. The deafult `warning level` (`CsWinRTAotWarningLevel`) for this code fixer is `1` which is to `not warn for scenarios involving only built-in types`. But if you pass types implementing only built-in interfaces that are mapped to WinRT across the WinRT ABI, it recommended to set the warning level to `2` and mark such types `partial` or suppress the warning if those types are not passed across the WinRT ABI. The source generator also detects other scenarios such as boxing of arrays and instantiations of generic WinRT types for which it generates code that will allow the WinRT vtable for it to be looked up when passed across the WinRT ABI. @@ -18,11 +18,9 @@ There are a couple issues related to our AOT support that are known with the cur 1. If any built-in .NET private types implementing mapped WinRT interfaces are passed across the WinRT ABI, they will not be detected by the source generator and thereby not be AOT compatible. You can workaround this by either avoiding passing them across the ABI or by registering your own WinRT vtable lookup function using `WinRT.ComWrappersSupport.RegisterTypeComInterfaceEntriesLookup` and `WinRT.ComWrappersSupport.RegisterTypeRuntimeClassNameLookup`. An example of this can be seen [here](https://github.com/manodasanW/WinUI-Gallery/blob/16ed717700b929dcb6591d32a4f10cd8b102aa07/WinUIGallery/VtableInitialization.cs#L57-L75) and [here](https://github.com/manodasanW/WinUI-Gallery/blob/16ed717700b929dcb6591d32a4f10cd8b102aa07/WinUIGallery/VtableInitialization.cs#L87-L90). -2. If you have any nested C# types that you use as part of .NET collections that are passed across the WinRT ABI, the source generator generates the incorrect type name to represent it in the generated lookup function. You can workaround this issue until it is addressed by registering your own WinRT vtable lookup function using `WinRT.ComWrappersSupport.RegisterTypeComInterfaceEntriesLookup` and `WinRT.ComWrappersSupport.RegisterTypeRuntimeClassNameLookup`. An example of this can be seen [here](https://github.com/manodasanW/WinUI-Gallery/blob/16ed717700b929dcb6591d32a4f10cd8b102aa07/WinUIGallery/VtableInitialization.cs#L21-L53) and [here](https://github.com/manodasanW/WinUI-Gallery/blob/16ed717700b929dcb6591d32a4f10cd8b102aa07/WinUIGallery/VtableInitialization.cs#L83-L86). +2. In WinUI apps, you might run into a race condition which triggers a hang during .NET GC needing to restart the app. This is the result of an exception being thrown unexpectedly during GC due to an invalid handle. -3. In WinUI apps, you might run into a race condition which triggers a hang during .NET GC needing to restart the app. This is the result of an exception being thrown unexpectedly during GC due to an invalid handle. - -4. If you have an app or library that just consumes WinRT types and doesn't generate a projection, it still needs a CsWinRT package reference in order for the source generator to run on it and make it AOT compatible. We will be looking at addressing this to avoid this requirement for the official version. +3. If you have an app or library that just consumes WinRT types and doesn't generate a projection, it still needs a CsWinRT package reference in order for the source generator to run on it and make it AOT compatible. We will be looking at addressing this to avoid this requirement for the official version. ## Known issues when publishing for JIT diff --git a/nuget/Microsoft.Windows.CsWinRT.targets b/nuget/Microsoft.Windows.CsWinRT.targets index 0d40334d6..6fa3042bd 100644 --- a/nuget/Microsoft.Windows.CsWinRT.targets +++ b/nuget/Microsoft.Windows.CsWinRT.targets @@ -50,6 +50,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. + @@ -100,7 +101,23 @@ Copyright (C) Microsoft Corporation. All rights reserved. '$(OutputType)' == 'Library' and '$(CsWinRTHasAnyIncludes)' == 'true'">false true - + + + 0 + 1 + diff --git a/nuget/readme.md b/nuget/readme.md index 520197b8c..3c23fb216 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -48,6 +48,7 @@ C#/WinRT behavior can be customized with these project properties: | CsWinRTRcwFactoryFallbackGeneratorForceOptIn | true \| *false | Forces the RCW factory fallback generator to be enabled (it only runs on .exe projects by default) | | CsWinRTRcwFactoryFallbackGeneratorForceOptOut | true \| *false | Forces the RCW factory fallback generator to be disabled (overrides "ForceOptIn" as well) | | CsWinRTMergeReferencedActivationFactories | true \| *false | Makes the native `DllGetActivationFactory` exported function for AOT scenarios also forward the activation call to all referenced WinRT components, allowing them to all be merged into a single executable or shared library | +| CsWinRTAotWarningLevel | 0 \| *1 \| 2 | Specifies the warning level used by the code fixer for trimming and AOT compat (0 - no warnings, 1 - warnings for scenarios involving non built-in types, warnings for all scenarios) | \*Default value **If CsWinRTFilters is not defined, the following effective value is used: diff --git a/src/Authoring/WinRT.SourceGenerator/Helper.cs b/src/Authoring/WinRT.SourceGenerator/Helper.cs index ee087bac1..0bde2a797 100644 --- a/src/Authoring/WinRT.SourceGenerator/Helper.cs +++ b/src/Authoring/WinRT.SourceGenerator/Helper.cs @@ -153,6 +153,17 @@ public static bool IsCsWinRTCcwLookupTableGeneratorEnabled(this AnalyzerConfigOp return false; } + public static int GetCsWinRTAotWarningLevel(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTAotWarningLevel", out var csWinRTAotWarningLevelStr) && + int.TryParse(csWinRTAotWarningLevelStr, out var csWinRTAotWarningLevel)) + { + return csWinRTAotWarningLevel; + } + + return 0; + } + public static bool ShouldGenerateWinMDOnly(this GeneratorExecutionContext context) { if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGenerateWinMDOnly", out var CsWinRTGenerateWinMDOnlyStr)) @@ -302,6 +313,33 @@ public static bool IsWinRTType(ISymbol type, ITypeSymbol winrtRuntimeTypeAttribu return isProjectedType; } + // Assuming a type is a WinRT type, this determines whether it is a WinRT type from custom type mappings. + // i.e Whether it is a built-in type that is also a WinRT type. + public static bool IsCustomMappedType(ISymbol type, TypeMapper mapper) + { + if (IsFundamentalType(type)) + { + return true; + } + + bool isCustomMappedType = false; + if (type.ContainingNamespace != null) + { + isCustomMappedType = mapper.HasMappingForType(string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName)); + } + + // Ensure all generic parameters are WinRT types. + if (isCustomMappedType && + type is INamedTypeSymbol namedType && + namedType.IsGenericType && + !namedType.IsDefinition) + { + isCustomMappedType = namedType.TypeArguments.All(t => IsCustomMappedType(t, mapper)); + } + + return isCustomMappedType; + } + // Checks if the interface references any internal types (either the interface itself or within its generic types). public static bool IsInternalInterfaceFromReferences(INamedTypeSymbol iface, IAssemblySymbol currentAssembly) { diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs index f0f009a15..f48b5b23a 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs @@ -18,13 +18,13 @@ namespace WinRT.SourceGenerator [DiagnosticAnalyzer(LanguageNames.CSharp), Shared] public sealed class WinRTAotDiagnosticAnalyzer : DiagnosticAnalyzer { - private static ImmutableArray _supportedDiagnostics = ImmutableArray.Create(WinRTRules.ClassNotAotCompatible); + private static ImmutableArray _supportedDiagnostics = ImmutableArray.Create(WinRTRules.ClassNotAotCompatibleWarning, WinRTRules.ClassNotAotCompatibleInfo); public override ImmutableArray SupportedDiagnostics => _supportedDiagnostics; public override void Initialize(AnalysisContext context) { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(static context => @@ -43,6 +43,7 @@ public override void Initialize(AnalysisContext context) } var typeMapper = new TypeMapper(context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetUIXamlProjectionsMode()); + var csWinRTAotWarningLevel = context.Options.AnalyzerConfigOptionsProvider.GetCsWinRTAotWarningLevel(); context.RegisterSymbolAction(context => { @@ -63,7 +64,11 @@ public override void Initialize(AnalysisContext context) { if (GeneratorHelper.IsWinRTType(iface, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) { - context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassNotAotCompatible, namedType.Locations[0], namedType.Name)); + // Based on the warning level, emit as a warning or as an info. + var diagnosticDescriptor = (csWinRTAotWarningLevel == 2 || + (csWinRTAotWarningLevel == 1 && !GeneratorHelper.IsCustomMappedType(iface, typeMapper))) ? + WinRTRules.ClassNotAotCompatibleWarning : WinRTRules.ClassNotAotCompatibleInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name)); return; } } @@ -79,7 +84,7 @@ public sealed class WinRTAotCodeFixer : CodeFixProvider { private const string title = "Make type partial"; - private static ImmutableArray _fixableDiagnosticIds = ImmutableArray.Create(WinRTRules.ClassNotAotCompatible.Id); + private static ImmutableArray _fixableDiagnosticIds = ImmutableArray.Create(WinRTRules.ClassNotAotCompatibleWarning.Id); public override ImmutableArray FixableDiagnosticIds => _fixableDiagnosticIds; diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs index 2809ef2ee..c9ae07c20 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs @@ -9,14 +9,14 @@ public class WinRTRules /// string, a few words generally describing the diagnostic /// string, describes the diagnostic -- formatted with {0}, ... -- /// such that data can be passed in for the code the diagnostic is reported for - private static DiagnosticDescriptor MakeRule(string id, string title, string messageFormat, bool isError = true) + private static DiagnosticDescriptor MakeRule(string id, string title, string messageFormat, bool isError = true, bool isWarning = false) { return new DiagnosticDescriptor( id: id, title: title, messageFormat: messageFormat, category: "Usage", - defaultSeverity: isError ? DiagnosticSeverity.Error : DiagnosticSeverity.Info, + defaultSeverity: isError ? DiagnosticSeverity.Error : isWarning? DiagnosticSeverity.Warning : DiagnosticSeverity.Info, isEnabledByDefault: true, helpLinkUri: "https://github.com/microsoft/CsWinRT/tree/master/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md"); } @@ -180,7 +180,14 @@ private static DiagnosticDescriptor MakeRule(string id, string title, string mes CsWinRTDiagnosticStrings.UnimplementedInterface_Brief, CsWinRTDiagnosticStrings.UnimplementedInterface_Text); - public static DiagnosticDescriptor ClassNotAotCompatible = MakeRule( + public static DiagnosticDescriptor ClassNotAotCompatibleWarning = MakeRule( + "CsWinRT1028", + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Text, + false, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleInfo = MakeRule( "CsWinRT1028", CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Text, diff --git a/src/Tests/FunctionalTests/CCW/Program.cs b/src/Tests/FunctionalTests/CCW/Program.cs index e4fe687ec..cb84cae01 100644 --- a/src/Tests/FunctionalTests/CCW/Program.cs +++ b/src/Tests/FunctionalTests/CCW/Program.cs @@ -447,6 +447,7 @@ public static void TestGenericList() instance.BindableIterableProperty = nestedClassList; } +#pragma warning disable CsWinRT1028 // Class is not marked partial sealed class NestedClass : IProperties2 { private int _value; @@ -476,6 +477,7 @@ internal sealed class NestedClass3 : IProperties2 public int ReadWriteProperty { get => _value; set => _value = value; } } } +#pragma warning restore CsWinRT1028 // Class is not marked partial } partial class TestClass2 From 6d559ec28fced66e18d4f6ca1016086012088b53 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 1 Jul 2024 00:21:36 -0700 Subject: [PATCH 3/4] Update docs/aot.md Co-authored-by: Dongle <29563098+dongle-the-gadget@users.noreply.github.com> --- docs/aot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/aot.md b/docs/aot.md index 843443d25..8cd309996 100644 --- a/docs/aot.md +++ b/docs/aot.md @@ -6,7 +6,7 @@ As of CsWinRT **2.1.0-preview** and Windows SDK projection 10.0..**35-p ## Consuming projected types and being AOT compatible -If your app or library has non-WinRT classes that implement C#/WinRT projected interfaces or built-in .NET interfaces that are mapped to WinRT types and are passed across the ABI to other WinRT functions, the class needs to be marked `partial` in order to be AOT compatible. Marking it `partial` allows the source generator distributed with C#/WinRT to add an attribute to the class which has the necessary logic to produce the WinRT vtable for it in a way that is both trimming and AOT compatible. To help with this, there is a `code fixer` that will warn you when such types are not marked `partial`. The deafult `warning level` (`CsWinRTAotWarningLevel`) for this code fixer is `1` which is to `not warn for scenarios involving only built-in types`. But if you pass types implementing only built-in interfaces that are mapped to WinRT across the WinRT ABI, it recommended to set the warning level to `2` and mark such types `partial` or suppress the warning if those types are not passed across the WinRT ABI. +If your app or library has non-WinRT classes that implement C#/WinRT projected interfaces or built-in .NET interfaces that are mapped to WinRT types and are passed across the ABI to other WinRT functions, the class needs to be marked `partial` in order to be AOT compatible. Marking it `partial` allows the source generator distributed with C#/WinRT to add an attribute to the class which has the necessary logic to produce the WinRT vtable for it in a way that is both trimming and AOT compatible. To help with this, there is a `code fixer` that will warn you when such types are not marked `partial`. The default warning level (`CsWinRTAotWarningLevel`) for this code fixer is `1` which is to not warn for scenarios involving only built-in types. But if you pass types implementing only built-in interfaces that are mapped to WinRT across the WinRT ABI, it recommended to set the warning level to `2` and mark such types `partial` or suppress the warning if those types are not passed across the WinRT ABI. The source generator also detects other scenarios such as boxing of arrays and instantiations of generic WinRT types for which it generates code that will allow the WinRT vtable for it to be looked up when passed across the WinRT ABI. From a56281ac2083d70cc1948ddee2b5f104be7c45ae Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 1 Jul 2024 00:22:57 -0700 Subject: [PATCH 4/4] Update nuget/readme.md Co-authored-by: Dongle <29563098+dongle-the-gadget@users.noreply.github.com> --- nuget/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuget/readme.md b/nuget/readme.md index 3c23fb216..39289bdb9 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -48,7 +48,7 @@ C#/WinRT behavior can be customized with these project properties: | CsWinRTRcwFactoryFallbackGeneratorForceOptIn | true \| *false | Forces the RCW factory fallback generator to be enabled (it only runs on .exe projects by default) | | CsWinRTRcwFactoryFallbackGeneratorForceOptOut | true \| *false | Forces the RCW factory fallback generator to be disabled (overrides "ForceOptIn" as well) | | CsWinRTMergeReferencedActivationFactories | true \| *false | Makes the native `DllGetActivationFactory` exported function for AOT scenarios also forward the activation call to all referenced WinRT components, allowing them to all be merged into a single executable or shared library | -| CsWinRTAotWarningLevel | 0 \| *1 \| 2 | Specifies the warning level used by the code fixer for trimming and AOT compat (0 - no warnings, 1 - warnings for scenarios involving non built-in types, warnings for all scenarios) | +| CsWinRTAotWarningLevel | 0 \| *1 \| 2 | Specifies the warning level used by the code fixer for trimming and AOT compat (0 - no warnings, 1 - warnings for scenarios involving non built-in types, 2 - warnings for all scenarios) | \*Default value **If CsWinRTFilters is not defined, the following effective value is used: