Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First round of converting System.Drawing.Common to COMWrappers #54636

Merged
merged 13 commits into from
Jun 28, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal enum HRESULT : int
E_FAIL = unchecked((int)0x80004005),
E_UNEXPECTED = unchecked((int)0x8000FFFF),
STG_E_INVALIDFUNCTION = unchecked((int)0x80030001L),
STG_E_INVALIDPOINTER = unchecked((int)0x80030009),
STG_E_INVALIDPARAMETER = unchecked((int)0x80030057),
STG_E_INVALIDFLAG = unchecked((int)0x800300FF),
E_ACCESSDENIED = unchecked((int)0x80070005),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void Stat(
// are converted to use ComWrappers.
internal interface IStreamComWrapper
{
static readonly Guid Guid = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
static readonly Guid IID = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

// pcbRead is optional so it must be a pointer
unsafe void Read(byte* pv, uint cb, uint* pcbRead);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ namespace System.Drawing
internal unsafe class DrawingComWrappers : ComWrappers
{
private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry();
private static readonly Lazy<DrawingComWrappers> s_instance = new Lazy<DrawingComWrappers>(() => new DrawingComWrappers());
internal static DrawingComWrappers Instance { get; } = new DrawingComWrappers();

private DrawingComWrappers() { }

internal static DrawingComWrappers Instance => s_instance.Value;

internal static void CheckStatus(int result)
{
if (result != 0)
Expand All @@ -35,13 +33,11 @@ internal static void CheckStatus(int result)
{
GetIUnknownImpl(out IntPtr fpQueryInteface, out IntPtr fpAddRef, out IntPtr fpRelease);

IStreamVtbl iStreamVtbl = IStreamVtbl.Create(fpQueryInteface, fpAddRef, fpRelease);

IntPtr iStreamVtblRaw = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), sizeof(IStreamVtbl));
Marshal.StructureToPtr(iStreamVtbl, iStreamVtblRaw, false);
IStreamVtbl.Fill((IStreamVtbl*)iStreamVtblRaw, fpQueryInteface, fpAddRef, fpRelease);

ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), sizeof(ComInterfaceEntry));
wrapperEntry->IID = Interop.Ole32.IStreamComWrapper.Guid;
wrapperEntry->IID = Interop.Ole32.IStreamComWrapper.IID;
wrapperEntry->Vtable = iStreamVtblRaw;
return wrapperEntry;
}
Expand All @@ -60,8 +56,8 @@ protected override object CreateObject(IntPtr externalComObject, CreateObjectFla
{
Debug.Assert(flags == CreateObjectFlags.None);

Guid pictureGuid = IPicture.Guid;
int hr = Marshal.QueryInterface(externalComObject, ref pictureGuid, out IntPtr comObject);
Guid pictureIID = IPicture.IID;
int hr = Marshal.QueryInterface(externalComObject, ref pictureIID, out IntPtr comObject);
if (hr == 0)
{
return new PictureWrapper(comObject);
Expand All @@ -82,136 +78,134 @@ internal struct IUnknownVtbl
public IntPtr Release;
}

internal struct IStreamVtbl
internal unsafe struct IStreamVtbl
{
public IUnknownVtbl IUnknownImpl;
public IntPtr Read;
public IntPtr Write;
public IntPtr Seek;
public IntPtr SetSize;
public IntPtr CopyTo;
public IntPtr Commit;
public IntPtr Revert;
public IntPtr LockRegion;
public IntPtr UnlockRegion;
public IntPtr Stat;
public IntPtr Clone;

private delegate void _Read(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead);
private delegate void _Write(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten);
private delegate void _Seek(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition);
private delegate void _SetSize(IntPtr thisPtr, ulong libNewSize);
private delegate void _CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten);
private delegate void _Commit(IntPtr thisPtr, uint grfCommitFlags);
private delegate void _Revert(IntPtr thisPtr);
private delegate Interop.HRESULT _LockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType);
private delegate Interop.HRESULT _UnlockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType);
private delegate void _Stat(IntPtr thisPtr, out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfStatFlag);
private delegate IntPtr _Clone(IntPtr thisPtr);

private static _Read s_Read = new _Read(ReadImplementation);
private static _Write s_Write = new _Write(WriteImplementation);
private static _Seek s_Seek = new _Seek(SeekImplementation);
private static _SetSize s_SetSize = new _SetSize(SetSizeImplementation);
private static _CopyTo s_CopyTo = new _CopyTo(CopyToImplementation);
private static _Commit s_Commit = new _Commit(CommitImplementation);
private static _Revert s_Revert = new _Revert(RevertImplementation);
private static _LockRegion s_LockRegion = new _LockRegion(LockRegionImplementation);
private static _UnlockRegion s_UnlockRegion = new _UnlockRegion(UnlockRegionImplementation);
private static _Stat s_Stat = new _Stat(StatImplementation);
private static _Clone s_Clone = new _Clone(CloneImplementation);

