diff --git a/src/Tests/UnitTest/ComGenerationTests.cs b/src/Tests/UnitTest/ComGenerationTests.cs index 12f79d4895..889b5b8214 100644 --- a/src/Tests/UnitTest/ComGenerationTests.cs +++ b/src/Tests/UnitTest/ComGenerationTests.cs @@ -2,6 +2,8 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using TestComponentCSharp; +using WindowsRuntime.InteropServices; +using WindowsRuntime.InteropServices.Marshalling; namespace UnitTest { @@ -12,6 +14,35 @@ internal partial interface IComInteropGenerated Int64 ReturnWindowHandle(IntPtr hwnd, Guid iid); } + [GeneratedComInterface(Options = ComInterfaceOptions.ManagedObjectWrapper, ExceptionToUnmanagedMarshaller = typeof(RestrictedErrorInfoExceptionMarshaller))] + [Guid("09E1CDE3-76A5-4E01-B0EE-18D48860A55A")] + internal partial interface IExceptionMarshalling + { + void Invoke(); + } + + // A second view of the same COM interface exposes its HRESULT as a marshalled exception. + [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper)] + [Guid("09E1CDE3-76A5-4E01-B0EE-18D48860A55A")] + internal partial interface IExceptionMarshallingPreserveSig + { + [PreserveSig] + [return: MarshalUsing(typeof(RestrictedErrorInfoExceptionMarshaller))] + Exception Invoke(); + } + + [GeneratedComClass] + internal sealed partial class ExceptionMarshalling(Exception exception) : IExceptionMarshalling + { + public void Invoke() + { + if (exception is not null) + { + throw exception; + } + } + } + [TestClass] public class ComGenerationTests { @@ -37,5 +68,64 @@ public void TestHWND() Assert.AreEqual(hwndValue, value); } } + + [TestMethod] + [DataRow(false)] + [DataRow(true)] + public unsafe void TestRestrictedErrorInfoExceptionMarshaller_UnmanagedToManagedOut(bool throwException) + { + Exception expectedException = throwException ? new NotImplementedException("Generated COM exception") : null; + var instance = new ExceptionMarshalling(expectedException); + void* target = ComInterfaceMarshaller.ConvertToUnmanaged(instance); + + try + { + UnitTestHelper.RoClearError(); + + int hresult = ((delegate* unmanaged[MemberFunction])(*(void***)target)[3])(target); + + Assert.AreEqual(expectedException?.HResult ?? 0, hresult); + Assert.AreSame(expectedException, RestrictedErrorInfo.GetExceptionForHR(hresult)); + } + finally + { + ComInterfaceMarshaller.Free(target); + UnitTestHelper.RoClearError(); + } + } + + [TestMethod] + [DataRow(false)] + [DataRow(true)] + public unsafe void TestRestrictedErrorInfoExceptionMarshaller_ManagedToUnmanagedOut(bool throwException) + { + Exception expectedException = throwException ? new NotImplementedException("Generated COM exception") : null; + var instance = new ExceptionMarshalling(expectedException); + void* target = ComInterfaceMarshaller.ConvertToUnmanaged(instance); + + try + { + // Force an RCW instead of unwrapping the CCW, so the generated native-call stub is exercised. + object wrapper = UniqueComInterfaceMarshaller.ConvertToManaged(target); + + try + { + UnitTestHelper.RoClearError(); + + Exception actualException = ((IExceptionMarshallingPreserveSig)wrapper).Invoke(); + + Assert.AreSame(expectedException, actualException); + } + finally + { + ((ComObject)wrapper).FinalRelease(); + } + } + finally + { + ComInterfaceMarshaller.Free(target); + UnitTestHelper.RoClearError(); + } + } } } diff --git a/src/WinRT.Runtime2/InteropServices/Marshalling/RestrictedErrorInfoExceptionMarshaller.cs b/src/WinRT.Runtime2/InteropServices/Marshalling/RestrictedErrorInfoExceptionMarshaller.cs index 8c68ae2054..77919076a0 100644 --- a/src/WinRT.Runtime2/InteropServices/Marshalling/RestrictedErrorInfoExceptionMarshaller.cs +++ b/src/WinRT.Runtime2/InteropServices/Marshalling/RestrictedErrorInfoExceptionMarshaller.cs @@ -18,6 +18,7 @@ namespace WindowsRuntime.InteropServices.Marshalling; /// /// . [CustomMarshaller(typeof(Exception), MarshalMode.ManagedToUnmanagedOut, typeof(RestrictedErrorInfoExceptionMarshaller))] +[CustomMarshaller(typeof(Exception), MarshalMode.UnmanagedToManagedOut, typeof(RestrictedErrorInfoExceptionMarshaller))] public static class RestrictedErrorInfoExceptionMarshaller { ///