Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/System.Windows.Forms.Primitives/src/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,23 @@ DestroyIcon
DestroyMenu
DestroyWindow
DestroyWindow
DISP_E_ARRAYISLOCKED
DISP_E_BADINDEX
DISP_E_BADPARAMCOUNT
DISP_E_BADVARTYPE
DISP_E_BADCALLEE
DISP_E_BUFFERTOOSMALL
DISP_E_DIVBYZERO
DISP_E_EXCEPTION
DISP_E_MEMBERNOTFOUND
DISP_E_NONAMEDARGS
DISP_E_NOTACOLLECTION
DISP_E_OVERFLOW
DISP_E_PARAMNOTFOUND
DISP_E_PARAMNOTOPTIONAL
DISP_E_TYPEMISMATCH
DISP_E_UNKNOWNINTERFACE
DISP_E_UNKNOWNLCID
DISP_E_UNKNOWNNAME
DISPATCH_FLAGS
DispatchMessage
Expand Down Expand Up @@ -267,6 +280,7 @@ HitTestThemeBackground
HWND_*
IAccessible
IAutoComplete2
IClassFactory
IClassFactory2
ICM_MODE
ICON_*
Expand Down Expand Up @@ -338,6 +352,7 @@ IsAccelerator
IsAppThemed
IsChild
IShellItem
ISpecifyPropertyPages
IsProcessDPIAware
IsThemeBackgroundPartiallyTransparent
IsThemePartDefined
Expand Down Expand Up @@ -384,6 +399,7 @@ LVSIL_*
LVTILEVIEWINFO_FLAGS
LVTILEVIEWINFO_MASK
MapWindowPoints
MEMBERID_NIL
MessageBeep
MCGRIDINFO_FLAGS
MCGRIDINFO_PART
Expand Down Expand Up @@ -417,6 +433,7 @@ OLE_E_INVALIDRECT
OLE_E_NOCONNECTION
OLE_E_PROMPTSAVECANCELLED
OleCreatePictureIndirect
OleCreatePropertyFrame
OleFlushClipboard
OleInitialize
OleUninitialize
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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;

namespace Windows.Win32.System.Com;