public static IStreamVtbl Create(IntPtr fpQueryInteface, IntPtr fpAddRef, IntPtr fpRelease)
public delegate* unmanaged<IntPtr, byte*, uint, uint*, Interop.HRESULT> Read;
eerhardt marked this conversation as resolved.
Show resolved Hide resolved
public delegate* unmanaged<IntPtr, byte*, uint, uint*, Interop.HRESULT> Write;
public delegate* unmanaged<IntPtr, long, SeekOrigin, ulong*, Interop.HRESULT> Seek;
public delegate* unmanaged<IntPtr, ulong, Interop.HRESULT> SetSize;
public delegate* unmanaged<IntPtr, IntPtr, ulong, ulong*, ulong*, Interop.HRESULT> CopyTo;
public delegate* unmanaged<IntPtr, uint, Interop.HRESULT> Commit;
public delegate* unmanaged<IntPtr, Interop.HRESULT> Revert;
public delegate* unmanaged<IntPtr, ulong, ulong, uint, Interop.HRESULT> LockRegion;
public delegate* unmanaged<IntPtr, ulong, ulong, uint, Interop.HRESULT> UnlockRegion;
public delegate* unmanaged<IntPtr, out Interop.Ole32.STATSTG, Interop.Ole32.STATFLAG, Interop.HRESULT> Stat;
public delegate* unmanaged<IntPtr, IntPtr*, Interop.HRESULT> Clone;

public static void Fill(IStreamVtbl* vtable, IntPtr fpQueryInteface, IntPtr fpAddRef, IntPtr fpRelease)
{
return new IStreamVtbl()
vtable->IUnknownImpl = new IUnknownVtbl()
{
IUnknownImpl = new IUnknownVtbl()
{
QueryInterface = fpQueryInteface,
AddRef = fpAddRef,
Release = fpRelease
},
Read = Marshal.GetFunctionPointerForDelegate(s_Read),
Write = Marshal.GetFunctionPointerForDelegate(s_Write),
Seek = Marshal.GetFunctionPointerForDelegate(s_Seek),
SetSize = Marshal.GetFunctionPointerForDelegate(s_SetSize),
CopyTo = Marshal.GetFunctionPointerForDelegate(s_CopyTo),
Commit = Marshal.GetFunctionPointerForDelegate(s_Commit),
Revert = Marshal.GetFunctionPointerForDelegate(s_Revert),
LockRegion = Marshal.GetFunctionPointerForDelegate(s_LockRegion),
UnlockRegion = Marshal.GetFunctionPointerForDelegate(s_UnlockRegion),
Stat = Marshal.GetFunctionPointerForDelegate(s_Stat),
Clone = Marshal.GetFunctionPointerForDelegate(s_Clone),
QueryInterface = fpQueryInteface,
AddRef = fpAddRef,
Release = fpRelease
};
vtable->Read = &ReadImplementation;
vtable->Write = &WriteImplementation;
vtable->Seek = &SeekImplementation;
vtable->SetSize = &SetSizeImplementation;
vtable->CopyTo = &CopyToImplementation;
vtable->Commit = &CommitImplementation;
vtable->Revert = &RevertImplementation;
vtable->LockRegion = &LockRegionImplementation;
vtable->UnlockRegion = &UnlockRegionImplementation;
vtable->Stat = &StatImplementation;
vtable->Clone = &CloneImplementation;
}

private static void ReadImplementation(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead)
[UnmanagedCallersOnly]
private static Interop.HRESULT ReadImplementation(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead)
eerhardt marked this conversation as resolved.
Show resolved Hide resolved
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Read(pv, cb, pcbRead);
return Interop.HRESULT.S_OK;
}

private static void WriteImplementation(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten)
[UnmanagedCallersOnly]
private static Interop.HRESULT WriteImplementation(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Write(pv, cb, pcbWritten);
return Interop.HRESULT.S_OK;
}

private static void SeekImplementation(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition)
[UnmanagedCallersOnly]
private static Interop.HRESULT SeekImplementation(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Seek(dlibMove, dwOrigin, plibNewPosition);
return Interop.HRESULT.S_OK;
}

private static void SetSizeImplementation(IntPtr thisPtr, ulong libNewSize)
[UnmanagedCallersOnly]
private static Interop.HRESULT SetSizeImplementation(IntPtr thisPtr, ulong libNewSize)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.SetSize(libNewSize);
return Interop.HRESULT.S_OK;
}

private static void CopyToImplementation(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten)
[UnmanagedCallersOnly]
private static Interop.HRESULT CopyToImplementation(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
Interop.Ole32.IStreamComWrapper pstmStream = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)pstm);

inst.CopyTo(pstmStream, cb, pcbRead, pcbWritten);
return Interop.HRESULT.S_OK;
}

