diff --git a/Winforms.sln b/Winforms.sln
index 5c8f72858c..0182c7e40a 100644
--- a/Winforms.sln
+++ b/Winforms.sln
@@ -135,6 +135,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.TestUt
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiToolStripTests", "src\System.Windows.Forms\tests\IntegrationTests\MauiTests\MauiToolStripTests\MauiToolStripTests.csproj", "{83634671-CF3A-43B0-B729-42CCBA62DF2C}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Interop.Tests", "src\System.Windows.Forms\tests\InteropTests\System.Windows.Forms.Interop.Tests.csproj", "{C272DA06-B98D-4BB7-B1C4-ECF58F54B224}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -277,6 +279,18 @@ Global
{83634671-CF3A-43B0-B729-42CCBA62DF2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83634671-CF3A-43B0-B729-42CCBA62DF2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83634671-CF3A-43B0-B729-42CCBA62DF2C}.Release|Any CPU.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}.Release|Any CPU.ActiveCfg = Release|x86
+ {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.Build.0 = Release|x86
+ {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|x86.ActiveCfg = Release|x86
+ {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x86.Build.0 = Release|x86
+ {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}.Release|x64.ActiveCfg = Release|x64
+ {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -321,6 +335,7 @@ Global
{73B0857A-966B-4E7D-8A83-FECFE0281AB9} = {DF68A171-D27B-4E6A-8A7E-63A651622355}
{86418F0B-39DC-4B5A-8145-6D607E6150AC} = {DF68A171-D27B-4E6A-8A7E-63A651622355}
{83634671-CF3A-43B0-B729-42CCBA62DF2C} = {8F20A905-BD37-4D80-B8DF-FA45276FC23F}
+ {C272DA06-B98D-4BB7-B1C4-ECF58F54B224} = {583F1292-AE8D-4511-B8D8-A81FE4642DDC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7B1B0433-F612-4E5A-BE7E-FCF5B9F6E136}
diff --git a/eng/configure-toolset.ps1 b/eng/configure-toolset.ps1
index b22416ff0f..a6048f7e63 100644
--- a/eng/configure-toolset.ps1
+++ b/eng/configure-toolset.ps1
@@ -1,2 +1,5 @@
$script:DoNotAbortNativeToolsInstallationOnFailure = $true
$script:DoNotDisplayNativeToolsInstallationWarnings = $true
+
+# Add CMake to path.
+$env:PATH = "$PSScriptRoot\..\.tools\bin;$env:PATH"
diff --git a/global.json b/global.json
index 5543137369..7ad9e7c39b 100644
--- a/global.json
+++ b/global.json
@@ -21,7 +21,7 @@
"Microsoft.NET.Sdk.IL": "5.0.0-rc.1.20371.13"
},
"native-tools": {
- "cmake": "3.14.2",
+ "cmake": "3.17.3",
"dotnet-api-docs_netcoreapp3.0": "0.0.0.1"
}
}
diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleClientSite.cs b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleClientSite.cs
index 8c6c18a5a7..140a7ccc1d 100644
--- a/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleClientSite.cs
+++ b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleClientSite.cs
@@ -23,9 +23,7 @@ internal partial class Interop
Ole32.OLEWHICHMK dwWhichMoniker,
IntPtr* ppmk);
- [PreserveSig]
- HRESULT GetContainer(
- out IOleContainer container);
+ IOleContainer GetContainer();
[PreserveSig]
HRESULT ShowObject();
diff --git a/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleControlSite.cs b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleControlSite.cs
index f8a8666738..6a1ee61894 100644
--- a/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleControlSite.cs
+++ b/src/System.Windows.Forms.Primitives/src/Interop/Ole32/Interop.IOleControlSite.cs
@@ -24,7 +24,7 @@ internal partial class Interop
[PreserveSig]
HRESULT GetExtendedControl(
- [MarshalAs(UnmanagedType.IDispatch)] out object ppDisp);
+ IntPtr* ppDisp);
[PreserveSig]
HRESULT TransformCoords(
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/AxHost.OleInterfaces.cs b/src/System.Windows.Forms/src/System/Windows/Forms/AxHost.OleInterfaces.cs
index 1fed41be82..08e442f508 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/AxHost.OleInterfaces.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/AxHost.OleInterfaces.cs
@@ -250,15 +250,21 @@ namespace System.Windows.Forms
return HRESULT.E_NOTIMPL;
}
- HRESULT IOleControlSite.GetExtendedControl(out object ppDisp)
+ unsafe HRESULT IOleControlSite.GetExtendedControl(IntPtr* ppDisp)
{
Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in GetExtendedControl " + host.ToString());
- ppDisp = host.GetParentContainer().GetProxyForControl(host);
if (ppDisp == null)
+ {
+ return HRESULT.E_POINTER;
+ }
+
+ object proxy = host.GetParentContainer().GetProxyForControl(host);
+ if (proxy == null)
{
return HRESULT.E_NOTIMPL;
}
+ *ppDisp = Marshal.GetIDispatchForObject(proxy);
return HRESULT.S_OK;
}
@@ -374,11 +380,10 @@ namespace System.Windows.Forms
return HRESULT.E_NOTIMPL;
}
- HRESULT IOleClientSite.GetContainer(out IOleContainer container)
+ IOleContainer IOleClientSite.GetContainer()
{
Debug.WriteLineIf(AxHTraceSwitch.TraceVerbose, "in getContainer");
- container = host.GetParentContainer();
- return HRESULT.S_OK;
+ return host.GetParentContainer();
}
unsafe HRESULT IOleClientSite.ShowObject()
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.AxSourcingSite.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.AxSourcingSite.cs
index ca19341f3b..9cca9f0490 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.AxSourcingSite.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.AxSourcingSite.cs
@@ -38,11 +38,10 @@ namespace System.Windows.Forms
{
if (service == typeof(HtmlDocument))
{
- HRESULT hr = _clientSite.GetContainer(out Ole32.IOleContainer iOlecontainer);
- if (hr.Succeeded() && iOlecontainer is Mshtml.IHTMLDocument)
+ if (_clientSite.GetContainer() is Mshtml.IHTMLDocument document)
{
_shimManager ??= new HtmlShimManager();
- return new HtmlDocument(_shimManager, iOlecontainer as Mshtml.IHTMLDocument);
+ return new HtmlDocument(_shimManager, document);
}
}
else if (_clientSite.GetType().IsAssignableFrom(service))
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserSiteBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserSiteBase.cs
index 3e1fae164f..2548229e4d 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserSiteBase.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/WebBrowserSiteBase.cs
@@ -88,9 +88,14 @@ namespace System.Windows.Forms
return HRESULT.E_NOTIMPL;
}
- HRESULT IOleControlSite.GetExtendedControl(out object ppDisp)
+ unsafe HRESULT IOleControlSite.GetExtendedControl(IntPtr* ppDisp)
{
- ppDisp = null;
+ if (ppDisp == null)
+ {
+ return HRESULT.E_POINTER;
+ }
+
+ *ppDisp = IntPtr.Zero;
return HRESULT.E_NOTIMPL;
}
@@ -98,7 +103,7 @@ namespace System.Windows.Forms
{
if (pPtlHimetric == null || pPtfContainer == null)
{
- return HRESULT.E_INVALIDARG;
+ return HRESULT.E_POINTER;
}
if ((dwFlags & XFORMCOORDS.HIMETRICTOCONTAINER) != 0)
@@ -183,10 +188,9 @@ namespace System.Windows.Forms
return HRESULT.E_NOTIMPL;
}
- HRESULT IOleClientSite.GetContainer(out IOleContainer container)
+ IOleContainer IOleClientSite.GetContainer()
{
- container = Host.GetParentContainer();
- return HRESULT.S_OK;
+ return Host.GetParentContainer();
}
unsafe HRESULT IOleClientSite.ShowObject()
diff --git a/src/System.Windows.Forms/tests/InteropTests/InteropTestBase.cs b/src/System.Windows.Forms/tests/InteropTests/InteropTestBase.cs
new file mode 100644
index 0000000000..34d58ed984
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/InteropTestBase.cs
@@ -0,0 +1,22 @@
+// 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 Xunit.Sdk;
+
+namespace System.Windows.Forms.InteropTests
+{
+ public abstract class InteropTestBase
+ {
+ public const string NativeTests = "NativeTests";
+ public const string Success = "Success";
+
+ protected static void AssertSuccess(string result)
+ {
+ if (result != Success)
+ {
+ throw new XunitException(result);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt b/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt
new file mode 100644
index 0000000000..314823d606
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required (VERSION 2.6)
+project (NativeTests)
+set(CMAKE_MACOSX_RPATH 1)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+add_library(NativeTests SHARED
+ WebBrowserSiteBaseInteropTests.cpp
+)
+install(TARGETS NativeTests)
diff --git a/src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.proj b/src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.proj
new file mode 100644
index 0000000000..fe624ca578
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/NativeTests.proj
@@ -0,0 +1,5 @@
+
+
+ CMakeLists.txt
+
+
diff --git a/src/System.Windows.Forms/tests/InteropTests/NativeTests/WebBrowserSiteBaseInteropTests.cpp b/src/System.Windows.Forms/tests/InteropTests/NativeTests/WebBrowserSiteBaseInteropTests.cpp
new file mode 100644
index 0000000000..194f6d134f
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/WebBrowserSiteBaseInteropTests.cpp
@@ -0,0 +1,635 @@
+// 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 "testhelpers.h"
+#include
+#include
+using namespace ATL;
+
+static HRESULT Test_IOleControlSite_OnControlInfoChanged(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pControlSite->OnControlInfoChanged();
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_LockInPlaceActive(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ // Negative tests.
+ hr = pControlSite->LockInPlaceActive(TRUE);
+ assertEqualHr(E_NOTIMPL, hr);
+
+ hr = pControlSite->LockInPlaceActive(TRUE);
+ assertEqualHr(E_NOTIMPL, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_GetExtendedControl(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ IDispatch* pDisp;
+
+ pDisp = (IDispatch*)(long)0xdeadbeef;
+ hr = pControlSite->GetExtendedControl(&pDisp);
+ assertEqualHr(E_NOTIMPL, hr);
+ assertNull(pDisp);
+
+ // Negative tests.
+ hr = pControlSite->GetExtendedControl(NULL);
+ assertEqualHr(E_POINTER, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_TransformCoords(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ POINTL ptlHimetric;
+ POINTF ptfContainer;
+
+ // HIMETRICTOCONTAINER, SIZE
+ ptlHimetric.x = 1000;
+ ptlHimetric.y = 2000;
+ ptfContainer.x = 0;
+ ptfContainer.y = 0;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_SIZE);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(1000, ptlHimetric.x);
+ assertEqualInt(2000, ptlHimetric.y);
+ assertEqualFloat(38, ptfContainer.x);
+ assertEqualFloat(76, ptfContainer.y);
+
+ ptlHimetric.x = 0;
+ ptlHimetric.y = 0;
+ ptfContainer.x = -1;
+ ptfContainer.y = -2;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_SIZE);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(0, ptlHimetric.x);
+ assertEqualInt(0, ptlHimetric.y);
+ assertEqualFloat(0, ptfContainer.x);
+ assertEqualFloat(0, ptfContainer.y);
+
+ // HIMETRICTOCONTAINER, POSITION
+ ptlHimetric.x = 1000;
+ ptlHimetric.y = 2000;
+ ptfContainer.x = 0;
+ ptfContainer.y = 0;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_POSITION);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(1000, ptlHimetric.x);
+ assertEqualInt(2000, ptlHimetric.y);
+ assertEqualFloat(38, ptfContainer.x);
+ assertEqualFloat(76, ptfContainer.y);
+
+ ptlHimetric.x = 0;
+ ptlHimetric.y = 0;
+ ptfContainer.x = 1;
+ ptfContainer.y = 2;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_POSITION);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(0, ptlHimetric.x);
+ assertEqualInt(0, ptlHimetric.y);
+ assertEqualFloat(0, ptfContainer.x);
+ assertEqualFloat(0, ptfContainer.y);
+
+ // CONTAINERTOHIMETRIC, SIZE
+ ptlHimetric.x = 0;
+ ptlHimetric.y = 0;
+ ptfContainer.x = 38;
+ ptfContainer.y = 76;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_CONTAINERTOHIMETRIC | XFORMCOORDS_SIZE);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(1005, ptlHimetric.x);
+ assertEqualInt(2011, ptlHimetric.y);
+ assertEqualFloat(38, ptfContainer.x);
+ assertEqualFloat(76, ptfContainer.y);
+
+ ptlHimetric.x = 1;
+ ptlHimetric.y = 2;
+ ptfContainer.x = 0;
+ ptfContainer.y = 0;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_CONTAINERTOHIMETRIC | XFORMCOORDS_SIZE);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(0, ptlHimetric.x);
+ assertEqualInt(0, ptlHimetric.y);
+ assertEqualFloat(0, ptfContainer.x);
+ assertEqualFloat(0, ptfContainer.y);
+
+ // CONTAINERTOHIMETRIC, POSITION
+ ptlHimetric.x = 0;
+ ptlHimetric.y = 0;
+ ptfContainer.x = 38;
+ ptfContainer.y = 76;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_CONTAINERTOHIMETRIC | XFORMCOORDS_POSITION);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(1005, ptlHimetric.x);
+ assertEqualInt(2011, ptlHimetric.y);
+ assertEqualFloat(38, ptfContainer.x);
+ assertEqualFloat(76, ptfContainer.y);
+
+ ptlHimetric.x = 1;
+ ptlHimetric.y = 2;
+ ptfContainer.x = 0;
+ ptfContainer.y = 0;
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_CONTAINERTOHIMETRIC | XFORMCOORDS_POSITION);
+ assertEqualHr(S_OK, hr);
+ assertEqualInt(0, ptlHimetric.x);
+ assertEqualInt(0, ptlHimetric.y);
+ assertEqualFloat(0, ptfContainer.x);
+ assertEqualFloat(0, ptfContainer.y);
+
+ // Negative tests.
+ hr = pControlSite->TransformCoords(NULL, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_SIZE);
+ assertEqualHr(E_POINTER, hr);
+
+ hr = pControlSite->TransformCoords(&ptlHimetric, NULL, XFORMCOORDS_HIMETRICTOCONTAINER | XFORMCOORDS_SIZE);
+ assertEqualHr(E_POINTER, hr);
+
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_HIMETRICTOCONTAINER);
+ assertEqualHr(E_INVALIDARG, hr);
+
+ hr = pControlSite->TransformCoords(&ptlHimetric, &ptfContainer, XFORMCOORDS_CONTAINERTOHIMETRIC);
+ assertEqualHr(E_INVALIDARG, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_TranslateAccelerator(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ MSG msg = { 0 };
+
+ hr = pControlSite->TranslateAccelerator(&msg, 0);
+ assertEqualHr(S_FALSE, hr);
+
+ // Negative tests.
+ hr = pControlSite->TranslateAccelerator(NULL, 0);
+ assertEqualHr(E_POINTER, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_OnFocus(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pControlSite->OnFocus(TRUE);
+ assertEqualHr(S_OK, hr);
+
+ hr = pControlSite->OnFocus(TRUE);
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite_ShowPropertyFrame(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ // Negative tests.
+ hr = pControlSite->ShowPropertyFrame();
+ assertEqualHr(E_NOTIMPL, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleControlSite(IOleControlSite* pControlSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = Test_IOleControlSite_OnControlInfoChanged(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_GetExtendedControl(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_LockInPlaceActive(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_TransformCoords(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_TranslateAccelerator(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_OnFocus(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleControlSite_ShowPropertyFrame(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+static HRESULT Test_ISimpleFrameSite_PreMessageFilter(ISimpleFrameSite* pSimpleFrameSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pSimpleFrameSite->PreMessageFilter(NULL, 0, NULL, NULL, NULL, NULL);
+ assertEqualHr(S_OK, hr);
+
+ hr = pSimpleFrameSite->PreMessageFilter((HWND)1, 1, (WPARAM)1, (LPARAM)1, (LRESULT*)1, (DWORD*)1);
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_ISimpleFrameSite_PostMessageFilter(ISimpleFrameSite* pSimpleFrameSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pSimpleFrameSite->PostMessageFilter(NULL, 0, NULL, NULL, NULL, 0);
+ assertEqualHr(S_FALSE, hr);
+
+ hr = pSimpleFrameSite->PostMessageFilter((HWND)1, 1, (WPARAM)1, (LPARAM)1, (LRESULT*)1, 1);
+ assertEqualHr(S_FALSE, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_ISimpleFrameSite(ISimpleFrameSite* pSimpleFrameSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = Test_ISimpleFrameSite_PreMessageFilter(pSimpleFrameSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_ISimpleFrameSite_PostMessageFilter(pSimpleFrameSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_SaveObject(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ // Negative tests.
+ hr = pClientSite->SaveObject();
+ assertEqualHr(E_NOTIMPL, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_GetMoniker(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ IMoniker* pmk;
+
+ // Negative tests.
+ pmk = (IMoniker*)(long)0xdeadbeef;
+ hr = pClientSite->GetMoniker(0, 0, &pmk);
+ assertEqualHr(E_NOTIMPL, hr);
+ assertNull(pmk);
+
+ hr = pClientSite->GetMoniker(0, 0, NULL);
+ assertEqualHr(E_POINTER, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_GetContainer(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ IOleContainer* pContainer;
+
+ pContainer = (IOleContainer*)(long)0xdeadbeef;
+ hr = pClientSite->GetContainer(&pContainer);
+ assertEqualHr(S_OK, hr);
+ assertNotNull(pContainer);
+
+#if false
+ // Negative tests.
+ hr = pClientSite->GetContainer(NULL);
+ assertEqualHr(E_POINTER, hr);
+#endif
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_ShowObject(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pClientSite->ShowObject();
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_OnShowWindow(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pClientSite->OnShowWindow(TRUE);
+ assertEqualHr(S_OK, hr);
+
+ hr = pClientSite->OnShowWindow(FALSE);
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite_RequestNewObjectLayout(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ // Negative tests.
+ hr = pClientSite->RequestNewObjectLayout();
+ assertEqualHr(E_NOTIMPL, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleClientSite(IOleClientSite* pClientSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = Test_IOleClientSite_SaveObject(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleClientSite_GetMoniker(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleClientSite_GetContainer(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleClientSite_ShowObject(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleClientSite_OnShowWindow(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleClientSite_RequestNewObjectLayout(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_GetWindow(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ HWND hwnd;
+
+ hwnd = NULL;
+ hr = pInPlaceSite->GetWindow(&hwnd);
+ assertEqualHr(S_OK, hr);
+ assertNotNull(hwnd);
+
+ // Negative tests.
+ hr = pInPlaceSite->GetWindow(NULL);
+ assertEqualHr(E_POINTER, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_ContextSensitiveHelp(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ // Negative tests.
+ hr = pInPlaceSite->ContextSensitiveHelp(TRUE);
+ assertEqualHr(E_NOTIMPL, hr);
+
+ hr = pInPlaceSite->ContextSensitiveHelp(FALSE);
+ assertEqualHr(E_NOTIMPL, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_CanInPlaceActivate(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pInPlaceSite->CanInPlaceActivate();
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_OnInPlaceActivate(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pInPlaceSite->OnInPlaceActivate();
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_OnUIActivate(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = pInPlaceSite->OnUIActivate();
+ assertEqualHr(S_OK, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite_GetWindowContext(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+ IOleInPlaceFrame* pFrame;
+ IOleInPlaceUIWindow* pDoc;
+ RECT posRect;
+ RECT clipRect;
+ OLEINPLACEFRAMEINFO frameInfo;
+
+ pFrame = NULL;
+ pDoc = NULL;
+ posRect = { 0 };
+ clipRect = { 0 };
+ frameInfo = { 0 };
+ hr = pInPlaceSite->GetWindowContext(&pFrame, &pDoc, &posRect, &clipRect, NULL);
+ assertEqualHr(S_OK, hr);
+ assertNotNull(pFrame);
+ assertNull(pDoc);
+ assertEqualInt(0, posRect.left);
+ assertEqualInt(0, posRect.top);
+ assertEqualInt(250, posRect.right);
+ assertEqualInt(250, posRect.bottom);
+ assertEqualInt(0, clipRect.left);
+ assertEqualInt(0, clipRect.top);
+ assertEqualInt(32000, clipRect.right);
+ assertEqualInt(32000, clipRect.bottom);
+
+ pFrame = NULL;
+ pDoc = NULL;
+ posRect = { 0 };
+ clipRect = { 0 };
+ frameInfo = { 0 };
+ hr = pInPlaceSite->GetWindowContext(&pFrame, &pDoc, &posRect, &clipRect, &frameInfo);
+ assertEqualHr(S_OK, hr);
+ assertNotNull(pFrame);
+ assertNull(pDoc);
+ assertEqualInt(0, posRect.left);
+ assertEqualInt(0, posRect.top);
+ assertEqualInt(250, posRect.right);
+ assertEqualInt(250, posRect.bottom);
+ assertEqualInt(0, clipRect.left);
+ assertEqualInt(0, clipRect.top);
+ assertEqualInt(32000, clipRect.right);
+ assertEqualInt(32000, clipRect.bottom);
+ assertEqualInt(sizeof(OLEINPLACEFRAMEINFO), frameInfo.cb);
+ assertEqualBool(FALSE, frameInfo.fMDIApp);
+ assertEqualInt(0, frameInfo.cAccelEntries);
+ assertNull(frameInfo.haccel);
+ assertNull(frameInfo.hwndFrame);
+
+ // Negative tests.
+#if false
+ hr = pInPlaceSite->GetWindowContext(NULL, &pDoc, &posRect, &clipRect, &frameInfo);
+ assertEqualHr(E_POINTER, hr);
+
+ hr = pInPlaceSite->GetWindowContext(&pFrame, NULL, &posRect, &clipRect, &frameInfo);
+ assertEqualHr(E_POINTER, hr);
+#endif
+
+ hr = pInPlaceSite->GetWindowContext(&pFrame, &pDoc, NULL, &clipRect, &frameInfo);
+ assertEqualHr(E_POINTER, hr);
+
+ hr = pInPlaceSite->GetWindowContext(&pFrame, &pDoc, &posRect, NULL, &frameInfo);
+ assertEqualHr(E_POINTER, hr);
+
+ return S_OK;
+}
+
+static HRESULT Test_IOleInPlaceSite(IOleInPlaceSite* pInPlaceSite, std::wstringstream& output)
+{
+ HRESULT hr;
+
+ hr = Test_IOleInPlaceSite_GetWindow(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleInPlaceSite_ContextSensitiveHelp(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleInPlaceSite_CanInPlaceActivate(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleInPlaceSite_OnInPlaceActivate(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleInPlaceSite_OnUIActivate(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = Test_IOleInPlaceSite_GetWindowContext(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+TEST const WCHAR* WINAPI Test_WebBrowserSiteBase(IUnknown* pUnknown)
+{
+ return RunTest([&](std::wstringstream& output)
+ {
+ HRESULT hr;
+ CComPtr pControlSite;
+ CComPtr pSimpleFrameSite;
+ CComPtr pClientSite;
+ CComPtr pInPlaceSite;
+
+ hr = pUnknown->QueryInterface(IID_IOleControlSite, (void**)&pControlSite);
+ hr = Test_IOleControlSite(pControlSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = pUnknown->QueryInterface(IID_ISimpleFrameSite, (void**)&pSimpleFrameSite);
+ hr = Test_ISimpleFrameSite(pSimpleFrameSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = pUnknown->QueryInterface(IID_IOleClientSite, (void**)&pClientSite);
+ hr = Test_IOleClientSite(pClientSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ hr = pUnknown->QueryInterface(IID_IOleInPlaceSite, (void**)&pInPlaceSite);
+ hr = Test_IOleInPlaceSite(pInPlaceSite, output);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ return S_OK;
+ });
+}
diff --git a/src/System.Windows.Forms/tests/InteropTests/NativeTests/testhelpers.h b/src/System.Windows.Forms/tests/InteropTests/NativeTests/testhelpers.h
new file mode 100644
index 0000000000..b96348d57c
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/NativeTests/testhelpers.h
@@ -0,0 +1,158 @@
+// 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.
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#define TEST extern "C" __declspec(dllexport)
+
+template::value>>
+static inline bool is_essentially_equal_to(const T& a, const T& b)
+{
+ if (std::isnan(a))
+ {
+ return std::isnan(b);
+ }
+ else if (std::isnan(b))
+ {
+ return false;
+ }
+
+ return std::abs(a - b) <= std::numeric_limits::epsilon() * std::fmin(std::abs(a), std::abs(b));
+}
+
+template< typename... Args >
+std::wstring format(const wchar_t* format, Args... args)
+{
+ int length = std::swprintf(nullptr, 0, format, args...);
+ // If this fails, let the program crash.
+ wchar_t* buf = new wchar_t[length + 1];
+ std::swprintf(buf, length + 1, format, args...);
+
+ std::wstring str(buf);
+ delete[] buf;
+ return str;
+}
+
+static void printAssertionFailure(std::wstringstream& output, const char* file, const char* function, int line = __LINE__)
+{
+ output << format(L"Assertion failure: file %hs in %hs, line %d", file, function, line) << std::endl;
+}
+
+#define printLine() printAssertionFailure(output, __FILE__, __FUNCTION__, __LINE__);
+
+#define assertEqualInt(expected, actual) \
+if ((expected) != (actual)) \
+{ \
+ printLine(); \
+ output << format(L"Expected: %d\n", (int)((expected))); \
+ output << format(L"Actual: %d\n", (int)((actual))); \
+ return E_FAIL; \
+}
+
+#define assertEqualBool(expected, actual) \
+if ((expected) != (actual)) \
+{ \
+ printLine(); \
+ if ((expected)) \
+ { \
+ output << format(L"Expected: TRUE\n"); \
+ output << format(L"Actual: FALSE\n"); \
+ } \
+ else \
+ { \
+ output << format(L"Expected: FALSE\n"); \
+ output << format(L"Actual: TRUE\n"); \
+ } \
+ return E_FAIL; \
+}
+
+#define assertEqualFloat(expected, actual) \
+if (!is_essentially_equal_to((float)((expected)), (float)((actual)))) \
+{ \
+ printLine(); \
+ output << format(L"Expected: %f\n", (float)((expected))); \
+ output << format(L"Actual: %f\n", (float)((actual))); \
+ return E_FAIL; \
+}
+
+#define assertEqualDouble(expected, actual) \
+if (!is_essentially_equal_to((double)((expected)), (double)((actual)))) \
+{ \
+ printLine(); \
+ output << format(L"Expected: %f\n", (double)((expected))); \
+ output << format(L"Actual: %f\n", (double)((actual))); \
+ return E_FAIL; \
+}
+
+#define assertEqualHr(expected, actual) \
+if ((int)((expected)) != (int)((actual))) \
+{ \
+ printLine(); \
+ output << format(L"Expected: 0x%08X\n", (int)((expected))); \
+ output << format(L"Actual: 0x%08X\n", (int)((actual))); \
+ return E_FAIL; \
+}
+
+#define assertEqualWString(expected, actual) \
+if (!(expected) && (actual)) \
+{ \
+ output << format(L"Expected: NULL\n"); \
+ output << format(L"Actual: %s\n", (const wchar_t*)((actual))); \
+} \
+else if ((expected) && !(actual)) \
+{ \
+ output << format(L"Expected: %s\n", (const wchar_t*)((expected))); \
+ output << format(L"Actual: NULL\n"); \
+} \
+else if (wcscmp((const wchar_t*)(expected), (const wchar_t*)(actual)) != 0) \
+{ \
+ printLine(); \
+ output << format(L"Expected: %s\n", (const wchar_t*)((expected))); \
+ output << format(L"Actual: %s\n", (const wchar_t*)((actual))); \
+ return E_FAIL; \
+}
+
+#define assertNull(actual) \
+if ((actual)) \
+{ \
+ printLine(); \
+ output << format(L"Actual: %p\n", ((actual))); \
+ return E_FAIL; \
+}
+
+#define assertNotNull(actual) \
+if (!(actual) || (void*)((actual)) == (void*)(long)0xdeadbeef) \
+{ \
+ printLine(); \
+ output << format(L"Actual: %p\n", ((actual))); \
+ return E_FAIL; \
+}
+
+template
+static const WCHAR* RunTest(T method)
+{
+ std::wstringstream output;
+ HRESULT hr = method(output);
+
+ if (hr == S_OK)
+ {
+ output << L"Success";
+ }
+ return Duplicate(output.str().c_str());
+}
+
+// Need to allocate this as CoTaskMemAlloc to be interpreted by interop marshallers.
+static const WCHAR* Duplicate(const WCHAR* source)
+{
+ size_t length = wcslen(source) + 1;
+ WCHAR* clone = (WCHAR*)CoTaskMemAlloc(length * sizeof(WCHAR));
+ wcscpy_s(clone, length, source);
+ return clone;
+}
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
new file mode 100644
index 0000000000..7444bb6e09
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/System.Windows.Forms.Interop.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ System.Windows.Forms.Interop.Tests
+ x86;x64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/System.Windows.Forms/tests/InteropTests/WebBrowserSiteBaseInteropTests.cs b/src/System.Windows.Forms/tests/InteropTests/WebBrowserSiteBaseInteropTests.cs
new file mode 100644
index 0000000000..52d61601c4
--- /dev/null
+++ b/src/System.Windows.Forms/tests/InteropTests/WebBrowserSiteBaseInteropTests.cs
@@ -0,0 +1,28 @@
+// 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.InteropTests
+{
+ public class WebBrowserSiteBaseInteropTests : InteropTestBase
+ {
+ [WinFormsFact]
+ public void WebBrowserSiteBase_RunInteropTests()
+ {
+ var browser = new SubWebBrowser();
+ WebBrowserSiteBase site = browser.CreateWebBrowserSiteBase();
+ AssertSuccess(Test_WebBrowserSiteBase(site));
+ }
+
+ [DllImport(NativeTests, CharSet = CharSet.Unicode, ExactSpelling = true)]
+ private static extern string Test_WebBrowserSiteBase([MarshalAs(UnmanagedType.IUnknown)] object pUnk);
+
+ private class SubWebBrowser : WebBrowser
+ {
+ public new WebBrowserSiteBase CreateWebBrowserSiteBase() => base.CreateWebBrowserSiteBase();
+ }
+ }
+}