Skip to content

Commit

Permalink
Use ComWrappers for IErrorInfo (#6743)
Browse files Browse the repository at this point in the history
* Use ComWrappers for IErrorInfo

* Cleanup RCW instance after use

* Oops

* Fix issue

* Add test case for IErrorInfo handling
Rely on the MXXMLWriter60 ProgID which implement IDispath and error handling

* Add second test which cover actual wrapper

* Initialize VsVarsAll when building tests project

* Attempt to fix

* One more stupid try

* Use reg-free COM for tests

* Add ARM64 platform

* Add configuration

* Ignore copying XML files for native projects

* Probably I was too smart for ARM64 changes.
Attempt to mimic System.Windows.Forms.Interop.Tests

* Apply suggestions from code review

Co-authored-by: Igor Velikorossov <RussKie@users.noreply.github.com>

* Add explanation about project file and cleanup of tests

* Use IID_IUnknown from Primitives

* Apply suggestions from code review

Co-authored-by: Igor Velikorossov <RussKie@users.noreply.github.com>

* Hide GetErrorInfo overload

* Add explanation

* Reuse CoCreateInstace from Primitives

* Apply PR feedback

* Bug, stupid bug!

* Move tests to Interop.Tests project

* Remove not needed file

* Remove not used Import

* Remove leftovers

* Format code

Co-authored-by: Igor Velikorossov <RussKie@users.noreply.github.com>
  • Loading branch information
kant2002 and RussKie committed Apr 7, 2022
1 parent 8942ed4 commit 9ea7948
Show file tree
Hide file tree
Showing 25 changed files with 1,143 additions and 45 deletions.
72 changes: 72 additions & 0 deletions eng/init-vs-env.cmd
Original file line number Diff line number Diff line change
@@ -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="
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
private 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() && pperrinfo != IntPtr.Zero)
{
errinfo = (WinFormsComWrappers.ErrorInfoWrapper)WinFormsComWrappers.Instance.GetOrCreateObjectForComInstance(pperrinfo, CreateObjectFlags.Unwrap);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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<IntPtr, IntPtr*, HRESULT>)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription */)))
(_wrappedInstance, &descriptionPtr);
if (result.Succeeded())
{
pBstrDescription = Marshal.PtrToStringUni(descriptionPtr);
Marshal.FreeBSTR(descriptionPtr);
}
else
{
pBstrDescription = null;
}

return result.Succeeded();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/System.Windows.Forms/src/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1306,13 +1306,18 @@ 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)
{
errorInfo = info;
string info;
if (pErrorInfo.GetDescription(out info))
{
errorInfo = info;
}

pErrorInfo.Dispose();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity
type="win32"
name="System.Windows.Forms.Interop.Tests"
version="1.0.0.0" />

<dependency>
<dependentAssembly>
<!-- RegFree COM -->
<assemblyIdentity
type="win32"
name="NativeTests.X"
version="1.0.0.0"/>
</dependentAssembly>
</dependency>

</assembly>
Original file line number Diff line number Diff line change
Expand Up @@ -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 $<TARGET_FILE_DIR:${PROJECT_NAME}>/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($<$<COMPILE_LANGUAGE:C,CXX>:/W3>) # warning level 3
Expand All @@ -15,5 +33,17 @@ add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/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 $<TARGET_FILE_DIR:${PROJECT_NAME}>/NativeTests.X.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/NativeTests.X.manifest)
file(GENERATE OUTPUT $<TARGET_FILE_DIR:${PROJECT_NAME}>/App.manifest INPUT ${CMAKE_CURRENT_SOURCE_DIR}/App.manifest)

install(TARGETS NativeTests)
install(FILES $<TARGET_FILE_DIR:${PROJECT_NAME}>/NativeTests.tlb TYPE BIN)
install(FILES $<TARGET_FILE_DIR:${PROJECT_NAME}>/NativeTests.X.manifest TYPE BIN)
install(FILES $<TARGET_FILE_DIR:${PROJECT_NAME}>/App.manifest TYPE BIN)
Loading

0 comments on commit 9ea7948

Please sign in to comment.