From ecbd80742a887379f65054e509b4c444614febce Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Feb 2022 14:14:31 +0600 Subject: [PATCH 01/28] Use ComWrappers for IErrorInfo --- .../src/Interop/Interop.IID.cs | 7 +++- .../Interop/OleAut32/Interop.GetErrorInfo.cs | 12 +++++- .../Interop/OleAut32/Interop.IErrorInfo.cs | 37 ----------------- .../WinFormsComWrappers.ErrorInfoWrapper.cs | 41 +++++++++++++++++++ .../src/Interop/WinFormsComWrappers.cs | 8 ++++ .../COM2Interop/COM2PropertyDescriptor.cs | 6 +-- 6 files changed, 68 insertions(+), 43 deletions(-) delete mode 100644 src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IErrorInfo.cs create mode 100644 src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Interop.IID.cs b/src/System.Windows.Forms.Primitives/src/Interop/Interop.IID.cs index 173dd9c5472..e9ca1e73232 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/Interop.IID.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/Interop.IID.cs @@ -15,11 +15,14 @@ internal static class IID // 00000121-0000-0000-C000-000000000046 public static Guid IDropSource { get; } = new(0x00000121, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + // 00000122-0000-0000-C000-000000000046 + public static Guid IDropTarget { get; } = new(0x00000122, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + // 00000101-0000-0000-C000-000000000046 public static Guid IEnumString { get; } = new(0x00000101, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); - // 00000122-0000-0000-C000-000000000046 - public static Guid IDropTarget { get; } = new(0x00000122, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + // 1CF2B120-547D-101B-8E65-08002B2BD119 + public static Guid IErrorInfo { get; } = new(0x1CF2B120, 0x547D, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); // E6FDD21A-163F-4975-9C8C-A69F1BA37034 internal static Guid IFileDialogCustomize { get; } = new(0xE6FDD21A, 0x163F, 0x4975, 0x9C, 0x8C, 0xA6, 0x9F, 0x1B, 0xA3, 0x70, 0x34); diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs index 00b2f3f69b5..c5098cce153 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs @@ -9,6 +9,16 @@ internal partial class Interop internal static partial class Oleaut32 { [DllImport(Libraries.Oleaut32, ExactSpelling = true)] - public static extern HRESULT GetErrorInfo(uint dwReserved, out IErrorInfo pperrinfo); + public static extern HRESULT GetErrorInfo(uint dwReserved, out IntPtr pperrinfo); + + public static void GetErrorInfo(out WinFormsComWrappers.ErrorInfoWrapper? errinfo) + { + HRESULT result = GetErrorInfo(0, out IntPtr pperrinfo); + errinfo = null; + if (result.Succeeded()) + { + errinfo = (WinFormsComWrappers.ErrorInfoWrapper)WinFormsComWrappers.Instance.GetOrCreateObjectForComInstance(pperrinfo, CreateObjectFlags.Unwrap); + } + } } } diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IErrorInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IErrorInfo.cs deleted file mode 100644 index 273282b5a5e..00000000000 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.IErrorInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -// 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.Runtime.InteropServices; - -internal partial class Interop -{ - internal static partial class Oleaut32 - { - [ComImport] - [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public unsafe interface IErrorInfo - { - [PreserveSig] - HRESULT GetGUID( - Guid* pguid); - - [PreserveSig] - HRESULT GetSource( - out string pBstrSource); - - [PreserveSig] - HRESULT GetDescription( - out string pBstrDescription); - - [PreserveSig] - HRESULT GetHelpFile( - out string pBstrHelpFile); - - [PreserveSig] - HRESULT GetHelpContext( - uint* pdwHelpContext); - } - } -} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs new file mode 100644 index 00000000000..e8685485f5f --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs @@ -0,0 +1,41 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +internal partial class Interop +{ + internal unsafe partial class WinFormsComWrappers + { + internal class ErrorInfoWrapper + { + private IntPtr _wrappedInstance; + + public ErrorInfoWrapper(IntPtr wrappedInstance) + { + _wrappedInstance = wrappedInstance.OrThrowIfZero(); + } + + internal IntPtr Instance => _wrappedInstance; + + public void Dispose() + { + Marshal.Release(_wrappedInstance); + _wrappedInstance = IntPtr.Zero; + } + + public bool GetDescription([NotNullWhen(true)] out string? pBstrDescription) + { + IntPtr descriptionPtr; + var result = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription */))) + (_wrappedInstance, &descriptionPtr); + pBstrDescription = Marshal.PtrToStringUni(descriptionPtr); + Marshal.FreeCoTaskMem(descriptionPtr); + return result.Succeeded(); + } + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.cs b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.cs index a82eecd98e8..6254bab2641 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.cs @@ -135,6 +135,14 @@ protected override object CreateObject(IntPtr externalComObject, CreateObjectFla return new PictureWrapper(pictureComObject); } + Guid errorInfoIID = IID.IErrorInfo; + hr = Marshal.QueryInterface(externalComObject, ref errorInfoIID, out IntPtr errorInfoComObject); + if (hr == S_OK) + { + Marshal.Release(externalComObject); + return new ErrorInfoWrapper(errorInfoComObject); + } + Guid fileOpenDialogIID = IID.IFileOpenDialog; hr = Marshal.QueryInterface(externalComObject, ref fileOpenDialogIID, out IntPtr fileOpenDialogComObject); if (hr == S_OK) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs index 4934acbe739..6f0ce414e6f 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs @@ -1306,11 +1306,11 @@ public unsafe override void SetValue(object component, object value) g = typeof(Oleaut32.IDispatch).GUID; if (iSupportErrorInfo.InterfaceSupportsErrorInfo(&g) == HRESULT.S_OK) { - Oleaut32.IErrorInfo pErrorInfo; - Oleaut32.GetErrorInfo(0, out pErrorInfo); + WinFormsComWrappers.ErrorInfoWrapper pErrorInfo; + Oleaut32.GetErrorInfo(out pErrorInfo); string info; - if (pErrorInfo is not null && pErrorInfo.GetDescription(out info).Succeeded()) + if (pErrorInfo is not null && pErrorInfo.GetDescription(out info)) { errorInfo = info; } From 43858e9023f35320a1aba609bc080f3d8ea0e5f9 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Feb 2022 14:41:41 +0600 Subject: [PATCH 02/28] Cleanup RCW instance after use --- .../COM2Interop/COM2PropertyDescriptor.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs index 6f0ce414e6f..d4ad79de63d 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs @@ -1309,10 +1309,15 @@ public unsafe override void SetValue(object component, object value) WinFormsComWrappers.ErrorInfoWrapper pErrorInfo; Oleaut32.GetErrorInfo(out pErrorInfo); - string info; - if (pErrorInfo is not null && pErrorInfo.GetDescription(out info)) + if (pErrorInfo is not null) { - errorInfo = info; + string info; + if (pErrorInfo.GetDescription(out info)) + { + errorInfo = info; + } + + pErrorInfo.Dispose(); } } } From 56938434243ba8ce30f72702301096a83ad19b29 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Feb 2022 15:49:01 +0600 Subject: [PATCH 03/28] Oops --- .../Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs index d4ad79de63d..dc43ed0ba6b 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ComponentModel/COM2Interop/COM2PropertyDescriptor.cs @@ -1316,7 +1316,7 @@ public unsafe override void SetValue(object component, object value) { errorInfo = info; } - + pErrorInfo.Dispose(); } } From 04a9f21140a6b3ae31e1b44647742ac70d99e101 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Fri, 25 Feb 2022 17:17:02 +0600 Subject: [PATCH 04/28] Fix issue --- .../src/Interop/OleAut32/Interop.GetErrorInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs index c5098cce153..57648f05da2 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs @@ -15,7 +15,7 @@ public static void GetErrorInfo(out WinFormsComWrappers.ErrorInfoWrapper? errinf { HRESULT result = GetErrorInfo(0, out IntPtr pperrinfo); errinfo = null; - if (result.Succeeded()) + if (result.Succeeded() && pperrinfo != IntPtr.Zero) { errinfo = (WinFormsComWrappers.ErrorInfoWrapper)WinFormsComWrappers.Instance.GetOrCreateObjectForComInstance(pperrinfo, CreateObjectFlags.Unwrap); } From 2e247a9d56ffdc651987efbb90f209818e902a4a Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Fri, 11 Mar 2022 19:50:19 +0600 Subject: [PATCH 05/28] Add test case for IErrorInfo handling Rely on the MXXMLWriter60 ProgID which implement IDispath and error handling --- .../PropertyGrid.IErrorInfoSupportTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs new file mode 100644 index 00000000000..acafa8b9abd --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Windows.Forms.Tests +{ + public class PropertyGrid_IErrorInfoSupportTests : IClassFixture + { + public const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); + + [WinFormsFact] + public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() + { + using PropertyGrid propertyGrid = new PropertyGrid(); + propertyGrid.SelectedObject = CreateComObjectWithIErrorInfo(); + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "encoding"); + try + { + encodingEntry.SetPropertyTextValue("nonexisting"); + Assert.False(true); + } + catch (ExternalException ex) + { + Assert.Equal(DISP_E_MEMBERNOTFOUND, ex.HResult); + } + } + + private object CreateComObjectWithIErrorInfo() + { + var CLSID_MXXMLWriter60 = new Guid("88d96a0f-f192-11d4-a65f-0040963251e5"); + var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + CoCreateInstance(ref CLSID_MXXMLWriter60, + IntPtr.Zero, + 1, + ref IID_IUnknown, + out object result); + return result; + } + + [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)] + private static extern void CoCreateInstance( + ref Guid rclsid, + IntPtr punkOuter, + int dwClsContext, + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppv); + } +} From 4d63a348c4b55b252295eebc3e2410fa7383524f Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Sun, 13 Mar 2022 14:48:42 +0600 Subject: [PATCH 06/28] Add second test which cover actual wrapper --- Winforms.sln | 16 +- .../WinFormsComWrappers.ErrorInfoWrapper.cs | 2 +- .../System.Windows.Forms.Tests.csproj | 13 + .../PropertyGrid.IErrorInfoSupportTests.cs | 58 ++- .../WindowsFormsTests/CMakeLists.txt | 36 ++ .../UnitTests/WindowsFormsTests/ComHelpers.h | 411 ++++++++++++++++++ .../UnitTests/WindowsFormsTests/Contract.idl | 38 ++ .../WindowsFormsTests/DispatchImpl.cpp | 58 +++ .../WindowsFormsTests/DispatchImpl.h | 43 ++ .../WindowsFormsTests/IErrorInfoObject.cpp | 27 ++ .../RawErrorInfoUsageTest.cpp | 47 ++ .../WindowsFormsTests/RawErrorInfoUsageTest.h | 53 +++ .../StandardErrorInfoUsageTest.cpp | 43 ++ .../StandardErrorInfoUsageTest.h | 53 +++ ...ystem_Windows_Forms_NativeTests.X.manifest | 16 + .../WindowsFormsTests/WindowsFormsTests.proj | 5 + 16 files changed, 890 insertions(+), 29 deletions(-) create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj diff --git a/Winforms.sln b/Winforms.sln index 11a1957c16b..1a317a880a2 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -193,14 +193,14 @@ Global {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x64.Build.0 = Release|Any CPU {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.ActiveCfg = Release|Any CPU {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.Build.0 = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|Any CPU {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|Any CPU diff --git a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs index e8685485f5f..44d00ca5b50 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs @@ -33,7 +33,7 @@ public bool GetDescription([NotNullWhen(true)] out string? pBstrDescription) var result = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription */))) (_wrappedInstance, &descriptionPtr); pBstrDescription = Marshal.PtrToStringUni(descriptionPtr); - Marshal.FreeCoTaskMem(descriptionPtr); + Marshal.FreeBSTR(descriptionPtr); return result.Succeeded(); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index a879f7c77fe..af3f4d14be3 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -2,6 +2,7 @@ System.Windows.Forms.Tests + x86;x64 true @@ -9,6 +10,8 @@ $(NoWarn),1573,1591,1712,WFDEV001 + + @@ -24,6 +27,10 @@ + + + + @@ -47,4 +54,10 @@ + + + + + + diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index acafa8b9abd..05264d5e7d5 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -7,46 +7,64 @@ namespace System.Windows.Forms.Tests { - public class PropertyGrid_IErrorInfoSupportTests : IClassFixture + [Collection("Sequential")] + public class PropertyGrid_IErrorInfoSupportTests { public const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); + public const string System_Windows_Forms_NativeTests = "System_Windows_Forms_NativeTests"; [WinFormsFact] public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() { using PropertyGrid propertyGrid = new PropertyGrid(); - propertyGrid.SelectedObject = CreateComObjectWithIErrorInfo(); + Create_Standard_IErrorInfo_UsageObject(out var target); + propertyGrid.SelectedObject = target; var entries = propertyGrid.GetCurrentEntries(); - var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "encoding"); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); try { - encodingEntry.SetPropertyTextValue("nonexisting"); + encodingEntry.SetPropertyTextValue("333"); Assert.False(true); } catch (ExternalException ex) { Assert.Equal(DISP_E_MEMBERNOTFOUND, ex.HResult); } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } } - private object CreateComObjectWithIErrorInfo() + [WinFormsFact] + public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() { - var CLSID_MXXMLWriter60 = new Guid("88d96a0f-f192-11d4-a65f-0040963251e5"); - var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); - CoCreateInstance(ref CLSID_MXXMLWriter60, - IntPtr.Zero, - 1, - ref IID_IUnknown, - out object result); - return result; + using PropertyGrid propertyGrid = new PropertyGrid(); + Create_Raw_IErrorInfo_UsageObject(out var target); + propertyGrid.SelectedObject = target; + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + try + { + encodingEntry.SetPropertyTextValue("123"); + Assert.False(true); + } + catch (ExternalException ex) + { + Assert.Equal("Error From IErrorInfo", ex.Message); + } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } } - [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)] - private static extern void CoCreateInstance( - ref Guid rclsid, - IntPtr punkOuter, - int dwClsContext, - ref Guid riid, - [MarshalAs(UnmanagedType.Interface)] out object ppv); + [DllImport(System_Windows_Forms_NativeTests, CharSet = CharSet.Unicode, ExactSpelling = true)] + private static extern void Create_Raw_IErrorInfo_UsageObject([MarshalAs(UnmanagedType.Interface)] out object pDisp); + + [DllImport(System_Windows_Forms_NativeTests, CharSet = CharSet.Unicode, ExactSpelling = true)] + private static extern void Create_Standard_IErrorInfo_UsageObject([MarshalAs(UnmanagedType.IUnknown)] out object pDisp); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt new file mode 100644 index 00000000000..4b3c4fd9e52 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required (VERSION 2.8.12) +project (System_Windows_Forms_NativeTests) +set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Compile IDL file using MIDL +set(IDL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Contract.idl) +get_filename_component(IDL_NAME ${IDL_SOURCE} NAME_WE) + +FIND_PROGRAM( MIDL midl.exe ) +set(IDL_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Contract) +add_custom_command( + OUTPUT ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h + COMMAND ${MIDL} ${MIDL_INCLUDE_DIRECTORIES} + /h ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h ${MIDL_DEFINITIONS} + /out ${IDL_OUTPUT_DIRECTORY} + /tlb $/System_Windows_Forms_NativeTests.tlb + ${IDL_SOURCE} + DEPENDS ${IDL_SOURCE} + COMMENT "Compiling ${IDL_SOURCE}") + +include_directories(${IDL_OUTPUT_DIRECTORY}) + +add_library(System_Windows_Forms_NativeTests SHARED + IErrorInfoObject.cpp + DispatchImpl.cpp + StandardErrorInfoUsageTest.cpp + RawErrorInfoUsageTest.cpp + ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c +) + +file(GENERATE OUTPUT $/System_Windows_Forms_NativeTests.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/System_Windows_Forms_NativeTests.X.manifest) + +install(TARGETS System_Windows_Forms_NativeTests) +install(FILES $/System_Windows_Forms_NativeTests.tlb TYPE BIN) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h new file mode 100644 index 00000000000..93ae4a3a09f --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h @@ -0,0 +1,411 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include +#include +#include +#include +#include +#include + +// Common macro for working in COM +#define RETURN_IF_FAILED(exp) { hr = exp; if (FAILED(hr)) { return hr; } } + +namespace Internal +{ + template + HRESULT __QueryInterfaceImpl( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject, + /* [in] */ I obj) + { + if (riid == __uuidof(I)) + { + *ppvObject = static_cast(obj); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + return S_OK; + } + + template + HRESULT __QueryInterfaceImpl( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject, + /* [in] */ I1 i1, + /* [in] */ IR... remain) + { + if (riid == __uuidof(I1)) + { + *ppvObject = static_cast(i1); + return S_OK; + } + + return __QueryInterfaceImpl(riid, ppvObject, remain...); + } +} + +// Implementation of IUnknown operations +class UnknownImpl +{ +public: + UnknownImpl() = default; + virtual ~UnknownImpl() = default; + + UnknownImpl(const UnknownImpl&) = delete; + UnknownImpl& operator=(const UnknownImpl&) = delete; + + UnknownImpl(UnknownImpl&&) = default; + UnknownImpl& operator=(UnknownImpl&&) = default; + + template + HRESULT DoQueryInterface( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void **ppvObject, + /* [in] */ I1 i1, + /* [in] */ IR... remain) + { + if (ppvObject == nullptr) + return E_POINTER; + + if (riid == __uuidof(IUnknown)) + { + *ppvObject = static_cast(i1); + } + else + { + HRESULT hr = Internal::__QueryInterfaceImpl(riid, ppvObject, i1, remain...); + if (hr != S_OK) + return hr; + } + + DoAddRef(); + return S_OK; + } + + ULONG DoAddRef() + { + assert(_refCount > 0); + return (++_refCount); + } + + ULONG DoRelease() + { + assert(_refCount > 0); + ULONG c = (--_refCount); + if (c == 0) + delete this; + return c; + } + +protected: + ULONG GetRefCount() + { + return _refCount; + } + +private: + std::atomic _refCount = 1; +}; + +// Macro to use for defining ref counting impls +#define DEFINE_REF_COUNTING() \ + STDMETHOD_(ULONG, AddRef)(void) { return UnknownImpl::DoAddRef(); } \ + STDMETHOD_(ULONG, Release)(void) { return UnknownImpl::DoRelease(); } + +// Templated class factory +template +class ClassFactoryBasic : public UnknownImpl, public IClassFactory +{ +public: // static + static HRESULT Create(_In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) + { + try + { + auto cf = new ClassFactoryBasic(); + HRESULT hr = cf->QueryInterface(riid, ppv); + cf->Release(); + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + +public: // IClassFactory + STDMETHOD(CreateInstance)( + _In_opt_ IUnknown *pUnkOuter, + _In_ REFIID riid, + _COM_Outptr_ void **ppvObject) + { + if (pUnkOuter != nullptr) + return CLASS_E_NOAGGREGATION; + + try + { + auto ti = new T(); + HRESULT hr = ti->QueryInterface(riid, ppvObject); + ti->Release(); + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + + STDMETHOD(LockServer)(/* [in] */ BOOL fLock) + { + assert(false && "Not impl"); + return E_NOTIMPL; + } + +public: // IUnknown + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) + { + return DoQueryInterface(riid, ppvObject, static_cast(this)); + } + + DEFINE_REF_COUNTING(); +}; + +// Templated class factory for aggregation +template +class ClassFactoryAggregate : public UnknownImpl, public IClassFactory +{ +public: // static + static HRESULT Create(_In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) + { + try + { + auto cf = new ClassFactoryAggregate(); + HRESULT hr = cf->QueryInterface(riid, ppv); + cf->Release(); + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + +public: // IClassFactory + STDMETHOD(CreateInstance)( + _In_opt_ IUnknown *pUnkOuter, + _In_ REFIID riid, + _COM_Outptr_ void **ppvObject) + { + if (pUnkOuter != nullptr && riid != IID_IUnknown) + return CLASS_E_NOAGGREGATION; + + try + { + auto ti = new T(pUnkOuter); + HRESULT hr = ti->QueryInterface(riid, ppvObject); + ti->Release(); + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + + STDMETHOD(LockServer)(/* [in] */ BOOL fLock) + { + assert(false && "Not impl"); + return E_NOTIMPL; + } + +public: // IUnknown + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) + { + return DoQueryInterface(riid, ppvObject, static_cast(this)); + } + + DEFINE_REF_COUNTING(); +}; + +// Templated class factory +// Supplied type must have the following properties to use this template: +// 1) Have a static method with the following signature: +// - HRESULT RequestLicKey(BSTR *key); +// 2) Have a constructor that takes an optional BSTR value as the key +template +class ClassFactoryLicense : public UnknownImpl, public IClassFactory2 +{ +public: // static + static HRESULT Create(_In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) + { + try + { + auto cf = new ClassFactoryLicense(); + HRESULT hr = cf->QueryInterface(riid, ppv); + cf->Release(); + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + +public: // IClassFactory + STDMETHOD(CreateInstance)( + _In_opt_ IUnknown *pUnkOuter, + _In_ REFIID riid, + _COM_Outptr_ void **ppvObject) + { + return CreateInstanceLic(pUnkOuter, nullptr, riid, nullptr, ppvObject); + } + + STDMETHOD(LockServer)(/* [in] */ BOOL fLock) + { + assert(false && "Not impl"); + return E_NOTIMPL; + } + +public: // IClassFactory2 + STDMETHOD(GetLicInfo)( + /* [out][in] */ __RPC__inout LICINFO *pLicInfo) + { + // The CLR does not call this function and as such, + // returns an error. Note that this is explicitly illegal + // in a proper implementation of IClassFactory2. + return E_UNEXPECTED; + } + + STDMETHOD(RequestLicKey)( + /* [in] */ DWORD dwReserved, + /* [out] */ __RPC__deref_out_opt BSTR *pBstrKey) + { + if (dwReserved != 0) + return E_UNEXPECTED; + + return T::RequestLicKey(pBstrKey); + } + + STDMETHOD(CreateInstanceLic)( + /* [annotation][in] */ _In_opt_ IUnknown *pUnkOuter, + /* [annotation][in] */ _Reserved_ IUnknown *pUnkReserved, + /* [annotation][in] */ __RPC__in REFIID riid, + /* [annotation][in] */ __RPC__in BSTR bstrKey, + /* [annotation][iid_is][out] */ __RPC__deref_out_opt PVOID *ppvObj) + { + if (pUnkOuter != nullptr) + return CLASS_E_NOAGGREGATION; + + if (pUnkReserved != nullptr) + return E_UNEXPECTED; + + try + { + auto ti = new T(bstrKey); + HRESULT hr = ti->QueryInterface(riid, ppvObj); + ti->Release(); + return hr; + } + catch (HRESULT hr) + { + return hr; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + } + +public: // IUnknown + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) + { + return DoQueryInterface(riid, ppvObject, static_cast(this), static_cast(this)); + } + + DEFINE_REF_COUNTING(); +}; + +template +struct ComSmartPtr +{ + T* p; + + ComSmartPtr() + : p{ nullptr } + { } + + ComSmartPtr(_In_ T* t) + : p{ t } + { + if (p != nullptr) + (void)p->AddRef(); + } + + ComSmartPtr(_In_ const ComSmartPtr&) = delete; + + ComSmartPtr(_Inout_ ComSmartPtr&& other) + : p{ other.Detach() } + { } + + ~ComSmartPtr() + { + Release(); + } + + ComSmartPtr& operator=(_In_ const ComSmartPtr&) = delete; + + ComSmartPtr& operator=(_Inout_ ComSmartPtr&& other) + { + Attach(other.Detach()); + return (*this); + } + + operator T*() + { + return p; + } + + T** operator&() + { + return &p; + } + + T* operator->() + { + return p; + } + + void Attach(_In_opt_ T* t) noexcept + { + Release(); + p = t; + } + + T* Detach() noexcept + { + T* tmp = p; + p = nullptr; + return tmp; + } + + void Release() noexcept + { + if (p != nullptr) + { + (void)p->Release(); + p = nullptr; + } + } +}; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl new file mode 100644 index 00000000000..a231e1f2a97 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl @@ -0,0 +1,38 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(F4611744-02AF-47D4-A10F-9E692368DEFD) +] +interface IBasicTest : IDispatch +{ + [propget] HRESULT Int_Property([out, retval] int *ret); + [propput] HRESULT Int_Property([in] int val); +}; + +[ + uuid(0971AD7E-3D4A-4C44-B0A3-A518AC88DFE1) +] +library System_Windows_Forms_NativeTests +{ + importlib("stdole2.tlb"); + + [ + uuid(0ED8EE0D-22E3-49EA-850C-E69B20D1F296) + ] + coclass RawErrorInfoUsageTest + { + [default] interface IBasicTest; + interface ISupportErrorInfo; + } + + [ + uuid(EA1FCB3A-277C-4C79-AB85-E2ED3E858201) + ] + coclass StandardErrorInfoUsageTest + { + [default] interface IBasicTest; + interface ISupportErrorInfo; + } +} diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp new file mode 100644 index 00000000000..1feac6b84eb --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "DispatchImpl.h" + +namespace +{ + const wchar_t* s_tlbDefault = L"System_Windows_Forms_NativeTests.tlb"; +} + +DispatchImpl::DispatchImpl(GUID guid, void *instance, const wchar_t* tlb) + : _typeLib{ nullptr } + , _typeInfo{ nullptr } + , _instance{ instance } +{ + const wchar_t* tlbToLoad = tlb == nullptr ? s_tlbDefault : tlb; + HRESULT hr = ::LoadTypeLibEx(tlbToLoad, REGKIND::REGKIND_NONE, &_typeLib); + if (FAILED(hr)) + throw hr; + + hr = _typeLib->GetTypeInfoOfGuid(guid, &_typeInfo); + if (FAILED(hr)) + throw hr; +} + +DispatchImpl::~DispatchImpl() +{ + if (_typeLib != nullptr) + _typeLib->Release(); + + if (_typeInfo != nullptr) + _typeInfo->Release(); +}; + +HRESULT DispatchImpl::DoGetTypeInfoCount(UINT* pctinfo) +{ + *pctinfo = 1; + return S_OK; +} + +HRESULT DispatchImpl::DoGetTypeInfo(UINT iTInfo, ITypeInfo** ppTInfo) +{ + if (iTInfo != 0) + return DISP_E_BADINDEX; + + assert(_typeInfo != nullptr); + return _typeInfo->QueryInterface(__uuidof(*ppTInfo), (void**)ppTInfo); +} + +HRESULT DispatchImpl::DoGetIDsOfNames(LPOLESTR* rgszNames, UINT cNames, DISPID* rgDispId) +{ + return _typeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); +} + +HRESULT DispatchImpl::DoInvoke(DISPID dispIdMember, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) +{ + return _typeInfo->Invoke(_instance, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); +} diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h new file mode 100644 index 00000000000..2a1a613b097 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "ComHelpers.h" +#include + +// Implementation of IDispatch operations +class DispatchImpl : public UnknownImpl +{ +public: + DispatchImpl(GUID guid, void *instance, const wchar_t* tlb = nullptr); + virtual ~DispatchImpl(); + + DispatchImpl(const DispatchImpl&) = delete; + DispatchImpl& operator=(const DispatchImpl&) = delete; + + DispatchImpl(DispatchImpl&&) = default; + DispatchImpl& operator=(DispatchImpl&&) = default; + +protected: + HRESULT DoGetTypeInfoCount(UINT* pctinfo); + HRESULT DoGetTypeInfo(UINT iTInfo, ITypeInfo** ppTInfo); + HRESULT DoGetIDsOfNames(LPOLESTR* rgszNames, UINT cNames, DISPID* rgDispId); + HRESULT DoInvoke(DISPID dispIdMember, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr); + +private: + ITypeLib *_typeLib; + ITypeInfo *_typeInfo; + void *_instance; +}; + +// Macro to use for defining dispatch impls +#define DEFINE_DISPATCH() \ + STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) \ + { return DispatchImpl::DoGetTypeInfoCount(pctinfo); } \ + STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) \ + { return DispatchImpl::DoGetTypeInfo(iTInfo, ppTInfo); } \ + STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) \ + { return DispatchImpl::DoGetIDsOfNames(rgszNames, cNames, rgDispId); } \ + STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) \ + { return DispatchImpl::DoInvoke(dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp new file mode 100644 index 00000000000..75b1f1674bd --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp @@ -0,0 +1,27 @@ +// 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. + +#include "ocidl.h" +#include +#include "ComHelpers.h" +#include + +#include "RawErrorInfoUsageTest.h" +#include "StandardErrorInfoUsageTest.h" + +extern "C" __declspec(dllexport) HRESULT WINAPI Create_Raw_IErrorInfo_UsageObject(_Out_ LPVOID FAR * ppDispatchPtr) +{ + IClassFactory* classFactory; + HRESULT hr; + RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); + return classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr); +} + +extern "C" __declspec(dllexport) HRESULT WINAPI Create_Standard_IErrorInfo_UsageObject(_Out_ LPVOID FAR * ppDispatchPtr) +{ + IClassFactory* classFactory; + HRESULT hr; + RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); + return classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr); +} diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp new file mode 100644 index 00000000000..7b3c3e84116 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "RawErrorInfoUsageTest.h" +#include + +HRESULT STDMETHODCALLTYPE RawErrorInfoUsageTest::get_Int_Property( + /* [retval][out] */ int *ret) +{ + *ret = _int; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE RawErrorInfoUsageTest::put_Int_Property( + /* [in] */ int val) +{ + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT STDMETHODCALLTYPE RawErrorInfoUsageTest::InterfaceSupportsErrorInfo( + /* [in] */ __RPC__in REFIID riid) +{ + // This is hack, in order to not implement IDispatch. + // By default implementations wrap any error during invoke into DISP_E_EXCEPTION + // and consume IErrorInfo. Some implementation behave differently, so this is emulation + // of that behaviour. + ICreateErrorInfo* cei; + if (SUCCEEDED(::CreateErrorInfo(&cei))) + { + if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) + { + if (SUCCEEDED(cei->SetDescription(L"Error From IErrorInfo"))) + { + IErrorInfo* errorInfo; + if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) + { + ::SetErrorInfo(0, errorInfo); + errorInfo->Release(); + } + } + } + + cei->Release(); + } + + return S_OK; +} diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h new file mode 100644 index 00000000000..b3883cf55d8 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "ComHelpers.h" +#include +#include "DispatchImpl.h" +#include + +class RawErrorInfoUsageTest : public DispatchImpl, public IBasicTest, public ISupportErrorInfo +{ +public: + RawErrorInfoUsageTest() + : DispatchImpl(IID_IBasicTest, static_cast(this)) + { + } + + ~RawErrorInfoUsageTest() + { + } + +public: // IBasicTest + + virtual HRESULT STDMETHODCALLTYPE get_Int_Property( + /* [retval][out] */ int *ret); + + virtual HRESULT STDMETHODCALLTYPE put_Int_Property( + /* [in] */ int val); + +public: // ISupportErrorInfo + virtual HRESULT STDMETHODCALLTYPE InterfaceSupportsErrorInfo( + /* [in] */ __RPC__in REFIID riid); + +public: // IDispatch + DEFINE_DISPATCH(); + +public: // IUnknown + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) + { + return DoQueryInterface(riid, ppvObject, + static_cast(this), + static_cast(this), + static_cast(this)); + } + + DEFINE_REF_COUNTING(); + +private: + int _int; +}; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp new file mode 100644 index 00000000000..54b014ce6e7 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "StandardErrorInfoUsageTest.h" +#include + +HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::get_Int_Property( + /* [retval][out] */ int *ret) +{ + *ret = _int; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::put_Int_Property( + /* [in] */ int val) +{ + ICreateErrorInfo* cei; + if (SUCCEEDED(::CreateErrorInfo(&cei))) + { + if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) + { + if (SUCCEEDED(cei->SetDescription(L"Error From IErrorInfo"))) + { + IErrorInfo* errorInfo; + if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) + { + ::SetErrorInfo(0, errorInfo); + errorInfo->Release(); + } + } + } + + cei->Release(); + } + + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::InterfaceSupportsErrorInfo( + /* [in] */ __RPC__in REFIID riid) +{ + return S_OK; +} diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h new file mode 100644 index 00000000000..3a5feb63b12 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "ComHelpers.h" +#include +#include "DispatchImpl.h" +#include + +class StandardErrorInfoUsageTest : public DispatchImpl, public IBasicTest, public ISupportErrorInfo +{ +public: + StandardErrorInfoUsageTest() + : DispatchImpl(IID_IBasicTest, static_cast(this)) + { + } + + ~StandardErrorInfoUsageTest() + { + } + +public: // IBasicTest + + virtual HRESULT STDMETHODCALLTYPE get_Int_Property( + /* [retval][out] */ int *ret); + + virtual HRESULT STDMETHODCALLTYPE put_Int_Property( + /* [in] */ int val); + +public: // ISupportErrorInfo + virtual HRESULT STDMETHODCALLTYPE InterfaceSupportsErrorInfo( + /* [in] */ __RPC__in REFIID riid); + +public: // IDispatch + DEFINE_DISPATCH(); + +public: // IUnknown + STDMETHOD(QueryInterface)( + /* [in] */ REFIID riid, + /* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) + { + return DoQueryInterface(riid, ppvObject, + static_cast(this), + static_cast(this), + static_cast(this)); + } + + DEFINE_REF_COUNTING(); + +private: + int _int; +}; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest new file mode 100644 index 00000000000..cf03f773211 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj new file mode 100644 index 00000000000..fe624ca5788 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj @@ -0,0 +1,5 @@ + + + CMakeLists.txt + + From e0fd4f5123e02bb14e5bc4af62a1977d121e0213 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Mon, 14 Mar 2022 18:14:34 +0600 Subject: [PATCH 07/28] Initialize VsVarsAll when building tests project --- eng/init-vs-env.cmd | 72 +++++++++++++++++++ .../WindowsFormsTests/WindowsFormsTests.proj | 1 + 2 files changed, 73 insertions(+) create mode 100644 eng/init-vs-env.cmd diff --git a/eng/init-vs-env.cmd b/eng/init-vs-env.cmd new file mode 100644 index 00000000000..18a77332e04 --- /dev/null +++ b/eng/init-vs-env.cmd @@ -0,0 +1,72 @@ +@if not defined _echo @echo off + +:: Initializes Visual Studio developer environment. If a build architecture is passed +:: as an argument, it also initializes VC++ build environment and CMakePath. + +set "__VCBuildArch=" +if /i "%~1" == "x86" (set __VCBuildArch=x86) +if /i "%~1" == "x64" (set __VCBuildArch=x86_amd64) +if /i "%~1" == "arm" (set __VCBuildArch=x86_arm) +if /i "%~1" == "arm64" (set __VCBuildArch=x86_arm64) +if /i "%~1" == "wasm" (set __VCBuildArch=x86_amd64) + +:: Default to highest Visual Studio version available that has Visual C++ tools. +:: +:: For VS2017 and later, multiple instances can be installed on the same box SxS and VS1*0COMNTOOLS +:: is no longer set as a global environment variable and is instead only set if the user +:: has launched the Visual Studio Developer Command Prompt. +:: +:: Following this logic, we will default to the Visual Studio toolset assocated with the active +:: Developer Command Prompt. Otherwise, we will query VSWhere to locate the later version of +:: Visual Studio available on the machine. Finally, we will fail the script if no supported +:: instance can be found. + +if defined VisualStudioVersion goto :VSDetected + +set "__VSWhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" +set "__VSCOMNTOOLS=" + +if exist "%__VSWhere%" ( + for /f "tokens=*" %%p in ( + '"%__VSWhere%" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath' + ) do set __VSCOMNTOOLS=%%p\Common7\Tools +) + +if not exist "%__VSCOMNTOOLS%" goto :VSMissing + +:: Make sure the current directory stays intact +set "VSCMD_START_DIR=%CD%" + +call "%__VSCOMNTOOLS%\VsDevCmd.bat" -no_logo + +:: Clean up helper variables +set "__VSWhere=" +set "__VSCOMNTOOLS=" +set "VSCMD_START_DIR=" + +:VSDetected +if "%VisualStudioVersion%"=="16.0" ( + set __VSVersion=vs2019 + set __PlatformToolset=v142 + goto :SetVCEnvironment +) +if "%VisualStudioVersion%"=="17.0" ( + set __VSVersion=vs2022 + set __PlatformToolset=v142 + goto :SetVCEnvironment +) + +:VSMissing +echo %__MsgPrefix%Error: Visual Studio 2019 or 2022 with C++ tools required. ^ +Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md for build requirements. +exit /b 1 + +:SetVCEnvironment + +if "%__VCBuildArch%"=="" exit /b 0 + +:: Set the environment for the native build +call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% +if not "%ErrorLevel%"=="0" exit /b 1 + +set "__VCBuildArch=" diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj index fe624ca5788..8b5b6b4132c 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj @@ -1,5 +1,6 @@ + call "$(RepoRoot)eng\init-vs-env.cmd" $(TargetArchitecture) CMakeLists.txt From 8e9ee85ec8c4ea6d9b980206f49f5d5490f2f412 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Mon, 14 Mar 2022 18:47:17 +0600 Subject: [PATCH 08/28] Attempt to fix --- .../UnitTests/WindowsFormsTests/IErrorInfoObject.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp index 75b1f1674bd..aaac491d8f1 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp @@ -15,7 +15,9 @@ extern "C" __declspec(dllexport) HRESULT WINAPI Create_Raw_IErrorInfo_UsageObjec IClassFactory* classFactory; HRESULT hr; RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); - return classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr); + RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); + ((IUnknown*)ppDispatchPtr)->AddRef(); + return S_OK; } extern "C" __declspec(dllexport) HRESULT WINAPI Create_Standard_IErrorInfo_UsageObject(_Out_ LPVOID FAR * ppDispatchPtr) @@ -23,5 +25,7 @@ extern "C" __declspec(dllexport) HRESULT WINAPI Create_Standard_IErrorInfo_Usage IClassFactory* classFactory; HRESULT hr; RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); - return classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr); + RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); + ((IUnknown*)ppDispatchPtr)->AddRef(); + return S_OK; } From a89b66beaf82c52ec56859df8c95db410cc12675 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Mon, 14 Mar 2022 19:27:16 +0600 Subject: [PATCH 09/28] One more stupid try --- .../tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp index aaac491d8f1..daf302465ec 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp @@ -16,7 +16,7 @@ extern "C" __declspec(dllexport) HRESULT WINAPI Create_Raw_IErrorInfo_UsageObjec HRESULT hr; RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); - ((IUnknown*)ppDispatchPtr)->AddRef(); + ((IUnknown*)*ppDispatchPtr)->AddRef(); return S_OK; } @@ -26,6 +26,6 @@ extern "C" __declspec(dllexport) HRESULT WINAPI Create_Standard_IErrorInfo_Usage HRESULT hr; RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); - ((IUnknown*)ppDispatchPtr)->AddRef(); + ((IUnknown*)*ppDispatchPtr)->AddRef(); return S_OK; } From 7ce1a85edbbcabcca3fb19ffc3fdce8de44603f7 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 16 Mar 2022 11:10:35 +0600 Subject: [PATCH 10/28] Use reg-free COM for tests --- .../System.Windows.Forms.Tests.csproj | 1 + .../PropertyGrid.IErrorInfoSupportTests.cs | 168 ++++++++++++++---- .../UnitTests/WindowsFormsTests/App.manifest | 18 ++ .../WindowsFormsTests/CMakeLists.txt | 4 + .../UnitTests/WindowsFormsTests/Exports.def | 2 + .../WindowsFormsTests/IErrorInfoObject.cpp | 23 +-- ...ystem_Windows_Forms_NativeTests.X.manifest | 8 +- 7 files changed, 168 insertions(+), 56 deletions(-) create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index af3f4d14be3..f676be7184d 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -57,6 +57,7 @@ + diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index 05264d5e7d5..df0777e7ef0 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -2,6 +2,7 @@ // 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.ComponentModel; using System.Runtime.InteropServices; using Xunit; @@ -13,58 +14,149 @@ public class PropertyGrid_IErrorInfoSupportTests public const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); public const string System_Windows_Forms_NativeTests = "System_Windows_Forms_NativeTests"; - [WinFormsFact] + [Fact] public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() { - using PropertyGrid propertyGrid = new PropertyGrid(); - Create_Standard_IErrorInfo_UsageObject(out var target); - propertyGrid.SelectedObject = target; - var entries = propertyGrid.GetCurrentEntries(); - var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); - try - { - encodingEntry.SetPropertyTextValue("333"); - Assert.False(true); - } - catch (ExternalException ex) - { - Assert.Equal(DISP_E_MEMBERNOTFOUND, ex.HResult); - } - finally - { - propertyGrid.SelectedObject = null; - Marshal.ReleaseComObject(target); - } + ExecuteWithActivationContext( + "App.manifest", + () => + { + using PropertyGrid propertyGrid = new PropertyGrid(); + var target = CreateComObjectWithStandardIErrorInfoUsage(); + propertyGrid.SelectedObject = target; + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + try + { + encodingEntry.SetPropertyTextValue("333"); + Assert.False(true); + } + catch (ExternalException ex) + { + Assert.Equal(DISP_E_MEMBERNOTFOUND, ex.HResult); + } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } + }); } - [WinFormsFact] + [Fact] public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() { - using PropertyGrid propertyGrid = new PropertyGrid(); - Create_Raw_IErrorInfo_UsageObject(out var target); - propertyGrid.SelectedObject = target; - var entries = propertyGrid.GetCurrentEntries(); - var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + ExecuteWithActivationContext( + "App.manifest", + () => + { + using PropertyGrid propertyGrid = new PropertyGrid(); + var target = CreateComObjectWithRawIErrorInfoUsage(); + propertyGrid.SelectedObject = target; + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + try + { + encodingEntry.SetPropertyTextValue("123"); + Assert.False(true); + } + catch (ExternalException ex) + { + Assert.Equal("Error From IErrorInfo", ex.Message); + } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } + }); + } + + private void ExecuteWithActivationContext(string applicationManifest, Action action) + { + ACTCTXW context = new ACTCTXW(); + context.cbSize = Marshal.SizeOf(); + context.lpSource = applicationManifest; + + var handle = CreateActCtxW(in context); + if (handle == IntPtr.Zero) + throw new Win32Exception(); try { - encodingEntry.SetPropertyTextValue("123"); - Assert.False(true); - } - catch (ExternalException ex) - { - Assert.Equal("Error From IErrorInfo", ex.Message); + if (!ActivateActCtx(handle, out var cookie)) + throw new Win32Exception(); + try + { + action(); + } + finally + { + if (!DeactivateActCtx(0, cookie)) + throw new Win32Exception(); + } } finally { - propertyGrid.SelectedObject = null; - Marshal.ReleaseComObject(target); + ReleaseActCtx(handle); } } - [DllImport(System_Windows_Forms_NativeTests, CharSet = CharSet.Unicode, ExactSpelling = true)] - private static extern void Create_Raw_IErrorInfo_UsageObject([MarshalAs(UnmanagedType.Interface)] out object pDisp); + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + struct ACTCTXW + { + public int cbSize; // ULONG + public int dwFlags; // DWORD + public string lpSource; // LPCWSTR + public short wProcessorArchitecture; // USHORT + public short wLangId; // LANGID + public string lpAssemblyDirectory; // LPCWSTR + public string lpResourceName; // LPCWSTR + public string lpApplicationName; // LPCWSTR + public IntPtr hModule; // HMODULE + } + + private object CreateComObjectWithRawIErrorInfoUsage() + { + var clsid = new Guid("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); + var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + CoCreateInstance(ref clsid, + IntPtr.Zero, + 1, + ref IID_IUnknown, + out object result); + return result; + } + + private object CreateComObjectWithStandardIErrorInfoUsage() + { + var clsid = new Guid("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); + var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + CoCreateInstance(ref clsid, + IntPtr.Zero, + 1, + ref IID_IUnknown, + out object result); + return result; + } + + [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)] + private static extern void CoCreateInstance( + ref Guid rclsid, + IntPtr punkOuter, + int dwClsContext, + ref Guid riid, + [MarshalAs(UnmanagedType.Interface)] out object ppv); + + [DllImport("kernel32", SetLastError = true)] + private static extern IntPtr CreateActCtxW(in ACTCTXW pActCtx); + + [DllImport("kernel32", SetLastError = true)] + private static extern void ReleaseActCtx(IntPtr hActCtx); + + [DllImport("kernel32", SetLastError = true)] + private static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie); - [DllImport(System_Windows_Forms_NativeTests, CharSet = CharSet.Unicode, ExactSpelling = true)] - private static extern void Create_Standard_IErrorInfo_UsageObject([MarshalAs(UnmanagedType.IUnknown)] out object pDisp); + [DllImport("kernel32")] + private static extern bool DeactivateActCtx(int dwFlags, IntPtr ulCookie); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest new file mode 100644 index 00000000000..4ff485bf076 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt index 4b3c4fd9e52..da710601d98 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt @@ -27,10 +27,14 @@ add_library(System_Windows_Forms_NativeTests SHARED DispatchImpl.cpp StandardErrorInfoUsageTest.cpp RawErrorInfoUsageTest.cpp + Exports.def ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c ) file(GENERATE OUTPUT $/System_Windows_Forms_NativeTests.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/System_Windows_Forms_NativeTests.X.manifest) +file(GENERATE OUTPUT $/App.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/App.manifest) install(TARGETS System_Windows_Forms_NativeTests) install(FILES $/System_Windows_Forms_NativeTests.tlb TYPE BIN) +install(FILES $/System_Windows_Forms_NativeTests.X.manifest TYPE BIN) +install(FILES $/App.manifest TYPE BIN) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def new file mode 100644 index 00000000000..2e9253049fd --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def @@ -0,0 +1,2 @@ +EXPORTS + DllGetClassObject PRIVATE diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp index daf302465ec..2b007eb83a3 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp @@ -10,22 +10,13 @@ #include "RawErrorInfoUsageTest.h" #include "StandardErrorInfoUsageTest.h" -extern "C" __declspec(dllexport) HRESULT WINAPI Create_Raw_IErrorInfo_UsageObject(_Out_ LPVOID FAR * ppDispatchPtr) +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID FAR* ppv) { - IClassFactory* classFactory; - HRESULT hr; - RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); - RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); - ((IUnknown*)*ppDispatchPtr)->AddRef(); - return S_OK; -} + if (rclsid == __uuidof(RawErrorInfoUsageTest)) + return ClassFactoryBasic::Create(riid, ppv); -extern "C" __declspec(dllexport) HRESULT WINAPI Create_Standard_IErrorInfo_UsageObject(_Out_ LPVOID FAR * ppDispatchPtr) -{ - IClassFactory* classFactory; - HRESULT hr; - RETURN_IF_FAILED(ClassFactoryBasic::Create(IID_IClassFactory, (LPVOID*)&classFactory)); - RETURN_IF_FAILED(classFactory->CreateInstance(nullptr, IID_IBasicTest, ppDispatchPtr)); - ((IUnknown*)*ppDispatchPtr)->AddRef(); - return S_OK; + if (rclsid == __uuidof(StandardErrorInfoUsageTest)) + return ClassFactoryBasic::Create(riid, ppv); + + return CLASS_E_CLASSNOTAVAILABLE; } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest index cf03f773211..a6d464be5be 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest @@ -7,10 +7,14 @@ version="1.0.0.0" /> - + + + From 0c263182f3f8e6688bb1d42b7bf7fe80634a8124 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 16 Mar 2022 11:47:52 +0600 Subject: [PATCH 11/28] Add ARM64 platform --- .../tests/UnitTests/System.Windows.Forms.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index f676be7184d..62113931fc1 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -2,7 +2,7 @@ System.Windows.Forms.Tests - x86;x64 + x86;x64;arm64 true From d12ffc265243fa5acba72011b451bb46d819916d Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 16 Mar 2022 13:36:24 +0600 Subject: [PATCH 12/28] Add configuration --- Winforms.sln | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Winforms.sln b/Winforms.sln index 1a317a880a2..7d946d16f6c 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -195,20 +195,20 @@ Global {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.Build.0 = Release|Any CPU {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|arm64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|arm64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.ActiveCfg = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.Build.0 = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.ActiveCfg = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.Build.0 = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|arm64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|arm64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.ActiveCfg = Release|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.Build.0 = Release|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.ActiveCfg = Release|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.Build.0 = Release|x86 {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|Any CPU.Build.0 = Debug|Any CPU {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -563,16 +563,16 @@ Global {86418F0B-39DC-4B5A-8145-6D607E6150AC}.Release|x86.Build.0 = Release|Any CPU {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.ActiveCfg = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.Build.0 = Debug|x86 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.ActiveCfg = Debug|x64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.Build.0 = Debug|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.ActiveCfg = Debug|arm64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.Build.0 = Debug|arm64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.ActiveCfg = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.Build.0 = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.ActiveCfg = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.Build.0 = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.ActiveCfg = Release|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.Build.0 = Release|x86 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.ActiveCfg = Release|x64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.Build.0 = Release|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.ActiveCfg = Release|arm64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.Build.0 = Release|arm64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.ActiveCfg = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.Build.0 = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x86.ActiveCfg = Release|x86 From 845989e185a9f663ba3180bdbb7318bdb54ee07f Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 16 Mar 2022 18:01:04 +0600 Subject: [PATCH 13/28] Ignore copying XML files for native projects --- Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index cdb7a8fbb7c..73b3f954737 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -2,7 +2,7 @@ - + From 66337df359c7788b00a2a41a232f5b5bb88366e9 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 16 Mar 2022 23:37:16 +0600 Subject: [PATCH 14/28] Probably I was too smart for ARM64 changes. Attempt to mimic System.Windows.Forms.Interop.Tests --- Winforms.sln | 16 ++++++++-------- .../UnitTests/System.Windows.Forms.Tests.csproj | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Winforms.sln b/Winforms.sln index 7d946d16f6c..03955d61e00 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -195,16 +195,16 @@ Global {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.Build.0 = Release|Any CPU {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|arm64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|arm64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|x86 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|arm64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|arm64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|x64 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.ActiveCfg = Release|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.Build.0 = Release|x64 {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.ActiveCfg = Release|x86 @@ -563,16 +563,16 @@ Global {86418F0B-39DC-4B5A-8145-6D607E6150AC}.Release|x86.Build.0 = Release|Any CPU {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.ActiveCfg = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.Build.0 = Debug|x86 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.ActiveCfg = Debug|arm64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.Build.0 = Debug|arm64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.ActiveCfg = Debug|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.Build.0 = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.ActiveCfg = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.Build.0 = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.ActiveCfg = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.Build.0 = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.ActiveCfg = Release|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.Build.0 = Release|x86 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.ActiveCfg = Release|arm64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.Build.0 = Release|arm64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.ActiveCfg = Release|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.Build.0 = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.ActiveCfg = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.Build.0 = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x86.ActiveCfg = Release|x86 diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index 62113931fc1..f676be7184d 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -2,7 +2,7 @@ System.Windows.Forms.Tests - x86;x64;arm64 + x86;x64 true From 7071825998d6ad6b737c41b92704fd2cc6132b92 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Mar 2022 12:15:39 +0600 Subject: [PATCH 15/28] Apply suggestions from code review Co-authored-by: Igor Velikorossov --- .../Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index df0777e7ef0..2cdcfc5599e 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -117,7 +117,7 @@ struct ACTCTXW private object CreateComObjectWithRawIErrorInfoUsage() { - var clsid = new Guid("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); + Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); CoCreateInstance(ref clsid, IntPtr.Zero, @@ -129,7 +129,7 @@ private object CreateComObjectWithRawIErrorInfoUsage() private object CreateComObjectWithStandardIErrorInfoUsage() { - var clsid = new Guid("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); + Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); CoCreateInstance(ref clsid, IntPtr.Zero, From f767d8ee150f1e6aaea14e202449e0ecb4b1b345 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Mar 2022 16:59:01 +0600 Subject: [PATCH 16/28] Add explanation about project file and cleanup of tests --- .../PropertyGrid.IErrorInfoSupportTests.cs | 65 ++++++++----------- .../UnitTests/WindowsFormsTests/App.manifest | 26 ++++---- .../UnitTests/WindowsFormsTests/README.md | 18 +++++ .../RawErrorInfoUsageTest.cpp | 2 +- .../StandardErrorInfoUsageTest.cpp | 2 +- ...ystem_Windows_Forms_NativeTests.X.manifest | 18 ++--- 6 files changed, 68 insertions(+), 63 deletions(-) create mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index 2cdcfc5599e..b10957b05fb 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -5,15 +5,13 @@ using System.ComponentModel; using System.Runtime.InteropServices; using Xunit; +using static Interop; namespace System.Windows.Forms.Tests { [Collection("Sequential")] public class PropertyGrid_IErrorInfoSupportTests { - public const int DISP_E_MEMBERNOTFOUND = unchecked((int)0x80020003); - public const string System_Windows_Forms_NativeTests = "System_Windows_Forms_NativeTests"; - [Fact] public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() { @@ -29,11 +27,15 @@ public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() try { encodingEntry.SetPropertyTextValue("333"); - Assert.False(true); + Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); } catch (ExternalException ex) { - Assert.Equal(DISP_E_MEMBERNOTFOUND, ex.HResult); + // Most default C++ implementation when Invoke return error code + // consult IErrorInfo object and populate EXCEPINFO structure + // So grid entry knows only about error code. + Assert.Equal((int)HRESULT.DISP_E_MEMBERNOTFOUND, ex.HResult); + Assert.Equal("Error From StandardErrorInfoUsageTest", ex.Message); } finally { @@ -58,11 +60,14 @@ public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() try { encodingEntry.SetPropertyTextValue("123"); - Assert.False(true); + Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); } catch (ExternalException ex) { - Assert.Equal("Error From IErrorInfo", ex.Message); + // If C++ implementation of Invoke did not populate EXCEPINFO structure + // from IErrorInfo then we read that information about error call. + // and display that error message to the user. + Assert.Equal("Error From RawErrorInfoUsageTest", ex.Message); } finally { @@ -72,18 +77,23 @@ public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() }); } - private void ExecuteWithActivationContext(string applicationManifest, Action action) + private unsafe void ExecuteWithActivationContext(string applicationManifest, Action action) { - ACTCTXW context = new ACTCTXW(); - context.cbSize = Marshal.SizeOf(); - context.lpSource = applicationManifest; + var context = new Kernel32.ACTCTXW(); + IntPtr handle; + fixed (char* p = applicationManifest) + { + context.cbSize = (uint)sizeof(Kernel32.ACTCTXW); + context.lpSource = p; + + handle = Kernel32.CreateActCtxW(ref context); + } - var handle = CreateActCtxW(in context); if (handle == IntPtr.Zero) throw new Win32Exception(); try { - if (!ActivateActCtx(handle, out var cookie)) + if (Kernel32.ActivateActCtx(handle, out var cookie).IsFalse()) throw new Win32Exception(); try { @@ -91,7 +101,7 @@ private void ExecuteWithActivationContext(string applicationManifest, Action act } finally { - if (!DeactivateActCtx(0, cookie)) + if (Kernel32.DeactivateActCtx(0, cookie).IsFalse()) throw new Win32Exception(); } } @@ -101,25 +111,11 @@ private void ExecuteWithActivationContext(string applicationManifest, Action act } } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct ACTCTXW - { - public int cbSize; // ULONG - public int dwFlags; // DWORD - public string lpSource; // LPCWSTR - public short wProcessorArchitecture; // USHORT - public short wLangId; // LANGID - public string lpAssemblyDirectory; // LPCWSTR - public string lpResourceName; // LPCWSTR - public string lpApplicationName; // LPCWSTR - public IntPtr hModule; // HMODULE - } - private object CreateComObjectWithRawIErrorInfoUsage() { Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); - CoCreateInstance(ref clsid, + CoCreateInstance(ref clsidRawErrorInfoUsageTest, IntPtr.Zero, 1, ref IID_IUnknown, @@ -131,7 +127,7 @@ private object CreateComObjectWithStandardIErrorInfoUsage() { Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); - CoCreateInstance(ref clsid, + CoCreateInstance(ref clsidStandardErrorInfoUsageTest, IntPtr.Zero, 1, ref IID_IUnknown, @@ -147,16 +143,7 @@ private static extern void CoCreateInstance( ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppv); - [DllImport("kernel32", SetLastError = true)] - private static extern IntPtr CreateActCtxW(in ACTCTXW pActCtx); - [DllImport("kernel32", SetLastError = true)] private static extern void ReleaseActCtx(IntPtr hActCtx); - - [DllImport("kernel32", SetLastError = true)] - private static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie); - - [DllImport("kernel32")] - private static extern bool DeactivateActCtx(int dwFlags, IntPtr ulCookie); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest index 4ff485bf076..1f3263c125d 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest @@ -1,18 +1,18 @@  - + - - - - - - + + + + + + diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md new file mode 100644 index 00000000000..401897553a4 --- /dev/null +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md @@ -0,0 +1,18 @@ +Supplementary native parts for WinForms unit tests +================================================== + +This project is native DLL which provide unmanaged parts required to testing of WinForms components. Most interesting part of the tests would be COM objects which given to controls. +This project builds as native DLL, which can be used as reg-free COM host. That allow run tests and do not worry about polluting the developer/build machines. + +Preferably each test case should have their separate COM objects. + +Manifests used in the application: + +- `App.manifest` This file used during running test to provide information about this DLL. Developers do not need to modify that file, just run it inside activation context. +- `System_Windows_Forms_NativeTests.X.manifest` This is manifest where all reg-free COM objects registered. + +## How to add new COM object + +1. Declare COM object in `Contract.idl` file +2. Implement COM object in C++. +3. Add CoClass defined in `Contract.idl` inside `System_Windows_Forms_NativeTests.X.manifest` diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp index 7b3c3e84116..aaca53024d7 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp @@ -29,7 +29,7 @@ HRESULT STDMETHODCALLTYPE RawErrorInfoUsageTest::InterfaceSupportsErrorInfo( { if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) { - if (SUCCEEDED(cei->SetDescription(L"Error From IErrorInfo"))) + if (SUCCEEDED(cei->SetDescription(L"Error From RawErrorInfoUsageTest"))) { IErrorInfo* errorInfo; if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp index 54b014ce6e7..173437ce799 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp @@ -19,7 +19,7 @@ HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::put_Int_Property( { if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) { - if (SUCCEEDED(cei->SetDescription(L"Error From IErrorInfo"))) + if (SUCCEEDED(cei->SetDescription(L"Error From StandardErrorInfoUsageTest"))) { IErrorInfo* errorInfo; if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest index a6d464be5be..bec90eea888 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest @@ -2,19 +2,19 @@ + type="win32" + name="System_Windows_Forms_NativeTests.X" + version="1.0.0.0" /> - - + + + clsid="{EA1FCB3A-277C-4C79-AB85-E2ED3E858201}" + threadingModel="Both" /> From 49bcdaac3b6f22b5129b515c163f793bdedb65fa Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Wed, 23 Mar 2022 18:01:35 +0600 Subject: [PATCH 17/28] Use IID_IUnknown from Primitives --- .../Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index b10957b05fb..9b253cc5204 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -114,11 +114,10 @@ private unsafe void ExecuteWithActivationContext(string applicationManifest, Act private object CreateComObjectWithRawIErrorInfoUsage() { Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); - var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); CoCreateInstance(ref clsidRawErrorInfoUsageTest, IntPtr.Zero, 1, - ref IID_IUnknown, + ref NativeMethods.ActiveX.IID_IUnknown, out object result); return result; } @@ -126,11 +125,10 @@ private object CreateComObjectWithRawIErrorInfoUsage() private object CreateComObjectWithStandardIErrorInfoUsage() { Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); - var IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); CoCreateInstance(ref clsidStandardErrorInfoUsageTest, IntPtr.Zero, 1, - ref IID_IUnknown, + ref NativeMethods.ActiveX.IID_IUnknown, out object result); return result; } From bd3fd4c573dc9f0804c4bc19fc333aa1dc4c4632 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 24 Mar 2022 17:24:01 +0600 Subject: [PATCH 18/28] Apply suggestions from code review Co-authored-by: Igor Velikorossov --- .../Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs | 3 +-- .../tests/UnitTests/WindowsFormsTests/README.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index 9b253cc5204..539acf78046 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -65,8 +65,7 @@ public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() catch (ExternalException ex) { // If C++ implementation of Invoke did not populate EXCEPINFO structure - // from IErrorInfo then we read that information about error call. - // and display that error message to the user. + // from IErrorInfo, then we read that information about error call and display that error message to the user. Assert.Equal("Error From RawErrorInfoUsageTest", ex.Message); } finally diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md index 401897553a4..348d98543e4 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md @@ -1,8 +1,8 @@ Supplementary native parts for WinForms unit tests ================================================== -This project is native DLL which provide unmanaged parts required to testing of WinForms components. Most interesting part of the tests would be COM objects which given to controls. -This project builds as native DLL, which can be used as reg-free COM host. That allow run tests and do not worry about polluting the developer/build machines. +This project is native DLL which provide unmanaged parts required for testing WinForms components. Most interesting part of the tests would be COM objects which given to controls. +This project builds as a native DLL, which can be used as a reg-free COM host. That allow running tests without worrying about "polluting" developer/build machines. Preferably each test case should have their separate COM objects. From 4323c9366e6ecbd182cc1410b295217109eb8232 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 24 Mar 2022 18:10:32 +0600 Subject: [PATCH 19/28] Hide GetErrorInfo overload --- .../src/Interop/OleAut32/Interop.GetErrorInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs index 57648f05da2..010cc14f3fb 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/OleAut32/Interop.GetErrorInfo.cs @@ -9,7 +9,7 @@ internal partial class Interop internal static partial class Oleaut32 { [DllImport(Libraries.Oleaut32, ExactSpelling = true)] - public static extern HRESULT GetErrorInfo(uint dwReserved, out IntPtr pperrinfo); + private static extern HRESULT GetErrorInfo(uint dwReserved, out IntPtr pperrinfo); public static void GetErrorInfo(out WinFormsComWrappers.ErrorInfoWrapper? errinfo) { From 7336ab964aa0969504c297614c344bfad88024f2 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 24 Mar 2022 18:55:25 +0600 Subject: [PATCH 20/28] Add explanation --- .../Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index 539acf78046..93e42a2d891 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -32,8 +32,9 @@ public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() catch (ExternalException ex) { // Most default C++ implementation when Invoke return error code - // consult IErrorInfo object and populate EXCEPINFO structure - // So grid entry knows only about error code. + // implementation consults IErrorInfo object and populates EXCEPINFO structure. + // From EXCEPINFO grid entry reads error code and message. + // IErrorInfo consulted too, but it does not hold error message anymore. Assert.Equal((int)HRESULT.DISP_E_MEMBERNOTFOUND, ex.HResult); Assert.Equal("Error From StandardErrorInfoUsageTest", ex.Message); } From 5d9d3bb0f78dce77835990404f906059484b3a4a Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 24 Mar 2022 19:11:49 +0600 Subject: [PATCH 21/28] Reuse CoCreateInstace from Primitives --- .../Forms/PropertyGrid.IErrorInfoSupportTests.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs index 93e42a2d891..da5f18a37c7 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs @@ -114,9 +114,9 @@ private unsafe void ExecuteWithActivationContext(string applicationManifest, Act private object CreateComObjectWithRawIErrorInfoUsage() { Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); - CoCreateInstance(ref clsidRawErrorInfoUsageTest, + Ole32.CoCreateInstance(ref clsidRawErrorInfoUsageTest, IntPtr.Zero, - 1, + Ole32.CLSCTX.INPROC_SERVER, ref NativeMethods.ActiveX.IID_IUnknown, out object result); return result; @@ -125,22 +125,14 @@ private object CreateComObjectWithRawIErrorInfoUsage() private object CreateComObjectWithStandardIErrorInfoUsage() { Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); - CoCreateInstance(ref clsidStandardErrorInfoUsageTest, + Ole32.CoCreateInstance(ref clsidStandardErrorInfoUsageTest, IntPtr.Zero, - 1, + Ole32.CLSCTX.INPROC_SERVER, ref NativeMethods.ActiveX.IID_IUnknown, out object result); return result; } - [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)] - private static extern void CoCreateInstance( - ref Guid rclsid, - IntPtr punkOuter, - int dwClsContext, - ref Guid riid, - [MarshalAs(UnmanagedType.Interface)] out object ppv); - [DllImport("kernel32", SetLastError = true)] private static extern void ReleaseActCtx(IntPtr hActCtx); } From 05c61f7f69d905ae0dd37510db4477a28fc34abf Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Fri, 1 Apr 2022 13:37:07 +0600 Subject: [PATCH 22/28] Apply PR feedback --- .../src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs | 8 ++++++-- .../tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp | 9 --------- .../tests/UnitTests/WindowsFormsTests/DispatchImpl.h | 6 +++--- .../WindowsFormsTests/RawErrorInfoUsageTest.cpp | 7 ++----- .../UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h | 4 ---- .../WindowsFormsTests/StandardErrorInfoUsageTest.cpp | 7 ++----- .../WindowsFormsTests/StandardErrorInfoUsageTest.h | 4 ---- 7 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs index 44d00ca5b50..5f9c34f2f9b 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs @@ -32,8 +32,12 @@ public bool GetDescription([NotNullWhen(true)] out string? pBstrDescription) IntPtr descriptionPtr; var result = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription */))) (_wrappedInstance, &descriptionPtr); - pBstrDescription = Marshal.PtrToStringUni(descriptionPtr); - Marshal.FreeBSTR(descriptionPtr); + if (result.Succeeded()) + { + pBstrDescription = Marshal.PtrToStringUni(descriptionPtr); + Marshal.FreeBSTR(descriptionPtr); + } + return result.Succeeded(); } } diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp index 1feac6b84eb..d56ebafbd57 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp @@ -23,15 +23,6 @@ DispatchImpl::DispatchImpl(GUID guid, void *instance, const wchar_t* tlb) throw hr; } -DispatchImpl::~DispatchImpl() -{ - if (_typeLib != nullptr) - _typeLib->Release(); - - if (_typeInfo != nullptr) - _typeInfo->Release(); -}; - HRESULT DispatchImpl::DoGetTypeInfoCount(UINT* pctinfo) { *pctinfo = 1; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h index 2a1a613b097..563e621c284 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h @@ -11,7 +11,7 @@ class DispatchImpl : public UnknownImpl { public: DispatchImpl(GUID guid, void *instance, const wchar_t* tlb = nullptr); - virtual ~DispatchImpl(); + virtual ~DispatchImpl() = default; DispatchImpl(const DispatchImpl&) = delete; DispatchImpl& operator=(const DispatchImpl&) = delete; @@ -26,8 +26,8 @@ class DispatchImpl : public UnknownImpl HRESULT DoInvoke(DISPID dispIdMember, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr); private: - ITypeLib *_typeLib; - ITypeInfo *_typeInfo; + ComSmartPtr _typeLib; + ComSmartPtr _typeInfo; void *_instance; }; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp index aaca53024d7..c0e48822396 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp @@ -24,23 +24,20 @@ HRESULT STDMETHODCALLTYPE RawErrorInfoUsageTest::InterfaceSupportsErrorInfo( // By default implementations wrap any error during invoke into DISP_E_EXCEPTION // and consume IErrorInfo. Some implementation behave differently, so this is emulation // of that behaviour. - ICreateErrorInfo* cei; + ComSmartPtr cei; if (SUCCEEDED(::CreateErrorInfo(&cei))) { if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) { if (SUCCEEDED(cei->SetDescription(L"Error From RawErrorInfoUsageTest"))) { - IErrorInfo* errorInfo; + ComSmartPtr errorInfo; if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) { ::SetErrorInfo(0, errorInfo); - errorInfo->Release(); } } } - - cei->Release(); } return S_OK; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h index b3883cf55d8..0cc1aa5924e 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h @@ -16,10 +16,6 @@ class RawErrorInfoUsageTest : public DispatchImpl, public IBasicTest, public ISu { } - ~RawErrorInfoUsageTest() - { - } - public: // IBasicTest virtual HRESULT STDMETHODCALLTYPE get_Int_Property( diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp index 173437ce799..9609f93ab7d 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp @@ -14,23 +14,20 @@ HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::get_Int_Property( HRESULT STDMETHODCALLTYPE StandardErrorInfoUsageTest::put_Int_Property( /* [in] */ int val) { - ICreateErrorInfo* cei; + ComSmartPtr cei; if (SUCCEEDED(::CreateErrorInfo(&cei))) { if (SUCCEEDED(cei->SetGUID(IID_IBasicTest))) { if (SUCCEEDED(cei->SetDescription(L"Error From StandardErrorInfoUsageTest"))) { - IErrorInfo* errorInfo; + ComSmartPtr errorInfo; if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (void**)&errorInfo))) { ::SetErrorInfo(0, errorInfo); - errorInfo->Release(); } } } - - cei->Release(); } return DISP_E_MEMBERNOTFOUND; diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h index 3a5feb63b12..3418d3ee36b 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h +++ b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h @@ -16,10 +16,6 @@ class StandardErrorInfoUsageTest : public DispatchImpl, public IBasicTest, publi { } - ~StandardErrorInfoUsageTest() - { - } - public: // IBasicTest virtual HRESULT STDMETHODCALLTYPE get_Int_Property( From eebb1c6717749effc5c49666fe8f157699024b6f Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Fri, 1 Apr 2022 18:01:59 +0600 Subject: [PATCH 23/28] Bug, stupid bug! --- .../src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs index 5f9c34f2f9b..a88da959b34 100644 --- a/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs +++ b/src/System.Windows.Forms.Primitives/src/Interop/WinFormsComWrappers.ErrorInfoWrapper.cs @@ -37,6 +37,10 @@ public bool GetDescription([NotNullWhen(true)] out string? pBstrDescription) pBstrDescription = Marshal.PtrToStringUni(descriptionPtr); Marshal.FreeBSTR(descriptionPtr); } + else + { + pBstrDescription = null; + } return result.Succeeded(); } From 3e54922f0593a528a490ec0247877ab04f0d2508 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 5 Apr 2022 19:42:43 +0600 Subject: [PATCH 24/28] Move tests to Interop.Tests project --- Winforms.sln | 32 ++-- .../src/Properties/AssemblyInfo.cs | 1 + .../NativeTests}/App.manifest | 4 +- .../InteropTests/NativeTests/CMakeLists.txt | 30 ++++ .../NativeTests}/ComHelpers.h | 0 .../NativeTests}/Contract.idl | 2 +- .../NativeTests}/DispatchImpl.cpp | 2 +- .../NativeTests}/DispatchImpl.h | 0 .../NativeTests/DllGetClassObject.cpp} | 0 .../NativeTests}/Exports.def | 0 .../NativeTests/NativeTests.X.manifest} | 4 +- .../InteropTests/NativeTests/NativeTests.proj | 1 + .../NativeTests}/README.md | 6 +- .../NativeTests}/RawErrorInfoUsageTest.cpp | 0 .../NativeTests}/RawErrorInfoUsageTest.h | 0 .../StandardErrorInfoUsageTest.cpp | 0 .../NativeTests}/StandardErrorInfoUsageTest.h | 0 .../tests/InteropTests/PropertyGridTests.cs | 138 ++++++++++++++++++ .../System.Windows.Forms.Interop.Tests.csproj | 7 + .../System.Windows.Forms.Tests.csproj | 12 -- .../WindowsFormsTests/CMakeLists.txt | 40 ----- .../WindowsFormsTests/WindowsFormsTests.proj | 6 - 22 files changed, 202 insertions(+), 83 deletions(-) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/App.manifest (80%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/ComHelpers.h (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/Contract.idl (94%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/DispatchImpl.cpp (94%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/DispatchImpl.h (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests/IErrorInfoObject.cpp => InteropTests/NativeTests/DllGetClassObject.cpp} (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/Exports.def (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest => InteropTests/NativeTests/NativeTests.X.manifest} (83%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/README.md (75%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/RawErrorInfoUsageTest.cpp (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/RawErrorInfoUsageTest.h (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/StandardErrorInfoUsageTest.cpp (100%) rename src/System.Windows.Forms/tests/{UnitTests/WindowsFormsTests => InteropTests/NativeTests}/StandardErrorInfoUsageTest.h (100%) create mode 100644 src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs delete mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt delete mode 100644 src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj diff --git a/Winforms.sln b/Winforms.sln index 03955d61e00..11a1957c16b 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -193,22 +193,22 @@ Global {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x64.Build.0 = Release|Any CPU {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.ActiveCfg = Release|Any CPU {0D23A41B-2626-4703-9E4A-87C07F69B0B2}.Release|x86.Build.0 = Release|Any CPU - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.ActiveCfg = Release|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.Build.0 = Release|x64 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.ActiveCfg = Release|x86 - {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.Build.0 = Release|x86 + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.ActiveCfg = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|arm64.Build.0 = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x64.Build.0 = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.ActiveCfg = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Debug|x86.Build.0 = Debug|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|Any CPU.Build.0 = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.ActiveCfg = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|arm64.Build.0 = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.ActiveCfg = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x64.Build.0 = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.ActiveCfg = Release|Any CPU + {AB38E262-F206-4820-8F29-23C5F72A4A16}.Release|x86.Build.0 = Release|Any CPU {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|Any CPU.Build.0 = Debug|Any CPU {F977CA8C-FBF9-4D82-80BA-FE5B2F33486E}.Debug|arm64.ActiveCfg = Debug|Any CPU diff --git a/src/System.Windows.Forms/src/Properties/AssemblyInfo.cs b/src/System.Windows.Forms/src/Properties/AssemblyInfo.cs index 464f8bc3936..c8394778a9d 100644 --- a/src/System.Windows.Forms/src/Properties/AssemblyInfo.cs +++ b/src/System.Windows.Forms/src/Properties/AssemblyInfo.cs @@ -8,6 +8,7 @@ [assembly: InternalsVisibleTo("System.Windows.Forms.Tests, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("System.Windows.Forms.Primitives.TestUtilities, PublicKey=00000000000000000400000000000000")] +[assembly: InternalsVisibleTo("System.Windows.Forms.Interop.Tests, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("System.Windows.Forms.UI.IntegrationTests, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("MauiImageListTests, PublicKey=00000000000000000400000000000000")] [assembly: InternalsVisibleTo("MauiListViewTests, PublicKey=00000000000000000400000000000000")] diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest b/src/System.Windows.Forms/tests/InteropTests/NativeTests/App.manifest similarity index 80% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/App.manifest index 1f3263c125d..800dce828cf 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/App.manifest +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/App.manifest @@ -2,7 +2,7 @@ @@ -10,7 +10,7 @@ diff --git a/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt b/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt index 0ad0f58f982..457fb7d11d5 100644 --- a/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt @@ -4,6 +4,24 @@ set(CMAKE_MACOSX_RPATH 1) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Compile IDL file using MIDL +set(IDL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Contract.idl) +get_filename_component(IDL_NAME ${IDL_SOURCE} NAME_WE) + + +FIND_PROGRAM( MIDL midl.exe ) +set(IDL_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Contract) +add_custom_command( + OUTPUT ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h + COMMAND ${MIDL} ${MIDL_INCLUDE_DIRECTORIES} + /h ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h ${MIDL_DEFINITIONS} + /out ${IDL_OUTPUT_DIRECTORY} + /tlb $/NativeTests.tlb + ${IDL_SOURCE} + DEPENDS ${IDL_SOURCE} + COMMENT "Compiling ${IDL_SOURCE}") + +include_directories(${IDL_OUTPUT_DIRECTORY}) # [[! Microsoft.Security.SystemsADM.10086 !]] - SQL required warnings add_compile_options($<$:/W3>) # warning level 3 @@ -15,5 +33,17 @@ add_compile_options($<$:/we4055>) # 'conversion' : from add_library(NativeTests SHARED AccessibleObjectTests.cpp WebBrowserSiteBaseInteropTests.cpp + DllGetClassObject.cpp + DispatchImpl.cpp + StandardErrorInfoUsageTest.cpp + RawErrorInfoUsageTest.cpp + Exports.def + ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c ) +file(GENERATE OUTPUT $/NativeTests.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/NativeTests.X.manifest) +file(GENERATE OUTPUT $/App.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/App.manifest) + install(TARGETS NativeTests) +install(FILES $/NativeTests.tlb TYPE BIN) +install(FILES $/NativeTests.X.manifest TYPE BIN) +install(FILES $/App.manifest TYPE BIN) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h b/src/System.Windows.Forms/tests/InteropTests/NativeTests/ComHelpers.h similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/ComHelpers.h rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/ComHelpers.h diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl b/src/System.Windows.Forms/tests/InteropTests/NativeTests/Contract.idl similarity index 94% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/Contract.idl index a231e1f2a97..168f76d8b13 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Contract.idl +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/Contract.idl @@ -14,7 +14,7 @@ interface IBasicTest : IDispatch [ uuid(0971AD7E-3D4A-4C44-B0A3-A518AC88DFE1) ] -library System_Windows_Forms_NativeTests +library NativeTests { importlib("stdole2.tlb"); diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp b/src/System.Windows.Forms/tests/InteropTests/NativeTests/DispatchImpl.cpp similarity index 94% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/DispatchImpl.cpp index d56ebafbd57..0e4a167a471 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.cpp +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/DispatchImpl.cpp @@ -5,7 +5,7 @@ namespace { - const wchar_t* s_tlbDefault = L"System_Windows_Forms_NativeTests.tlb"; + const wchar_t* s_tlbDefault = L"NativeTests.tlb"; } DispatchImpl::DispatchImpl(GUID guid, void *instance, const wchar_t* tlb) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h b/src/System.Windows.Forms/tests/InteropTests/NativeTests/DispatchImpl.h similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/DispatchImpl.h rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/DispatchImpl.h diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp b/src/System.Windows.Forms/tests/InteropTests/NativeTests/DllGetClassObject.cpp similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/IErrorInfoObject.cpp rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/DllGetClassObject.cpp diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def b/src/System.Windows.Forms/tests/InteropTests/NativeTests/Exports.def similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/Exports.def rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/Exports.def diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest b/src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.X.manifest similarity index 83% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.X.manifest index bec90eea888..5fdd87552cb 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/System_Windows_Forms_NativeTests.X.manifest +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.X.manifest @@ -3,10 +3,10 @@ - + + call "$(RepoRoot)eng\init-vs-env.cmd" $(TargetArchitecture) CMakeLists.txt diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md b/src/System.Windows.Forms/tests/InteropTests/NativeTests/README.md similarity index 75% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/README.md index 348d98543e4..631085a9090 100644 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/README.md +++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/README.md @@ -1,4 +1,4 @@ -Supplementary native parts for WinForms unit tests +Supplementary native parts for WinForms Interop tests ================================================== This project is native DLL which provide unmanaged parts required for testing WinForms components. Most interesting part of the tests would be COM objects which given to controls. @@ -9,10 +9,10 @@ Preferably each test case should have their separate COM objects. Manifests used in the application: - `App.manifest` This file used during running test to provide information about this DLL. Developers do not need to modify that file, just run it inside activation context. -- `System_Windows_Forms_NativeTests.X.manifest` This is manifest where all reg-free COM objects registered. +- `NativeTests.X.manifest` This is manifest where all reg-free COM objects registered. ## How to add new COM object 1. Declare COM object in `Contract.idl` file 2. Implement COM object in C++. -3. Add CoClass defined in `Contract.idl` inside `System_Windows_Forms_NativeTests.X.manifest` +3. Add CoClass defined in `Contract.idl` inside `NativeTests.X.manifest` diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/InteropTests/NativeTests/RawErrorInfoUsageTest.cpp similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.cpp rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/RawErrorInfoUsageTest.cpp diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/InteropTests/NativeTests/RawErrorInfoUsageTest.h similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/RawErrorInfoUsageTest.h rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/RawErrorInfoUsageTest.h diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp b/src/System.Windows.Forms/tests/InteropTests/NativeTests/StandardErrorInfoUsageTest.cpp similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.cpp rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/StandardErrorInfoUsageTest.cpp diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h b/src/System.Windows.Forms/tests/InteropTests/NativeTests/StandardErrorInfoUsageTest.h similarity index 100% rename from src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/StandardErrorInfoUsageTest.h rename to src/System.Windows.Forms/tests/InteropTests/NativeTests/StandardErrorInfoUsageTest.h diff --git a/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs b/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs new file mode 100644 index 00000000000..87842ff3e4f --- /dev/null +++ b/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs @@ -0,0 +1,138 @@ +// 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.ComponentModel; +using System.Runtime.InteropServices; +using Xunit; +using static Interop; + +namespace System.Windows.Forms.Interop.Tests; + +[Collection("Sequential")] +public class PropertyGridTests +{ + [Fact] + public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() + { + ExecuteWithActivationContext( + "App.manifest", + () => + { + using PropertyGrid propertyGrid = new PropertyGrid(); + var target = CreateComObjectWithStandardIErrorInfoUsage(); + propertyGrid.SelectedObject = target; + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + try + { + encodingEntry.SetPropertyTextValue("333"); + Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); + } + catch (ExternalException ex) + { + // Most default C++ implementation when Invoke return error code + // implementation consults IErrorInfo object and populates EXCEPINFO structure. + // From EXCEPINFO grid entry reads error code and message. + // IErrorInfo consulted too, but it does not hold error message anymore. + Assert.Equal((int)HRESULT.DISP_E_MEMBERNOTFOUND, ex.HResult); + Assert.Equal("Error From StandardErrorInfoUsageTest", ex.Message); + } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } + }); + } + + [Fact] + public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() + { + ExecuteWithActivationContext( + "App.manifest", + () => + { + using PropertyGrid propertyGrid = new PropertyGrid(); + var target = CreateComObjectWithRawIErrorInfoUsage(); + propertyGrid.SelectedObject = target; + var entries = propertyGrid.GetCurrentEntries(); + var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); + try + { + encodingEntry.SetPropertyTextValue("123"); + Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); + } + catch (ExternalException ex) + { + // If C++ implementation of Invoke did not populate EXCEPINFO structure + // from IErrorInfo, then we read that information about error call and display that error message to the user. + Assert.Equal("Error From RawErrorInfoUsageTest", ex.Message); + } + finally + { + propertyGrid.SelectedObject = null; + Marshal.ReleaseComObject(target); + } + }); + } + + private unsafe void ExecuteWithActivationContext(string applicationManifest, Action action) + { + var context = new Kernel32.ACTCTXW(); + IntPtr handle; + fixed (char* p = applicationManifest) + { + context.cbSize = (uint)sizeof(Kernel32.ACTCTXW); + context.lpSource = p; + + handle = Kernel32.CreateActCtxW(ref context); + } + + if (handle == IntPtr.Zero) + throw new Win32Exception(); + try + { + if (Kernel32.ActivateActCtx(handle, out var cookie).IsFalse()) + throw new Win32Exception(); + try + { + action(); + } + finally + { + if (Kernel32.DeactivateActCtx(0, cookie).IsFalse()) + throw new Win32Exception(); + } + } + finally + { + ReleaseActCtx(handle); + } + } + + private object CreateComObjectWithRawIErrorInfoUsage() + { + Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); + Ole32.CoCreateInstance(ref clsidRawErrorInfoUsageTest, + IntPtr.Zero, + Ole32.CLSCTX.INPROC_SERVER, + ref NativeMethods.ActiveX.IID_IUnknown, + out object result); + return result; + } + + private object CreateComObjectWithStandardIErrorInfoUsage() + { + Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); + Ole32.CoCreateInstance(ref clsidStandardErrorInfoUsageTest, + IntPtr.Zero, + Ole32.CLSCTX.INPROC_SERVER, + ref NativeMethods.ActiveX.IID_IUnknown, + out object result); + return result; + } + + [DllImport("kernel32", SetLastError = true)] + private static extern void ReleaseActCtx(IntPtr hActCtx); +} diff --git a/src/System.Windows.Forms/tests/InteropTests/System.Windows.Forms.Interop.Tests.csproj b/src/System.Windows.Forms/tests/InteropTests/System.Windows.Forms.Interop.Tests.csproj index d5e5f680164..da36d96df85 100644 --- a/src/System.Windows.Forms/tests/InteropTests/System.Windows.Forms.Interop.Tests.csproj +++ b/src/System.Windows.Forms/tests/InteropTests/System.Windows.Forms.Interop.Tests.csproj @@ -23,4 +23,11 @@ + + + + + + + diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index f676be7184d..e42d4fd1537 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -2,7 +2,6 @@ System.Windows.Forms.Tests - x86;x64 true @@ -27,10 +26,6 @@ - - - - @@ -54,11 +49,4 @@ - - - - - - - diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt deleted file mode 100644 index da710601d98..00000000000 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required (VERSION 2.8.12) -project (System_Windows_Forms_NativeTests) -set(CMAKE_MACOSX_RPATH 1) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Compile IDL file using MIDL -set(IDL_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Contract.idl) -get_filename_component(IDL_NAME ${IDL_SOURCE} NAME_WE) - -FIND_PROGRAM( MIDL midl.exe ) -set(IDL_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Contract) -add_custom_command( - OUTPUT ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h - COMMAND ${MIDL} ${MIDL_INCLUDE_DIRECTORIES} - /h ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}.h ${MIDL_DEFINITIONS} - /out ${IDL_OUTPUT_DIRECTORY} - /tlb $/System_Windows_Forms_NativeTests.tlb - ${IDL_SOURCE} - DEPENDS ${IDL_SOURCE} - COMMENT "Compiling ${IDL_SOURCE}") - -include_directories(${IDL_OUTPUT_DIRECTORY}) - -add_library(System_Windows_Forms_NativeTests SHARED - IErrorInfoObject.cpp - DispatchImpl.cpp - StandardErrorInfoUsageTest.cpp - RawErrorInfoUsageTest.cpp - Exports.def - ${IDL_OUTPUT_DIRECTORY}/${IDL_NAME}_i.c -) - -file(GENERATE OUTPUT $/System_Windows_Forms_NativeTests.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/System_Windows_Forms_NativeTests.X.manifest) -file(GENERATE OUTPUT $/App.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/App.manifest) - -install(TARGETS System_Windows_Forms_NativeTests) -install(FILES $/System_Windows_Forms_NativeTests.tlb TYPE BIN) -install(FILES $/System_Windows_Forms_NativeTests.X.manifest TYPE BIN) -install(FILES $/App.manifest TYPE BIN) diff --git a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj b/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj deleted file mode 100644 index 8b5b6b4132c..00000000000 --- a/src/System.Windows.Forms/tests/UnitTests/WindowsFormsTests/WindowsFormsTests.proj +++ /dev/null @@ -1,6 +0,0 @@ - - - call "$(RepoRoot)eng\init-vs-env.cmd" $(TargetArchitecture) - CMakeLists.txt - - From 71d038ec44886767693a64392650f4a2d5cabc59 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 5 Apr 2022 19:43:34 +0600 Subject: [PATCH 25/28] Remove not needed file --- .../PropertyGrid.IErrorInfoSupportTests.cs | 139 ------------------ 1 file changed, 139 deletions(-) delete mode 100644 src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs deleted file mode 100644 index da5f18a37c7..00000000000 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/PropertyGrid.IErrorInfoSupportTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -// 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.ComponentModel; -using System.Runtime.InteropServices; -using Xunit; -using static Interop; - -namespace System.Windows.Forms.Tests -{ - [Collection("Sequential")] - public class PropertyGrid_IErrorInfoSupportTests - { - [Fact] - public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() - { - ExecuteWithActivationContext( - "App.manifest", - () => - { - using PropertyGrid propertyGrid = new PropertyGrid(); - var target = CreateComObjectWithStandardIErrorInfoUsage(); - propertyGrid.SelectedObject = target; - var entries = propertyGrid.GetCurrentEntries(); - var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); - try - { - encodingEntry.SetPropertyTextValue("333"); - Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); - } - catch (ExternalException ex) - { - // Most default C++ implementation when Invoke return error code - // implementation consults IErrorInfo object and populates EXCEPINFO structure. - // From EXCEPINFO grid entry reads error code and message. - // IErrorInfo consulted too, but it does not hold error message anymore. - Assert.Equal((int)HRESULT.DISP_E_MEMBERNOTFOUND, ex.HResult); - Assert.Equal("Error From StandardErrorInfoUsageTest", ex.Message); - } - finally - { - propertyGrid.SelectedObject = null; - Marshal.ReleaseComObject(target); - } - }); - } - - [Fact] - public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() - { - ExecuteWithActivationContext( - "App.manifest", - () => - { - using PropertyGrid propertyGrid = new PropertyGrid(); - var target = CreateComObjectWithRawIErrorInfoUsage(); - propertyGrid.SelectedObject = target; - var entries = propertyGrid.GetCurrentEntries(); - var encodingEntry = entries[0].Children.First(_ => _.PropertyName == "Int_Property"); - try - { - encodingEntry.SetPropertyTextValue("123"); - Assert.False(true, "Invalid set values should produce ExternalException which will be presenttted to the user."); - } - catch (ExternalException ex) - { - // If C++ implementation of Invoke did not populate EXCEPINFO structure - // from IErrorInfo, then we read that information about error call and display that error message to the user. - Assert.Equal("Error From RawErrorInfoUsageTest", ex.Message); - } - finally - { - propertyGrid.SelectedObject = null; - Marshal.ReleaseComObject(target); - } - }); - } - - private unsafe void ExecuteWithActivationContext(string applicationManifest, Action action) - { - var context = new Kernel32.ACTCTXW(); - IntPtr handle; - fixed (char* p = applicationManifest) - { - context.cbSize = (uint)sizeof(Kernel32.ACTCTXW); - context.lpSource = p; - - handle = Kernel32.CreateActCtxW(ref context); - } - - if (handle == IntPtr.Zero) - throw new Win32Exception(); - try - { - if (Kernel32.ActivateActCtx(handle, out var cookie).IsFalse()) - throw new Win32Exception(); - try - { - action(); - } - finally - { - if (Kernel32.DeactivateActCtx(0, cookie).IsFalse()) - throw new Win32Exception(); - } - } - finally - { - ReleaseActCtx(handle); - } - } - - private object CreateComObjectWithRawIErrorInfoUsage() - { - Guid clsidRawErrorInfoUsageTest = new("0ED8EE0D-22E3-49EA-850C-E69B20D1F296"); - Ole32.CoCreateInstance(ref clsidRawErrorInfoUsageTest, - IntPtr.Zero, - Ole32.CLSCTX.INPROC_SERVER, - ref NativeMethods.ActiveX.IID_IUnknown, - out object result); - return result; - } - - private object CreateComObjectWithStandardIErrorInfoUsage() - { - Guid clsidStandardErrorInfoUsageTest = new("EA1FCB3A-277C-4C79-AB85-E2ED3E858201"); - Ole32.CoCreateInstance(ref clsidStandardErrorInfoUsageTest, - IntPtr.Zero, - Ole32.CLSCTX.INPROC_SERVER, - ref NativeMethods.ActiveX.IID_IUnknown, - out object result); - return result; - } - - [DllImport("kernel32", SetLastError = true)] - private static extern void ReleaseActCtx(IntPtr hActCtx); - } -} From 4c33480b08d05faf1ff41f645671f38296eb7a0c Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 5 Apr 2022 19:44:11 +0600 Subject: [PATCH 26/28] Remove not used Import --- .../tests/UnitTests/System.Windows.Forms.Tests.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj index e42d4fd1537..a879f7c77fe 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj +++ b/src/System.Windows.Forms/tests/UnitTests/System.Windows.Forms.Tests.csproj @@ -9,8 +9,6 @@ $(NoWarn),1573,1591,1712,WFDEV001 - - From f4a54d08ab3e7ce5d0301358e6e270bd980bec9d Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 5 Apr 2022 19:45:38 +0600 Subject: [PATCH 27/28] Remove leftovers --- Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 73b3f954737..cdb7a8fbb7c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -2,7 +2,7 @@ - + From b1db619107a252be00fbfa62e2cae5f8adebae21 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Thu, 7 Apr 2022 08:29:35 +0600 Subject: [PATCH 28/28] Format code --- .../tests/InteropTests/PropertyGridTests.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs b/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs index 87842ff3e4f..1f412edaa31 100644 --- a/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs +++ b/src/System.Windows.Forms/tests/InteropTests/PropertyGridTests.cs @@ -31,10 +31,10 @@ public void ISupportErrorInfo_Supported_ButNoIErrorInfoGiven() } catch (ExternalException ex) { - // Most default C++ implementation when Invoke return error code - // implementation consults IErrorInfo object and populates EXCEPINFO structure. - // From EXCEPINFO grid entry reads error code and message. - // IErrorInfo consulted too, but it does not hold error message anymore. + // Most default C++ implementation when Invoke return error code + // implementation consults IErrorInfo object and populates EXCEPINFO structure. + // From EXCEPINFO grid entry reads error code and message. + // IErrorInfo consulted too, but it does not hold error message anymore. Assert.Equal((int)HRESULT.DISP_E_MEMBERNOTFOUND, ex.HResult); Assert.Equal("Error From StandardErrorInfoUsageTest", ex.Message); } @@ -65,8 +65,8 @@ public void ISupportErrorInfo_Supported_WithIErrorInfoGiven() } catch (ExternalException ex) { - // If C++ implementation of Invoke did not populate EXCEPINFO structure - // from IErrorInfo, then we read that information about error call and display that error message to the user. + // If C++ implementation of Invoke did not populate EXCEPINFO structure + // from IErrorInfo, then we read that information about error call and display that error message to the user. Assert.Equal("Error From RawErrorInfoUsageTest", ex.Message); } finally @@ -90,11 +90,17 @@ private unsafe void ExecuteWithActivationContext(string applicationManifest, Act } if (handle == IntPtr.Zero) + { throw new Win32Exception(); + } + try { if (Kernel32.ActivateActCtx(handle, out var cookie).IsFalse()) + { throw new Win32Exception(); + } + try { action(); @@ -102,7 +108,9 @@ private unsafe void ExecuteWithActivationContext(string applicationManifest, Act finally { if (Kernel32.DeactivateActCtx(0, cookie).IsFalse()) + { throw new Win32Exception(); + } } } finally