From 308ab659f4098bc7df7ed3b1a87a90a209604349 Mon Sep 17 00:00:00 2001 From: Elinor Fung <47805090+elinor-fung@users.noreply.github.com> Date: Tue, 10 Mar 2020 12:24:48 -0700 Subject: [PATCH] Implement Marshal.GetIDispatchForObject on platforms with COM support (#33403) --- .../Runtime/InteropServices/Marshal.CoreCLR.cs | 11 + src/coreclr/src/vm/ecalllist.h | 1 + .../RuntimeBinder/ComInterop/ComInvokeBinder.cs | 2 +- .../RuntimeBinder/ComInterop/ComRuntimeHelpers.cs | 6 - .../RuntimeBinder/ComInterop/DispatchArgBuilder.cs | 2 +- .../Runtime/InteropServices/Marshal.NoCom.cs | 5 + .../src/System/Runtime/InteropServices/Marshal.cs | 2 - .../System.Runtime.InteropServices.Tests.csproj | 1 + .../InteropServices/DispatchWrapperTests.cs | 10 + .../Marshal/GetIDispatchForObjectTests.Windows.cs | 17 +- .../Marshal/GetIDispatchForObjectTests.cs | 15 + .../GetNativeVariantForObjectTests.Windows.cs | 23 +- .../GetObjectForNativeVariantTests.Windows.cs | 594 +++++++++++++++++++++ .../Marshal/GetObjectForNativeVariantTests.cs | 589 +------------------- 14 files changed, 664 insertions(+), 614 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.Windows.cs diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index 9873211..07b7ce8 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -340,6 +340,17 @@ namespace System.Runtime.InteropServices internal static extern IntPtr /* IUnknown* */ GetRawIUnknownForComObjectNoAddRef(object o); /// + /// Return the IDispatch* for an Object. + /// + public static IntPtr /* IDispatch */ GetIDispatchForObject(object o) + { + return GetIDispatchForObjectNative(o, false); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern IntPtr /* IDispatch* */ GetIDispatchForObjectNative(object o, bool onlyInContext); + + /// /// Return the IUnknown* representing the interface for the Object. /// Object o should support Type T /// diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 1ae20b1..3cbfa0e 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -820,6 +820,7 @@ FCFuncStart(gInteropMarshalFuncs) FCFuncElement("GetStartComSlot", MarshalNative::GetStartComSlot) FCFuncElement("GetEndComSlot", MarshalNative::GetEndComSlot) FCFuncElement("GetIUnknownForObjectNative", MarshalNative::GetIUnknownForObjectNative) + FCFuncElement("GetIDispatchForObjectNative", MarshalNative::GetIDispatchForObjectNative) FCFuncElement("GetComInterfaceForObjectNative", MarshalNative::GetComInterfaceForObjectNative) FCFuncElement("InternalReleaseComObject", MarshalNative::ReleaseComObject) FCFuncElement("Release", MarshalNative::Release) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs index ac48657..54bb330 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComInvokeBinder.cs @@ -506,7 +506,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop Expression.Assign( DispatchPointerVariable, Expression.Call( - ComRuntimeHelpers.GetGetIDispatchForObjectMethod(), + typeof(Marshal).GetMethod(nameof(Marshal.GetIDispatchForObject)), DispatchObjectVariable ) ) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs index 6b80e5f..7732a96 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComRuntimeHelpers.cs @@ -211,12 +211,6 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { return new DispCallable(dispatch, method.Name, method.DispId); } - - internal static MethodInfo GetGetIDispatchForObjectMethod() - { - // GetIDispatchForObject always throws a PNSE in .NET Core, so we work around it by using GetComInterfaceForObject with our IDispatch type. - return typeof(Marshal).GetMethods().Single(m => m.Name == nameof(Marshal.GetComInterfaceForObject) && m.GetParameters().Length == 1 && m.ContainsGenericParameters).MakeGenericMethod(typeof(object), typeof(IDispatch)); - } } /// diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs index 2d8d486..45e91dd 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs @@ -45,7 +45,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop Expression.Equal(parameter, Expression.Constant(null)), Expression.Constant(IntPtr.Zero), Expression.Call( - ComRuntimeHelpers.GetGetIDispatchForObjectMethod(), + typeof(Marshal).GetMethod(nameof(System.Runtime.InteropServices.Marshal.GetIDispatchForObject)), parameter ) ); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs index b310134..af94757 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.cs @@ -91,6 +91,11 @@ namespace System.Runtime.InteropServices return (IntPtr)(-1); } + public static IntPtr GetIDispatchForObject(object o) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); + } + public static IntPtr GetIUnknownForObject(object o) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index d7fe337..f7bbc8b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -913,8 +913,6 @@ namespace System.Runtime.InteropServices return (dwLastError & 0x0000FFFF) | unchecked((int)0x80070000); } - public static IntPtr /* IDispatch */ GetIDispatchForObject(object o) => throw new PlatformNotSupportedException(); - public static unsafe void ZeroFreeBSTR(IntPtr s) { if (s == IntPtr.Zero) diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index 7b692de..e57c30b 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -82,6 +82,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/DispatchWrapperTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/DispatchWrapperTests.cs index 9781110..4c2636d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/DispatchWrapperTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/DispatchWrapperTests.cs @@ -19,10 +19,20 @@ namespace System.Runtime.InteropServices.Tests [Theory] [InlineData("")] [InlineData(0)] + [PlatformSpecific(TestPlatforms.AnyUnix)] public void Ctor_NonNull_ThrowsPlatformNotSupportedException(object value) { Assert.Throws(() => new DispatchWrapper(value)); } + + [Theory] + [InlineData("")] + [InlineData(0)] + [PlatformSpecific(TestPlatforms.Windows)] + public void Ctor_NonDispatchObject_ThrowsInvalidCastException(object value) + { + Assert.Throws(() => new DispatchWrapper(value)); + } } #pragma warning restore 0618 } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.Windows.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.Windows.cs index 5109f03..ba3c0e5 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.Windows.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.Windows.cs @@ -10,7 +10,7 @@ namespace System.Runtime.InteropServices.Tests { public partial class GetIDispatchForObjectTests { - public static IEnumerable GetIUnknownForObject_ComObject_TestData() + public static IEnumerable GetIDispatchForObject_ComObject_TestData() { yield return new object[] { new ComImportObject() }; @@ -27,5 +27,20 @@ namespace System.Runtime.InteropServices.Tests yield return new object[] { new AutoDispatchComObjectEmpty() }; yield return new object[] { new AutoDualComObjectEmpty() }; } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [MemberData(nameof(GetIDispatchForObject_ComObject_TestData))] + public void GetIDispatchForObject_DispatchObject_Success(object obj) + { + IntPtr ptr = Marshal.GetIDispatchForObject(obj); + try + { + Assert.NotEqual(IntPtr.Zero, ptr); + } + finally + { + Marshal.Release(ptr); + } + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.cs index 792ea5b..1a564e3 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetIDispatchForObjectTests.cs @@ -13,9 +13,24 @@ namespace System.Runtime.InteropServices.Tests public partial class GetIDispatchForObjectTests { [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] public void GetIDispatchForObject_NetCore_ThrowsPlatformNotSupportedException() { Assert.Throws(() => Marshal.GetIDispatchForObject(null)); } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void GetIDispatchForObject_NullObject_ThrowsArgumentNullException() + { + AssertExtensions.Throws("o", () => Marshal.GetIDispatchForObject(null)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void GetIDispatchForObject_NonDispatchObject_ThrowsInvalidCastException() + { + Assert.Throws(() => Marshal.GetIDispatchForObject(string.Empty)); + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetNativeVariantForObjectTests.Windows.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetNativeVariantForObjectTests.Windows.cs index d96f13f..d2f4da6 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetNativeVariantForObjectTests.Windows.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetNativeVariantForObjectTests.Windows.cs @@ -80,21 +80,14 @@ namespace System.Runtime.InteropServices.Tests yield return new object[] { new UnknownWrapper(autoDispatch), autoDispatch, VarEnum.VT_UNKNOWN }; yield return new object[] { new UnknownWrapper(autoDual), autoDual, VarEnum.VT_UNKNOWN }; - if (!PlatformDetection.IsNetCore) - { - yield return new object[] { new DispatchWrapper(empty), empty, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(dual), dual, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(iUnknown), iUnknown, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(iDispatch), iDispatch, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(iInspectable), iInspectable, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(nonDual), nonDual, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(autoDispatch), autoDispatch, VarEnum.VT_DISPATCH }; - yield return new object[] { new DispatchWrapper(autoDual), autoDual, VarEnum.VT_DISPATCH }; - } - else - { - Assert.Throws(() => new DispatchWrapper(10)); - } + yield return new object[] { new DispatchWrapper(empty), empty, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(dual), dual, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(iUnknown), iUnknown, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(iDispatch), iDispatch, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(iInspectable), iInspectable, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(nonDual), nonDual, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(autoDispatch), autoDispatch, VarEnum.VT_DISPATCH }; + yield return new object[] { new DispatchWrapper(autoDual), autoDual, VarEnum.VT_DISPATCH }; } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.Windows.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.Windows.cs new file mode 100644 index 0000000..5d78faf --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.Windows.cs @@ -0,0 +1,594 @@ +// 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. + +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Xunit; + +namespace System.Runtime.InteropServices.Tests +{ + public partial class GetObjectForNativeVariantTests + { + public static IEnumerable GetObjectForNativeVariant_PrimitivesByRef_TestData() + { + // VT_NULL => null. + yield return new object[] + { + CreateVariant(VT_NULL, new UnionTypes { _byref = IntPtr.Zero }), + DBNull.Value + }; + + yield return new object[] + { + CreateVariant(VT_NULL, new UnionTypes { _byref = (IntPtr)10 }), + DBNull.Value + }; + + // VT_I2 => short. + yield return new object[] + { + CreateVariant(VT_I2, new UnionTypes { _i2 = 10 }), + (short)10 + }; + + yield return new object[] + { + CreateVariant(VT_I2, new UnionTypes { _i2 = 0 }), + (short)0 + }; + + yield return new object[] + { + CreateVariant(VT_I2, new UnionTypes { _i2 = -10 }), + (short)(-10) + }; + + // VT_I4 => int. + yield return new object[] + { + CreateVariant(VT_I4, new UnionTypes { _i4 = 10 }), + 10 + }; + + yield return new object[] + { + CreateVariant(VT_I4, new UnionTypes { _i4 = 0 }), + 0 + }; + + yield return new object[] + { + CreateVariant(VT_I4, new UnionTypes { _i4 = -10 }), + -10 + }; + + // VT_R4 => float. + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = 10 }), + (float)10 + }; + + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = 0 }), + (float)0 + }; + + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = -10 }), + (float)(-10) + }; + + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = float.PositiveInfinity }), + float.PositiveInfinity + }; + + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = float.NegativeInfinity }), + float.NegativeInfinity + }; + + yield return new object[] + { + CreateVariant(VT_R4, new UnionTypes { _r4 = float.NaN }), + float.NaN + }; + + // VT_R8 => double. + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = 10 }), + (double)10 + }; + + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = 0 }), + (double)0 + }; + + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = -10 }), + (double)(-10) + }; + + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = double.PositiveInfinity }), + double.PositiveInfinity + }; + + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = double.NegativeInfinity }), + double.NegativeInfinity + }; + + yield return new object[] + { + CreateVariant(VT_R8, new UnionTypes { _r8 = double.NaN }), + double.NaN + }; + + // VT_CY => decimal. + yield return new object[] + { + CreateVariant(VT_CY, new UnionTypes { _cy = 200 }), + 0.02m + }; + + yield return new object[] + { + CreateVariant(VT_CY, new UnionTypes { _cy = 0 }), + 0m + }; + + yield return new object[] + { + CreateVariant(VT_CY, new UnionTypes { _cy = -200 }), + -0.02m + }; + + // VT_DATE => DateTime. + DateTime maxDate = DateTime.MaxValue; + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = maxDate.ToOADate() }), + new DateTime(9999, 12, 31, 23, 59, 59, 999) + }; + + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = 200 }), + new DateTime(1900, 07, 18) + }; + + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = 0.5 }), + new DateTime(1899, 12, 30, 12, 0, 0) + }; + + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = 0 }), + new DateTime(1899, 12, 30) + }; + + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = -0.5 }), + new DateTime(1899, 12, 30, 12, 0, 0) + }; + + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = -200 }), + new DateTime(1899, 06, 13) + }; + + DateTime minDate = new DateTime(100, 01, 01, 23, 59, 59, 999); + yield return new object[] + { + CreateVariant(VT_DATE, new UnionTypes { _date = minDate.ToOADate() }), + minDate + }; + + // VT_BSTR => string. + yield return new object[] + { + CreateVariant(VT_BSTR, new UnionTypes { _bstr = IntPtr.Zero }), + null + }; + + IntPtr emptyString = Marshal.StringToBSTR(""); + yield return new object[] + { + CreateVariant(VT_BSTR, new UnionTypes { _bstr = emptyString }), + "" + }; + + IntPtr oneLetterString = Marshal.StringToBSTR("a"); + yield return new object[] + { + CreateVariant(VT_BSTR, new UnionTypes { _bstr = oneLetterString }), + "a" + }; + + IntPtr twoLetterString = Marshal.StringToBSTR("ab"); + yield return new object[] + { + CreateVariant(VT_BSTR, new UnionTypes { _bstr = twoLetterString }), + "ab" + }; + + IntPtr embeddedNullString = Marshal.StringToBSTR("a\0c"); + yield return new object[] + { + CreateVariant(VT_BSTR, new UnionTypes { _bstr = embeddedNullString }), + "a\0c" + }; + + // VT_DISPATCH => object. + yield return new object[] + { + CreateVariant(VT_DISPATCH, new UnionTypes { _dispatch = IntPtr.Zero }), + null + }; + + var obj = new Common.IDispatchComObject(); + if (!PlatformDetection.IsNetCore || PlatformDetection.IsWindows) + { + IntPtr dispatch = Marshal.GetIDispatchForObject(obj); + yield return new object[] + { + CreateVariant(VT_DISPATCH, new UnionTypes { _dispatch = dispatch }), + obj + }; + } + else + { + Assert.Throws(() => Marshal.GetIDispatchForObject(obj)); + } + + // VT_ERROR => int. + yield return new object[] + { + CreateVariant(VT_ERROR, new UnionTypes { _error = int.MaxValue }), + int.MaxValue + }; + + yield return new object[] + { + CreateVariant(VT_ERROR, new UnionTypes { _error = 0 }), + 0 + }; + + yield return new object[] + { + CreateVariant(VT_ERROR, new UnionTypes { _error = int.MinValue }), + int.MinValue + }; + + // VT_BOOL => bool. + yield return new object[] + { + CreateVariant(VT_BOOL, new UnionTypes { _i1 = 1 }), + true + }; + + yield return new object[] + { + CreateVariant(VT_BOOL, new UnionTypes { _i1 = 0 }), + false + }; + + yield return new object[] + { + CreateVariant(VT_BOOL, new UnionTypes { _i1 = -1 }), + true + }; + + // VT_UNKNOWN => object. + yield return new object[] + { + CreateVariant(VT_UNKNOWN, new UnionTypes { _unknown = IntPtr.Zero }), + null + }; + + IntPtr unknown = Marshal.GetIUnknownForObject(obj); + yield return new object[] + { + CreateVariant(VT_UNKNOWN, new UnionTypes { _unknown = unknown }), + obj + }; + + // VT_I1 => sbyte. + yield return new object[] + { + CreateVariant(VT_I1, new UnionTypes { _i1 = 10 }), + (sbyte)10 + }; + + yield return new object[] + { + CreateVariant(VT_I1, new UnionTypes { _i1 = 0 }), + (sbyte)0 + }; + + yield return new object[] + { + CreateVariant(VT_I1, new UnionTypes { _i1 = -10 }), + (sbyte)(-10) + }; + + // VT_UI1 => byte. + yield return new object[] + { + CreateVariant(VT_UI1, new UnionTypes { _ui1 = 10 }), + (byte)10 + }; + + yield return new object[] + { + CreateVariant(VT_UI1, new UnionTypes { _ui1 = 0 }), + (byte)0 + }; + + // VT_UI2 => ushort. + yield return new object[] + { + CreateVariant(VT_UI2, new UnionTypes { _ui2 = 10 }), + (ushort)10 + }; + + yield return new object[] + { + CreateVariant(VT_UI2, new UnionTypes { _ui2 = 0 }), + (ushort)0 + }; + + // VT_UI4 => uint. + yield return new object[] + { + CreateVariant(VT_UI4, new UnionTypes { _ui4 = 10 }), + (uint)10 + }; + + yield return new object[] + { + CreateVariant(VT_UI4, new UnionTypes { _ui4 = 0 }), + (uint)0 + }; + + // VT_I8 => long. + yield return new object[] + { + CreateVariant(VT_I8, new UnionTypes { _i8 = 10 }), + (long)10 + }; + + yield return new object[] + { + CreateVariant(VT_I8, new UnionTypes { _i8 = 0 }), + (long)0 + }; + + yield return new object[] + { + CreateVariant(VT_I8, new UnionTypes { _i8 = -10 }), + (long)(-10) + }; + + // VT_UI8 => ulong. + yield return new object[] + { + CreateVariant(VT_UI8, new UnionTypes { _ui8 = 10 }), + (ulong)10 + }; + + yield return new object[] + { + CreateVariant(VT_UI8, new UnionTypes { _ui8 = 0 }), + (ulong)0 + }; + + // VT_INT => int. + yield return new object[] + { + CreateVariant(VT_INT, new UnionTypes { _int = 10 }), + 10 + }; + + yield return new object[] + { + CreateVariant(VT_INT, new UnionTypes { _int = 0 }), + 0 + }; + + yield return new object[] + { + CreateVariant(VT_INT, new UnionTypes { _int = -10 }), + -10 + }; + + // VT_UINT => uint. + yield return new object[] + { + CreateVariant(VT_UINT, new UnionTypes { _uint = 10 }), + (uint)10 + }; + + yield return new object[] + { + CreateVariant(VT_UINT, new UnionTypes { _uint = 0 }), + (uint)0 + }; + + // VT_VOID => null. + yield return new object[] + { + CreateVariant(VT_VOID, new UnionTypes()), + null + }; + } + + public static IEnumerable GetObjectForNativeVariant_TestData() + { + // VT_EMPTY => null. + yield return new object[] + { + CreateVariant(VT_EMPTY, new UnionTypes { _byref = IntPtr.Zero }), + null + }; + + yield return new object[] + { + CreateVariant(VT_EMPTY, new UnionTypes { _byref = (IntPtr)10 }), + null + }; + + // VT_EMPTY | VT_BYREF => zero. + object expectedZero; + if (IntPtr.Size == 8) + { + expectedZero = (ulong)0; + } + else + { + expectedZero = (uint)0; + } + yield return new object[] + { + CreateVariant(VT_EMPTY | VT_BYREF, new UnionTypes { _byref = IntPtr.Zero }), + expectedZero + }; + + object expectedTen; + if (IntPtr.Size == 8) + { + expectedTen = (ulong)10; + } + else + { + expectedTen = (uint)10; + } + yield return new object[] + { + CreateVariant(VT_EMPTY | VT_BYREF, new UnionTypes { _byref = (IntPtr)10 }), + expectedTen + }; + + // VT_RECORD. + yield return new object[] + { + CreateVariant(VT_RECORD, new UnionTypes { _record = new Record { _record = IntPtr.Zero, _recordInfo = (IntPtr)1 } }), + null + }; + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] + [MemberData(nameof(GetObjectForNativeVariant_TestData))] + public void GetObjectForNativeVariant_Normal_ReturnsExpected(Variant variant, object expected) + { + try + { + Assert.Equal(expected, GetObjectForNativeVariant(variant)); + } + finally + { + DeleteVariant(variant); + } + } + + [Fact] + public void GetObjectForNativeVariant_ErrorMissing_ReturnsTypeMissing() + { + // This cannot be in the [MemberData] as XUnit uses reflection to invoke the test method + // and Type.Missing is handled specially by the runtime. + GetObjectForNativeVariant_Normal_ReturnsExpected(CreateVariant(VT_ERROR, new UnionTypes { _error = unchecked((int)0x80020004) }), Type.Missing); + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] + public void GetObjectForNativeVariant_NestedVariant_ReturnsExpected(Variant source, object expected) + { + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); + try + { + Marshal.StructureToPtr(source, ptr, fDeleteOld: false); + + Variant variant = CreateVariant(VT_VARIANT | VT_BYREF, new UnionTypes { _pvarVal = ptr }); + Assert.Equal(expected, GetObjectForNativeVariant(variant)); + } + finally + { + DeleteVariant(source); + Marshal.DestroyStructure(ptr); + Marshal.FreeHGlobal(ptr); + } + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/27015", TargetFrameworkMonikers.Netcoreapp)] + public void GetObjectForNativeVariant_Record_ReturnsExpected() + { + int record = 10; + var recordInfo = new RecordInfo { Guid = typeof(int).GUID }; + IntPtr pRecord = Marshal.AllocHGlobal(Marshal.SizeOf()); + IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); + try + { + Marshal.StructureToPtr(record, pRecord, fDeleteOld: false); + + Variant variant = CreateVariant(VT_RECORD, new UnionTypes + { + _record = new Record + { + _record = pRecord, + _recordInfo = pRecordInfo + } + }); + Assert.Equal(10, GetObjectForNativeVariant(variant)); + GetObjectForNativeVariant_NestedVariant_ReturnsExpected(variant, record); + + variant.m_Variant.vt |= VT_BYREF; + Assert.Equal(10, GetObjectForNativeVariant(variant)); + } + finally + { + Marshal.DestroyStructure(pRecord); + Marshal.FreeHGlobal(pRecord); + Marshal.Release(pRecordInfo); + } + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] + [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] + public unsafe void GetObjectForNativeVariant_ByRef_ReturnsExpected(Variant source, object value) + { + try + { + IntPtr ptr = new IntPtr(&source.m_Variant._unionTypes); + + var variant = new Variant(); + variant.m_Variant.vt = (ushort)(source.m_Variant.vt | VT_BYREF); + variant.m_Variant._unionTypes._byref = ptr; + + Assert.Equal(value, GetObjectForNativeVariant(variant)); + } + finally + { + DeleteVariant(source); + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs index e4cfc53..aa5df1c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/GetObjectForNativeVariantTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace System.Runtime.InteropServices.Tests { - public class GetObjectForNativeVariantTests + public partial class GetObjectForNativeVariantTests { [StructLayout(LayoutKind.Sequential)] public struct Record @@ -119,516 +119,6 @@ namespace System.Runtime.InteropServices.Tests public const ushort VT_ILLEGALMASKED = 0xfff; public const ushort VT_TYPEMASK = 0xfff; - public static IEnumerable GetObjectForNativeVariant_PrimitivesByRef_TestData() - { - // VT_NULL => null. - yield return new object[] - { - CreateVariant(VT_NULL, new UnionTypes { _byref = IntPtr.Zero }), - DBNull.Value - }; - - yield return new object[] - { - CreateVariant(VT_NULL, new UnionTypes { _byref = (IntPtr)10 }), - DBNull.Value - }; - - - // VT_I2 => short. - yield return new object[] - { - CreateVariant(VT_I2, new UnionTypes { _i2 = 10 }), - (short)10 - }; - - yield return new object[] - { - CreateVariant(VT_I2, new UnionTypes { _i2 = 0 }), - (short)0 - }; - - yield return new object[] - { - CreateVariant(VT_I2, new UnionTypes { _i2 = -10 }), - (short)(-10) - }; - - // VT_I4 => int. - yield return new object[] - { - CreateVariant(VT_I4, new UnionTypes { _i4 = 10 }), - 10 - }; - - yield return new object[] - { - CreateVariant(VT_I4, new UnionTypes { _i4 = 0 }), - 0 - }; - - yield return new object[] - { - CreateVariant(VT_I4, new UnionTypes { _i4 = -10 }), - -10 - }; - - // VT_R4 => float. - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = 10 }), - (float)10 - }; - - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = 0 }), - (float)0 - }; - - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = -10 }), - (float)(-10) - }; - - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = float.PositiveInfinity }), - float.PositiveInfinity - }; - - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = float.NegativeInfinity }), - float.NegativeInfinity - }; - - yield return new object[] - { - CreateVariant(VT_R4, new UnionTypes { _r4 = float.NaN }), - float.NaN - }; - - // VT_R8 => double. - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = 10 }), - (double)10 - }; - - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = 0 }), - (double)0 - }; - - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = -10 }), - (double)(-10) - }; - - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = double.PositiveInfinity }), - double.PositiveInfinity - }; - - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = double.NegativeInfinity }), - double.NegativeInfinity - }; - - yield return new object[] - { - CreateVariant(VT_R8, new UnionTypes { _r8 = double.NaN }), - double.NaN - }; - - // VT_CY => decimal. - yield return new object[] - { - CreateVariant(VT_CY, new UnionTypes { _cy = 200 }), - 0.02m - }; - - yield return new object[] - { - CreateVariant(VT_CY, new UnionTypes { _cy = 0 }), - 0m - }; - - yield return new object[] - { - CreateVariant(VT_CY, new UnionTypes { _cy = -200 }), - -0.02m - }; - - // VT_DATE => DateTime. - DateTime maxDate = DateTime.MaxValue; - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = maxDate.ToOADate() }), - new DateTime(9999, 12, 31, 23, 59, 59, 999) - }; - - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = 200 }), - new DateTime(1900, 07, 18) - }; - - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = 0.5 }), - new DateTime(1899, 12, 30, 12, 0, 0) - }; - - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = 0 }), - new DateTime(1899, 12, 30) - }; - - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = -0.5 }), - new DateTime(1899, 12, 30, 12, 0, 0) - }; - - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = -200 }), - new DateTime(1899, 06, 13) - }; - - DateTime minDate = new DateTime(100, 01, 01, 23, 59, 59, 999); - yield return new object[] - { - CreateVariant(VT_DATE, new UnionTypes { _date = minDate.ToOADate() }), - minDate - }; - - // VT_BSTR => string. - yield return new object[] - { - CreateVariant(VT_BSTR, new UnionTypes { _bstr = IntPtr.Zero }), - null - }; - - IntPtr emptyString = Marshal.StringToBSTR(""); - yield return new object[] - { - CreateVariant(VT_BSTR, new UnionTypes { _bstr = emptyString }), - "" - }; - - IntPtr oneLetterString = Marshal.StringToBSTR("a"); - yield return new object[] - { - CreateVariant(VT_BSTR, new UnionTypes { _bstr = oneLetterString }), - "a" - }; - - IntPtr twoLetterString = Marshal.StringToBSTR("ab"); - yield return new object[] - { - CreateVariant(VT_BSTR, new UnionTypes { _bstr = twoLetterString }), - "ab" - }; - - IntPtr embeddedNullString = Marshal.StringToBSTR("a\0c"); - yield return new object[] - { - CreateVariant(VT_BSTR, new UnionTypes { _bstr = embeddedNullString }), - "a\0c" - }; - - // VT_DISPATCH => object. - yield return new object[] - { - CreateVariant(VT_DISPATCH, new UnionTypes { _dispatch = IntPtr.Zero }), - null - }; - - var obj = new object(); - if (!PlatformDetection.IsNetCore) - { - IntPtr dispatch = Marshal.GetIDispatchForObject(obj); - yield return new object[] - { - CreateVariant(VT_DISPATCH, new UnionTypes { _dispatch = dispatch }), - obj - }; - } - else - { - Assert.Throws(() => Marshal.GetIDispatchForObject(obj)); - } - - // VT_ERROR => int. - yield return new object[] - { - CreateVariant(VT_ERROR, new UnionTypes { _error = int.MaxValue }), - int.MaxValue - }; - - yield return new object[] - { - CreateVariant(VT_ERROR, new UnionTypes { _error = 0 }), - 0 - }; - - yield return new object[] - { - CreateVariant(VT_ERROR, new UnionTypes { _error = int.MinValue }), - int.MinValue - }; - - // VT_BOOL => bool. - yield return new object[] - { - CreateVariant(VT_BOOL, new UnionTypes { _i1 = 1 }), - true - }; - - yield return new object[] - { - CreateVariant(VT_BOOL, new UnionTypes { _i1 = 0 }), - false - }; - - yield return new object[] - { - CreateVariant(VT_BOOL, new UnionTypes { _i1 = -1 }), - true - }; - - // VT_UNKNOWN => object. - yield return new object[] - { - CreateVariant(VT_UNKNOWN, new UnionTypes { _unknown = IntPtr.Zero }), - null - }; - - IntPtr unknown = Marshal.GetIUnknownForObject(obj); - yield return new object[] - { - CreateVariant(VT_UNKNOWN, new UnionTypes { _unknown = unknown }), - obj - }; - - // VT_I1 => sbyte. - yield return new object[] - { - CreateVariant(VT_I1, new UnionTypes { _i1 = 10 }), - (sbyte)10 - }; - - yield return new object[] - { - CreateVariant(VT_I1, new UnionTypes { _i1 = 0 }), - (sbyte)0 - }; - - yield return new object[] - { - CreateVariant(VT_I1, new UnionTypes { _i1 = -10 }), - (sbyte)(-10) - }; - - // VT_UI1 => byte. - yield return new object[] - { - CreateVariant(VT_UI1, new UnionTypes { _ui1 = 10 }), - (byte)10 - }; - - yield return new object[] - { - CreateVariant(VT_UI1, new UnionTypes { _ui1 = 0 }), - (byte)0 - }; - - // VT_UI2 => ushort. - yield return new object[] - { - CreateVariant(VT_UI2, new UnionTypes { _ui2 = 10 }), - (ushort)10 - }; - - yield return new object[] - { - CreateVariant(VT_UI2, new UnionTypes { _ui2 = 0 }), - (ushort)0 - }; - - // VT_UI4 => uint. - yield return new object[] - { - CreateVariant(VT_UI4, new UnionTypes { _ui4 = 10 }), - (uint)10 - }; - - yield return new object[] - { - CreateVariant(VT_UI4, new UnionTypes { _ui4 = 0 }), - (uint)0 - }; - - // VT_I8 => long. - yield return new object[] - { - CreateVariant(VT_I8, new UnionTypes { _i8 = 10 }), - (long)10 - }; - - yield return new object[] - { - CreateVariant(VT_I8, new UnionTypes { _i8 = 0 }), - (long)0 - }; - - yield return new object[] - { - CreateVariant(VT_I8, new UnionTypes { _i8 = -10 }), - (long)(-10) - }; - - // VT_UI8 => ulong. - yield return new object[] - { - CreateVariant(VT_UI8, new UnionTypes { _ui8 = 10 }), - (ulong)10 - }; - - yield return new object[] - { - CreateVariant(VT_UI8, new UnionTypes { _ui8 = 0 }), - (ulong)0 - }; - - // VT_INT => int. - yield return new object[] - { - CreateVariant(VT_INT, new UnionTypes { _int = 10 }), - 10 - }; - - yield return new object[] - { - CreateVariant(VT_INT, new UnionTypes { _int = 0 }), - 0 - }; - - yield return new object[] - { - CreateVariant(VT_INT, new UnionTypes { _int = -10 }), - -10 - }; - - // VT_UINT => uint. - yield return new object[] - { - CreateVariant(VT_UINT, new UnionTypes { _uint = 10 }), - (uint)10 - }; - - yield return new object[] - { - CreateVariant(VT_UINT, new UnionTypes { _uint = 0 }), - (uint)0 - }; - - // VT_VOID => null. - yield return new object[] - { - CreateVariant(VT_VOID, new UnionTypes()), - null - }; - } - - public static IEnumerable GetObjectForNativeVariant_TestData() - { - // VT_EMPTY => null. - yield return new object[] - { - CreateVariant(VT_EMPTY, new UnionTypes { _byref = IntPtr.Zero }), - null - }; - - yield return new object[] - { - CreateVariant(VT_EMPTY, new UnionTypes { _byref = (IntPtr)10 }), - null - }; - - // VT_EMPTY | VT_BYREF => zero. - object expectedZero; - if (IntPtr.Size == 8) - { - expectedZero = (ulong)0; - } - else - { - expectedZero = (uint)0; - } - yield return new object[] - { - CreateVariant(VT_EMPTY | VT_BYREF, new UnionTypes { _byref = IntPtr.Zero }), - expectedZero - }; - - object expectedTen; - if (IntPtr.Size == 8) - { - expectedTen = (ulong)10; - } - else - { - expectedTen = (uint)10; - } - yield return new object[] - { - CreateVariant(VT_EMPTY | VT_BYREF, new UnionTypes { _byref = (IntPtr)10 }), - expectedTen - }; - - // VT_RECORD. - yield return new object[] - { - CreateVariant(VT_RECORD, new UnionTypes { _record = new Record { _record = IntPtr.Zero, _recordInfo = (IntPtr)1 } }), - null - }; - } - - [Theory] - [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] - [MemberData(nameof(GetObjectForNativeVariant_TestData))] - [PlatformSpecific(TestPlatforms.Windows)] - public void GetObjectForNativeVariant_Normal_ReturnsExpected(Variant variant, object expected) - { - try - { - Assert.Equal(expected, GetObjectForNativeVariant(variant)); - } - finally - { - DeleteVariant(variant); - } - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public void GetObjectForNativeVariant_ErrorMissing_ReturnsTypeMissing() - { - // This cannot be in the [MemberData] as XUnit uses reflection to invoke the test method - // and Type.Missing is handled specially by the runtime. - GetObjectForNativeVariant_Normal_ReturnsExpected(CreateVariant(VT_ERROR, new UnionTypes { _error = unchecked((int)0x80020004) }), Type.Missing); - } - public static IEnumerable GetObjectForNativeVariant_Decimal_TestData() { // VT_DECIMAL => decimal. @@ -650,83 +140,6 @@ namespace System.Runtime.InteropServices.Tests } [Theory] - [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] - [PlatformSpecific(TestPlatforms.Windows)] - public void GetObjectForNativeVariant_NestedVariant_ReturnsExpected(Variant source, object expected) - { - IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); - try - { - Marshal.StructureToPtr(source, ptr, fDeleteOld: false); - - Variant variant = CreateVariant(VT_VARIANT | VT_BYREF, new UnionTypes { _pvarVal = ptr }); - Assert.Equal(expected, GetObjectForNativeVariant(variant)); - } - finally - { - DeleteVariant(source); - Marshal.DestroyStructure(ptr); - Marshal.FreeHGlobal(ptr); - } - } - - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/27015", TargetFrameworkMonikers.Netcoreapp)] - public void GetObjectForNativeVariant_Record_ReturnsExpected() - { - int record = 10; - var recordInfo = new RecordInfo { Guid = typeof(int).GUID }; - IntPtr pRecord = Marshal.AllocHGlobal(Marshal.SizeOf()); - IntPtr pRecordInfo = Marshal.GetComInterfaceForObject(recordInfo); - try - { - Marshal.StructureToPtr(record, pRecord, fDeleteOld: false); - - Variant variant = CreateVariant(VT_RECORD, new UnionTypes - { - _record = new Record - { - _record = pRecord, - _recordInfo = pRecordInfo - } - }); - Assert.Equal(10, GetObjectForNativeVariant(variant)); - GetObjectForNativeVariant_NestedVariant_ReturnsExpected(variant, record); - - variant.m_Variant.vt |= VT_BYREF; - Assert.Equal(10, GetObjectForNativeVariant(variant)); - } - finally - { - Marshal.DestroyStructure(pRecord); - Marshal.FreeHGlobal(pRecord); - Marshal.Release(pRecordInfo); - } - } - - [Theory] - [MemberData(nameof(GetObjectForNativeVariant_PrimitivesByRef_TestData))] - [PlatformSpecific(TestPlatforms.Windows)] - public unsafe void GetObjectForNativeVariant_ByRef_ReturnsExpected(Variant source, object value) - { - try - { - IntPtr ptr = new IntPtr(&source.m_Variant._unionTypes); - - var variant = new Variant(); - variant.m_Variant.vt = (ushort)(source.m_Variant.vt | VT_BYREF); - variant.m_Variant._unionTypes._byref = ptr; - - Assert.Equal(value, GetObjectForNativeVariant(variant)); - } - finally - { - DeleteVariant(source); - } - } - - [Theory] [MemberData(nameof(GetObjectForNativeVariant_Decimal_TestData))] [PlatformSpecific(TestPlatforms.Windows)] public unsafe void GetObjectForNativeVariant_DecimalByRef_Success(decimal d) -- 2.7.4