private static void CommitImplementation(IntPtr thisPtr, uint grfCommitFlags)
[UnmanagedCallersOnly]
private static Interop.HRESULT CommitImplementation(IntPtr thisPtr, uint grfCommitFlags)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Commit(grfCommitFlags);
return Interop.HRESULT.S_OK;
}

private static void RevertImplementation(IntPtr thisPtr)
[UnmanagedCallersOnly]
private static Interop.HRESULT RevertImplementation(IntPtr thisPtr)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Revert();
return Interop.HRESULT.S_OK;
}

[UnmanagedCallersOnly]
private static Interop.HRESULT LockRegionImplementation(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
return inst.LockRegion(libOffset, cb, dwLockType);
}

[UnmanagedCallersOnly]
private static Interop.HRESULT UnlockRegionImplementation(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
return inst.UnlockRegion(libOffset, cb, dwLockType);
}

private static void StatImplementation(IntPtr thisPtr, out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfStatFlag)
[UnmanagedCallersOnly]
private static Interop.HRESULT StatImplementation(IntPtr thisPtr, out Interop.Ole32.STATSTG pstatstg, Interop.Ole32.STATFLAG grfStatFlag)
{
Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);
inst.Stat(out pstatstg, grfStatFlag);
return Interop.HRESULT.S_OK;
}

private static IntPtr CloneImplementation(IntPtr thisPtr)
[UnmanagedCallersOnly]
private static Interop.HRESULT CloneImplementation(IntPtr thisPtr, IntPtr* ppstm)
{
if (ppstm == null)
{
return Interop.HRESULT.STG_E_INVALIDPOINTER;
}

Interop.Ole32.IStreamComWrapper inst = ComInterfaceDispatch.GetInstance<Interop.Ole32.IStreamComWrapper>((ComInterfaceDispatch*)thisPtr);

return Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None);
*ppstm = Instance.GetOrCreateComInterfaceForObject(inst.Clone(), CreateComInterfaceFlags.None);
return Interop.HRESULT.S_OK;
}
}

Expand All @@ -231,11 +225,9 @@ internal struct IPictureVtbl
public IntPtr GetKeepOriginalFormat;
public IntPtr SetKeepOriginalFormat;
public IntPtr PictureChanged;
public _SaveAsFile SaveAsFile;
public delegate* unmanaged<IntPtr, IntPtr, int, int*, int> SaveAsFile;
public IntPtr GetAttributes;
public IntPtr SetHdc;

public delegate int _SaveAsFile(IntPtr thisPtr, IntPtr pstm, int fSaveMemCopy, out int pcbSize);
}

internal struct VtblPtr
Expand All @@ -244,35 +236,47 @@ internal struct VtblPtr
}
#pragma warning restore CS0649

internal interface IPicture
internal interface IPicture : IDisposable
{
static readonly Guid Guid = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0, 0xAA, 0x00, 0x30, 0x0C, 0xAB);
static readonly Guid IID = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0, 0xAA, 0x00, 0x30, 0x0C, 0xAB);

// NOTE: Only SaveAsFile is invoked. The other methods on IPicture are not necessary

int SaveAsFile(IntPtr pstm, int fSaveMemCopy, out int pcbSize);
int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize);
eerhardt marked this conversation as resolved.
Show resolved Hide resolved
}

private class PictureWrapper : IPicture
private unsafe class PictureWrapper : IPicture
{
private readonly IntPtr _wrappedInstance;
private readonly IPictureVtbl _vtable;
private readonly IPictureVtbl* _vtable;
eerhardt marked this conversation as resolved.
Show resolved Hide resolved

public PictureWrapper(IntPtr wrappedInstance)
{
_wrappedInstance = wrappedInstance;

VtblPtr inst = Marshal.PtrToStructure<VtblPtr>(_wrappedInstance);
_vtable = Marshal.PtrToStructure<IPictureVtbl>(inst.Vtbl);
VtblPtr* inst = (VtblPtr*)_wrappedInstance;
_vtable = (IPictureVtbl*)inst->Vtbl;
}

public int SaveAsFile(IntPtr pstm, int fSaveMemCopy, out int pcbSize)
public void Dispose()
{
Marshal.Release(_wrappedInstance);
}

public int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize)
{
// Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation
Guid streamGuid = Interop.Ole32.IStreamComWrapper.Guid;
CheckStatus(Marshal.QueryInterface(pstm, ref streamGuid, out IntPtr pstmImpl));
Guid streamIID = Interop.Ole32.IStreamComWrapper.IID;
CheckStatus(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl));

return _vtable.SaveAsFile(_wrappedInstance, pstmImpl, fSaveMemCopy, out pcbSize);
try
{
return _vtable->SaveAsFile(_wrappedInstance, pstmImpl, fSaveMemCopy, pcbSize);
}
finally
{
Marshal.Release(pstmImpl);
}
}
}
}
Expand Down
Loading