Implemented base class for Com Interface implementations

This commit is contained in:
Simon Lübeß
2025-06-26 23:39:05 +02:00
parent 2cc8889ac1
commit 39c9109919
3 changed files with 183 additions and 71 deletions
@@ -0,0 +1,80 @@
#if BF_PLATFORM_WINDOWS
using System;
using DirectX.Common;
using GlitchyEngine.Platform.Windows.Com;
namespace GlitchyEditor.Platform.Windows;
[CRepr]
public struct IDropSource : IUnknown
{
public const new Guid IID = .(0x00000121, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
public new VTable* VT { get => (.)mVT; }
public HResult QueryContinueDrag(BigBool fEscapePressed, uint32 grfKeyState) mut => VT.QueryContinueDrag(&this, fEscapePressed, grfKeyState);
public HResult GiveFeedback(DropEffect dwEffect) mut => VT.GiveFeedback(&this, dwEffect);
[CRepr]
public struct VTable : IUnknown.VTable
{
public new function [CallingConvention(.Stdcall)] HResult(IDropSource* self, BigBool fEscapePressed, uint32 grfKeyState) QueryContinueDrag;
public new function [CallingConvention(.Stdcall)] HResult(IDropSource* self, DropEffect dwEffect) GiveFeedback;
}
}
abstract class IDropSourceImplBase : IUnknownImplBase<IDropSource, IDropSource.VTable>
{
protected override void InitVTable(ref IDropSource.VTable vTable)
{
vTable.QueryContinueDrag = => QueryContinueDragImpl;
vTable.GiveFeedback = => GiveFeedbackImpl;
}
[CallingConvention(.Stdcall)]
private static HResult QueryContinueDragImpl(IDropSource* self, BigBool fEscapePressed, uint32 grfKeyState)
{
Self instance = GetInstance<Self>(self);
// TODO: Translate keyStates (MK_CONTROL, etc...)
if (instance.QueryContinueDrag(fEscapePressed, grfKeyState) case .Ok)
{
return .S_OK;
}
return .E_FAIL;
}
[CallingConvention(.Stdcall)]
private static HResult GiveFeedbackImpl(IDropSource* self, DropEffect effect)
{
Self instance = GetInstance<Self>(self);
if (instance.GiveFeedback(effect) case .Ok)
{
return .S_OK;
}
return .E_FAIL;
}
public abstract Result<void> QueryContinueDrag(bool escapePressed, uint32 grfKeyState);
public abstract Result<void> GiveFeedback(DropEffect effect);
}
class MyDropSource : IDropSourceImplBase
{
public override Result<void> QueryContinueDrag(bool escapePressed, uint32 grfKeyState)
{
Runtime.NotImplemented();
}
public override Result<void> GiveFeedback(DropEffect effect)
{
Runtime.NotImplemented();
}
}
#endif
@@ -9,6 +9,7 @@ using static System.Windows;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using System.Collections; using System.Collections;
using System.IO; using System.IO;
using GlitchyEngine.Platform.Windows.Com;
namespace GlitchyEditor.Platform.Windows; namespace GlitchyEditor.Platform.Windows;
@@ -191,28 +192,10 @@ public enum TYMED : int32
NULL = 0, NULL = 0,
} }
abstract class IDropTargetImplBase : RefCounted abstract class IDropTargetImplBase : IUnknownImplBase<IDropTarget, IDropTarget.VTable>
{ {
[CRepr] protected override void InitVTable(ref IDropTarget.VTable vTable)
private struct IDropTargetImpl : IDropTarget
{ {
public void* ClassPtr;
}
IDropTargetImpl impl;
IDropTarget.VTable vTable;
public this()
{
impl = .();
impl.[Friend]mVT = &vTable;
impl.ClassPtr = Internal.UnsafeCastToPtr(this);
vTable.QueryInterface = => QueryInterfaceImpl;
vTable.AddRef = => AddRefImpl;
vTable.Release = => ReleaseImpl;
vTable.DragEnter = => DragEnterImpl; vTable.DragEnter = => DragEnterImpl;
vTable.DragOver = => DragOverImpl; vTable.DragOver = => DragOverImpl;
vTable.DragLeave = => DragLeaveImpl; vTable.DragLeave = => DragLeaveImpl;
@@ -221,7 +204,7 @@ abstract class IDropTargetImplBase : RefCounted
public void Register() public void Register()
{ {
HResult result = RegisterDragDrop(Application.Instance.Window.[Friend]_windowHandle, &impl); HResult result = RegisterDragDrop(Application.Instance.Window.[Friend]_windowHandle, InterfacePtr);
Log.EngineLogger.Assert(result not case .E_OUTOFMEMORY, "Failed to register drag drop handler (E_OUTOFMEMORY). Make sure you called OleInitialize and not CoInitialize[Ex]"); Log.EngineLogger.Assert(result not case .E_OUTOFMEMORY, "Failed to register drag drop handler (E_OUTOFMEMORY). Make sure you called OleInitialize and not CoInitialize[Ex]");
Log.EngineLogger.Assert(result case .S_OK); Log.EngineLogger.Assert(result case .S_OK);
} }
@@ -234,50 +217,6 @@ abstract class IDropTargetImplBase : RefCounted
OleUninitialize(); OleUninitialize();
} }
private static IDropTargetImplBase GetInstance(IUnknown* self)
{
IDropTargetImpl* impl = (.)self;
return (.)Internal.UnsafeCastToObject(impl.ClassPtr);
}
private static HResult QueryInterfaceImpl(IUnknown* self, ref Guid riid, void** output)
{
Self instance = GetInstance(self);
HResult result = .E_NOINTERFACE;
*output = null;
if (riid == IUnknown.IID || riid == IDropTarget.IID)
{
*output = (IUnknown*)&instance.impl;
instance.AddRef();
result = .S_OK;
}
return result;
}
private static uint32 AddRefImpl(IUnknown* self)
{
Self instance = GetInstance(self);
instance.AddRef();
return (uint32)instance.RefCount;
}
private static uint32 ReleaseImpl(IUnknown* self)
{
Self instance = GetInstance(self);
uint32 count = (uint32)instance.ReleaseRefNoDelete();
if (count == 0)
delete instance;
return count;
}
// TODO: Data object? KeyState? // TODO: Data object? KeyState?
public abstract Result<DropEffect> OnDragEnter(int2 cursorPosition); public abstract Result<DropEffect> OnDragEnter(int2 cursorPosition);
public abstract Result<DropEffect> OnDragOver(int2 cursorPosition); public abstract Result<DropEffect> OnDragOver(int2 cursorPosition);
@@ -287,7 +226,7 @@ abstract class IDropTargetImplBase : RefCounted
[CallingConvention(.Stdcall)] [CallingConvention(.Stdcall)]
private static HResult DragEnterImpl(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) private static HResult DragEnterImpl(IDropTarget* self, /*IDataObject*/ IUnknown* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect)
{ {
Self instance = GetInstance(self); Self instance = GetInstance<Self>(self);
Result<DropEffect> result = instance.OnDragEnter(point); Result<DropEffect> result = instance.OnDragEnter(point);
@@ -300,7 +239,7 @@ abstract class IDropTargetImplBase : RefCounted
[CallingConvention(.Stdcall)] [CallingConvention(.Stdcall)]
private static HResult DragOverImpl(IDropTarget* self, uint32 grfKeyState, int2 point, ref DropEffect effect) private static HResult DragOverImpl(IDropTarget* self, uint32 grfKeyState, int2 point, ref DropEffect effect)
{ {
Self instance = GetInstance(self); Self instance = GetInstance<Self>(self);
Result<DropEffect> result = instance.OnDragOver(point); Result<DropEffect> result = instance.OnDragOver(point);
if (result case .Ok(out effect)) if (result case .Ok(out effect))
@@ -312,7 +251,7 @@ abstract class IDropTargetImplBase : RefCounted
[CallingConvention(.Stdcall)] [CallingConvention(.Stdcall)]
private static HResult DragLeaveImpl(IDropTarget* self) private static HResult DragLeaveImpl(IDropTarget* self)
{ {
Self instance = GetInstance(self); Self instance = GetInstance<Self>(self);
Result<void> result = instance.OnDragLeave(); Result<void> result = instance.OnDragLeave();
if (result case .Ok) if (result case .Ok)
@@ -330,7 +269,7 @@ abstract class IDropTargetImplBase : RefCounted
[CallingConvention(.Stdcall)] [CallingConvention(.Stdcall)]
private static HResult DropImpl(IDropTarget* self, IDataObject* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect) private static HResult DropImpl(IDropTarget* self, IDataObject* dataObject, uint32 grfKeyState, int2 point, ref DropEffect effect)
{ {
Self instance = GetInstance(self); Self instance = GetInstance<Self>(self);
// render the data into stgm using the data description in fmte // render the data into stgm using the data description in fmte
FORMATETC format = .() FORMATETC format = .()
@@ -368,7 +307,7 @@ abstract class IDropTargetImplBase : RefCounted
int endIndex = paths.Length; int endIndex = paths.Length;
// StringViews might be invalid until fixup step down below // StringViews might be invalid until fixup step down below
files.Add(StringView(paths, startIndex, endIndex - startIndex)); files.Add(StringView(paths, startIndex, endIndex - startIndex - 1));
} }
} }
@@ -381,7 +320,7 @@ abstract class IDropTargetImplBase : RefCounted
for (ref StringView path in ref files) for (ref StringView path in ref files)
{ {
path.Ptr = paths.Ptr + index; path.Ptr = paths.Ptr + index;
index += path.Length; index += path.Length + 1;
} }
Result<DropEffect> result = instance.OnDrop(point, files); Result<DropEffect> result = instance.OnDrop(point, files);
@@ -0,0 +1,93 @@
using System;
using DirectX.Common;
using GlitchyEngine.Core;
using System.Diagnostics;
namespace GlitchyEngine.Platform.Windows.Com;
/// Base class for implementation of an COM-Interface using a Beef-Class.
abstract class IUnknownImplBase<TInterface, TVTable> : RefCounter where TInterface : IUnknown, struct where TVTable : IUnknown.VTable
{
[CRepr]
protected struct InterfaceImpl<TInterface>
{
public TInterface Base;
public void* ClassPtr;
}
InterfaceImpl<TInterface> impl;
private static TVTable vTable;
protected TInterface* InterfacePtr => (TInterface*)&impl;
public this()
{
impl = .();
impl.Base.[Friend]mVT = (IUnknown.VTable*)&vTable;
impl.ClassPtr = Internal.UnsafeCastToPtr(this);
if (vTable.QueryInterface == null)
{
vTable.QueryInterface = => QueryInterfaceImpl;
vTable.AddRef = => AddRefImpl;
vTable.Release = => ReleaseImpl;
InitVTable(ref vTable);
}
}
protected abstract void InitVTable(ref TVTable vTable);
/// Get the beef instance from the interface.
protected static T GetInstance<T>(IUnknown* self) where T : class
{
InterfaceImpl<TInterface>* impl = (.)self;
Object beefInstance = Internal.UnsafeCastToObject(impl.ClassPtr);
Debug.Assert(beefInstance is T);
return (T)beefInstance;
}
[CallingConvention(.Stdcall)]
private static HResult QueryInterfaceImpl(IUnknown* self, ref Guid riid, void** output)
{
Self instance = GetInstance<Self>(self);
HResult result = .E_NOINTERFACE;
*output = null;
if (riid == IUnknown.IID || riid == TInterface.IID)
{
*output = (IUnknown*)&instance.impl;
instance.AddRef();
result = .S_OK;
}
return result;
}
[CallingConvention(.Stdcall)]
private static uint32 AddRefImpl(IUnknown* self)
{
Self instance = GetInstance<Self>(self);
instance.AddRef();
return (uint32)instance.RefCount;
}
[CallingConvention(.Stdcall)]
private static uint32 ReleaseImpl(IUnknown* self)
{
Self instance = GetInstance<Self>(self);
uint32 count = (uint32)instance.ReleaseRefNoDelete();
if (count == 0)
delete instance;
return count;
}
}