/// <summary>
/// Wraps an <see cref="IClassFactory"/> from a dynamically loaded assembly.
/// </summary>
internal unsafe class ComClassFactory : IDisposable
{
private readonly string _filePath;
public Guid ClassId { get; }
private readonly HINSTANCE _instance;
private AgileComPointer<IClassFactory> _classFactory;

private const string ExportMethodName = "DllGetClassObject";

public ComClassFactory(
string filePath,
Guid classId)
{
HRESULT result = PInvoke.OleInitialize(pvReserved: (void*)null);

_filePath = filePath;
ClassId = classId;
_instance = PInvoke.LoadLibraryEx(filePath, HANDLE.Null, default);
if (_instance.IsNull)
{
throw new Win32Exception();
}

string name = PInvoke.GetModuleFileNameLongPath(_instance);

// Dynamically get the class factory method.

// HRESULT DllGetClassObject(
// [in] REFCLSID rclsid,
// [in] REFIID riid,
// [out] LPVOID* ppv
// );

FARPROC proc = PInvoke.GetProcAddress(_instance, ExportMethodName);
IClassFactory* classFactory;
((delegate* unmanaged<Guid*, Guid*, void**, HRESULT>)proc.Value)(
&classId, IID.Get<IClassFactory>(),
(void**)&classFactory).ThrowOnFailure();
_classFactory = new(classFactory);
}

internal HRESULT CreateInstance(out IUnknown* unknown)
{
unknown = default;
fixed (IUnknown** u = &unknown)
{
using var factory = _classFactory.GetInterface();
return factory.Value->CreateInstance(null, IID.Get<IUnknown>(), (void**)u);
}
}

internal HRESULT CreateInstance(out object unknown)
{
HRESULT result = CreateInstance(out IUnknown* punk);
unknown = result.Failed || punk is null ? null : Marshal.GetObjectForIUnknown((nint)punk);
return result;
}

public void Dispose()
{
_classFactory.Dispose();
PInvoke.FreeLibrary(_instance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ internal void StartEvents()

try
{
_connectionPoint = new ConnectionPointCookie(nativeObject, this, typeof(IPropertyNotifySink));
_connectionPoint = new ConnectionPointCookie(nativeObject, this, typeof(IPropertyNotifySink), throwException: false);
}
catch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms.Design;
using Windows.Win32.System.Com;
using Windows.Win32.System.Ole;
using static Interop;
using static Interop.Ole32;

namespace System.Windows.Forms.ComponentModel.Com2Interop
{
Expand All @@ -19,26 +18,26 @@ public static unsafe bool NeedsComponentEditor(object comObject)
{
// Check for a property page.
Guid guid = Guid.Empty;
HRESULT hr = perPropertyBrowsing.MapPropertyToPage((int)DispatchID.MEMBERID_NIL, &guid);
HRESULT hr = perPropertyBrowsing.MapPropertyToPage(PInvoke.MEMBERID_NIL, &guid);
if (hr.Succeeded && !guid.Equals(Guid.Empty))
{
return true;
}
}

if (comObject is ISpecifyPropertyPages ispp)
if (comObject is ISpecifyPropertyPages.Interface ispp)
{
CAUUID uuids = default;
CAUUID pages = default;
try
{
HRESULT hr = ispp.GetPages(&uuids);
return hr.Succeeded && uuids.cElems > 0;
HRESULT hr = ispp.GetPages(&pages);
return hr.Succeeded && pages.cElems > 0;
}
finally
{
if (uuids.pElems is not null)
if (pages.pElems is not null)
{
Marshal.FreeCoTaskMem((IntPtr)uuids.pElems);
Marshal.FreeCoTaskMem((IntPtr)pages.pElems);
}
}
}
Expand All @@ -48,35 +47,36 @@ public static unsafe bool NeedsComponentEditor(object comObject)

public override unsafe bool EditComponent(ITypeDescriptorContext? context, object obj, IWin32Window? parent)
{
IntPtr handle = (parent is null ? IntPtr.Zero : parent.Handle);
HWND handle = parent is null ? HWND.Null : (HWND)parent.Handle;

// Try to get the page guid
if (obj is IPerPropertyBrowsing.Interface perPropertyBrowsing)
{
// Check for a property page.
Guid guid = Guid.Empty;
HRESULT hr = perPropertyBrowsing.MapPropertyToPage((int)DispatchID.MEMBERID_NIL, &guid);
HRESULT hr = perPropertyBrowsing.MapPropertyToPage(PInvoke.MEMBERID_NIL, &guid);
if (hr.Succeeded & !guid.Equals(Guid.Empty))
{
IntPtr pUnk = Marshal.GetIUnknownForObject(obj);
try
{
Oleaut32.OleCreatePropertyFrame(
new HandleRef(parent, handle),
PInvoke.OleCreatePropertyFrame(
handle,
0,
0,
"PropertyPages",
1,
&pUnk,
(IUnknown**)(void**)&pUnk,
1,
&guid,
in guid,
PInvoke.GetThreadLocale(),
0,
IntPtr.Zero);
null);
return true;
}
finally
{
GC.KeepAlive(parent);
Marshal.Release(pUnk);
}
}
Expand All @@ -96,22 +96,24 @@ public override unsafe bool EditComponent(ITypeDescriptorContext? context, objec
IntPtr pUnk = Marshal.GetIUnknownForObject(obj);
try
{
Oleaut32.OleCreatePropertyFrame(
new HandleRef(parent, handle),
Guid guid = *uuids.pElems;
PInvoke.OleCreatePropertyFrame(
handle,
0,
0,
"PropertyPages",
1,
&pUnk,
(IUnknown**)(void**)pUnk,
uuids.cElems,
uuids.pElems,
guid,
PInvoke.GetThreadLocale(),
0,
IntPtr.Zero);
null);
return true;
}
finally
{
GC.KeepAlive(parent);
Marshal.Release(pUnk);
if (uuids.pElems is not null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ internal static string GetName(object component)
Guid guid = Guid.Empty;
try
{
return dispatch.GetIDsOfNames(&guid, names, 1, PInvoke.GetThreadLocale(), &dispid).Failed || dispid == DispatchID.UNKNOWN
HRESULT result = dispatch.GetIDsOfNames(&guid, names, 1, PInvoke.GetThreadLocale(), &dispid);
return result.Failed || dispid == DispatchID.UNKNOWN
? null
: GetPropertyValue(component, dispid, ref succeeded);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,10 @@
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
<None Update="TestResources\VB6\SimpleControl.vb6">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 System.Windows.Forms.Tests.TestResources;
using Xunit;

namespace System.Windows.Forms.Tests
{
public class AxHostVisualBasic6Tests
{
[WinFormsFact]
public void AxHost_SimpleControl_Create()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to run this as a unit test and not as UI integration test? The latter are run serially.

{
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{
return;
}

using Form form = new();
form.Shown += (object sender, EventArgs e) => form.Close();
using DynamicAxHost control = new(ComClasses.VisualBasicSimpleControl);
((ISupportInitialize)control).BeginInit();
form.Controls.Add(control);
((ISupportInitialize)control).EndInit();
form.ShowDialog();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms.Tests.TestResources;
using Windows.Win32.System.Ole;
using WMPLib;
using Xunit;
Expand Down Expand Up @@ -104,6 +106,40 @@ public void ComNativeDescriptor_GetProperties_FromActiveXControl()

var converter = (Com2ExtendedTypeConverter)urlProperty.Converter;
Assert.IsAssignableFrom<StringConverter>(converter.InnerConverter);

var attributes = TypeDescriptor.GetAttributes(mediaPlayer);
Assert.Equal(1, attributes.Count);
EditorAttribute editor = (EditorAttribute)attributes[0];
Assert.Equal(typeof(Com2ComponentEditor).AssemblyQualifiedName, editor.EditorTypeName);
Assert.Equal(typeof(ComponentEditor).AssemblyQualifiedName, editor.EditorBaseTypeName);
}

[StaFact]
public void ComNativeDescriptor_GetProperties_FromSimpleVBControl()
{
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{
return;
}

// Not much to see with this control, but it does exercise a fair amount of code.
ComClasses.VisualBasicSimpleControl.CreateInstance(out object vbcontrol).ThrowOnFailure();

var properties = TypeDescriptor.GetProperties(vbcontrol);
Assert.Empty(properties);

var events = TypeDescriptor.GetEvents(vbcontrol);
Assert.Empty(events);

// Things don't go well after this hits ISpecifyPropertyPages in COM2ComponentEditor. The runtime
// state gets corrupted. VB problem maybe? In any case this gets back the static attributes.
//
//var attributes = TypeDescriptor.GetAttributes(vbcontrol);
//Assert.Equal(2, attributes.Count);
//BrowsableAttribute browsable = (BrowsableAttribute)attributes[0];
//Assert.True(browsable.Browsable);
//DesignTimeVisibleAttribute visible = (DesignTimeVisibleAttribute)attributes[1];
//Assert.False(visible.Visible);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 Windows.Win32.System.Com;
using System.Diagnostics;

namespace System.Windows.Forms.Tests
{
/// <summary>
/// Custom <see cref="AxHost"/> that directly uses a <see cref="ComClassFactory"/>.
/// </summary>
public unsafe class DynamicAxHost : AxHost
{
private readonly ComClassFactory _factory;

internal DynamicAxHost(ComClassFactory factory) : base(factory.ClassId.ToString("D"), 0)
{
_factory = factory;
}

protected override object CreateInstanceCore(Guid clsid)
{
Debug.Assert(clsid == _factory.ClassId);
_factory.CreateInstance(out object unknown).ThrowOnFailure();
return unknown;
}
}
}
Loading