Skip to content

Commit

Permalink
Break StandardDispatch into a base class with a derived generic.
Browse files Browse the repository at this point in the history
This will allow using StandardDispatch for dynamic scenarios where there is no preknown interface (i.e. found via ITypeInfo at runtime).
  • Loading branch information
JeremyKuhne committed Feb 3, 2024
1 parent 52d0afc commit 485f249
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 15 deletions.
71 changes: 56 additions & 15 deletions src/thirtytwo/Win32/System/Com/StandardDispatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@
namespace Windows.Win32.System.Com;

/// <summary>
/// Base class for providing <see cref="IDispatch"/> services through a standard dispatch implementation
/// generated from a type library.
/// Base class for providing <see cref="IDispatch"/> services around an existing <see cref="ITypeInfo"/>.
/// </summary>
public unsafe abstract class StandardDispatch<T> : IDispatch.Interface, IDispatchEx.Interface, IDisposable
where T : unmanaged, IComIID
public unsafe abstract class StandardDispatch : IDispatch.Interface, IDispatchEx.Interface, IDisposable
{
private ITypeInfo* _typeInfo;

/// <summary>
/// Construct a new instance with the specified backing <see cref="ITypeInfo"/>.
/// </summary>
public StandardDispatch(ITypeInfo* typeInfo)
public StandardDispatch(ITypeInfo* typeInfo, Guid interfaceGuid)
{
if (typeInfo is null)
{
Expand All @@ -29,7 +27,7 @@ public StandardDispatch(ITypeInfo* typeInfo)
typeInfo->GetTypeAttr(out TYPEATTR* typeAttributes).ThrowOnFailure();
try
{
if (typeAttributes->guid != T.Guid)
if (typeAttributes->guid != interfaceGuid)
{
throw new ArgumentException("Interface guid doesn't match type info", nameof(typeInfo));
}
Expand Down Expand Up @@ -116,19 +114,56 @@ HRESULT IDispatch.Interface.Invoke(
}

// The override couldn't find it, pass it along via the ITypeInfo.
using ComScope<T> @interface = new(ComHelpers.GetComPointer<T>(this));
return _typeInfo->Invoke(@interface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, pArgErr);
using ComScope @interface = GetComCallableWrapper();
return @interface.IsNull
? hr
: _typeInfo->Invoke(@interface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, pArgErr);
}

/// <summary>
/// If applicable, the pointer to our interface so the ITypeInfo implementation can call back via the interface.
/// </summary>
protected virtual ComScope GetComCallableWrapper() => default;

HRESULT IDispatchEx.Interface.GetDispID(BSTR bstrName, uint grfdex, int* pid)
=> bstrName.IsNull || pid is null ? HRESULT.E_POINTER : GetDispID(bstrName, grfdex, pid);
{
if (pid is null)
{
return HRESULT.E_POINTER;
}

protected virtual HRESULT GetDispID(BSTR bstrName, uint grfdex, int* pid) => HRESULT.E_NOTIMPL;
*pid = Interop.DISPID_UNKNOWN;
return bstrName.IsNull ? HRESULT.E_POINTER : GetDispID(bstrName, grfdex, pid);
}

/// <summary>
/// Override to provide a dispatch id for the given name. Return <see cref="HRESULT.DISP_E_UNKNOWNNAME"/>
/// if the name isn't supported.
/// </summary>
/// <remarks>
/// <para>
/// <see href="https://learn.microsoft.com/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/reference/idispatchex-getdispid">
/// Official documentation.
/// </see>
/// </para>
/// </remarks>
protected virtual HRESULT GetDispID(BSTR bstrName, uint grfdex, int* pid) => HRESULT.DISP_E_UNKNOWNNAME;

HRESULT IDispatchEx.Interface.GetMemberName(int id, BSTR* pbstrName)
=> pbstrName is null ? HRESULT.E_POINTER : GetMemberName(id, pbstrName);

protected virtual HRESULT GetMemberName(int id, BSTR* pbstrName) => HRESULT.E_NOTIMPL;
/// <summary>
/// Override to provide the name for a given dispatch id. Return <see cref="HRESULT.DISP_E_UNKNOWNNAME"/> if the
/// name isn't known.
/// </summary>
/// <remarks>
/// <para>
/// <see href="https://learn.microsoft.com/previous-versions/windows/internet-explorer/ie-developer/windows-scripting/reference/idispatchex-getmembername">
/// Official documentation.
/// </see>
/// </para>
/// </remarks>
protected virtual HRESULT GetMemberName(int id, BSTR* pbstrName) => HRESULT.DISP_E_UNKNOWNNAME;

HRESULT IDispatchEx.Interface.GetNextDispID(uint grfdex, int id, int* pid)
{
Expand All @@ -142,7 +177,13 @@ HRESULT IDispatchEx.Interface.GetNextDispID(uint grfdex, int id, int* pid)
return GetNextDispID(grfdex, id, pid);
}

protected virtual HRESULT GetNextDispID(uint grfdex, int id, int* pid) => HRESULT.E_NOTIMPL;
/// <param name="id">
/// <see cref="Interop.DISPID_STARTENUM"/> to start enumeration, or the last id returned by a previous call to
/// <see cref="GetNextDispID(uint, int, int*)"/>.
/// </param>
/// <param name="pid">The next dispatch id.</param>
/// <returns>The next dispatch id, or <see cref="HRESULT.S_FALSE"/> if there are no more.</returns>
protected virtual HRESULT GetNextDispID(uint grfdex, int id, int* pid) => HRESULT.S_FALSE;

HRESULT IDispatchEx.Interface.InvokeEx(
int id,
Expand All @@ -168,9 +209,9 @@ HRESULT IDispatchEx.Interface.InvokeEx(
return hr;
}

// The override couldn't find it, pass it along via the ITypeInfo.
using ComScope<T> @interface = new(ComHelpers.GetComPointer<T>(this));
return _typeInfo->Invoke(@interface, id, (DISPATCH_FLAGS)wFlags, pdp, pvarRes, pei, puArgErr: null);
// The override couldn't find it, pass our own interface along so it can be dispatche
using ComScope @interface = GetComCallableWrapper();
return @interface.IsNull ? hr : _typeInfo->Invoke(@interface, id, (DISPATCH_FLAGS)wFlags, pdp, pvarRes, pei, puArgErr: null);
}

protected virtual HRESULT Invoke(
Expand Down
18 changes: 18 additions & 0 deletions src/thirtytwo/Win32/System/Com/StandardDispatch{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Windows.Win32.System.Com;

/// <summary>
/// Base class for providing <see cref="IDispatch"/> services around an existing <see cref="ITypeInfo"/> for a
/// given <typeparamref name="T"/>.
/// </summary>
public unsafe abstract class StandardDispatch<T> : StandardDispatch
where T : unmanaged, IComIID
{
public StandardDispatch(ITypeInfo* typeInfo) : base(typeInfo, T.Guid)
{
}

protected override ComScope GetComCallableWrapper() => new(ComHelpers.GetComPointer<T>(this));
}

0 comments on commit 485f249

Please sign in to comment.