Skip to content
Merged
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;
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)
{
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);
}

private class PictureWrapper : IPicture
private unsafe class PictureWrapper : IPicture
{
private readonly IntPtr _wrappedInstance;
private readonly IPictureVtbl _vtable;
private readonly IPictureVtbl* _vtable;

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