DragDropHelper<TOleServices, TDataFormat>
static class DragDropHelper<TOleServices, TDataFormat> where TOleServices : IOleServices where TDataFormat : IDataFormat<TDataFormat>
Helper class for drop targets to display the drag image while the cursor is over the target window and allows the
application to specify the drag image bitmap that will be displayed during a drag-and-drop operation.
using System.ComponentModel;
using System.Drawing;
using System.Private.Windows.Core.Resources;
using System.Private.Windows.Graphics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.System.Com;
using Windows.Win32.System.Memory;
using Windows.Win32.System.Ole;
using Windows.Win32.UI.Shell;
namespace System.Private.Windows.Ole
{
[NullableContext(1)]
[Nullable(0)]
internal static class DragDropHelper<[Nullable(0)] TOleServices, [Nullable(0)] TDataFormat> where TOleServices : IOleServices where TDataFormat : IDataFormat<TDataFormat>
{
[NullableContext(2)]
public static void ClearDropDescription(IComVisibleDataObject dataObject)
{
if (dataObject != null)
SetDropDescription(dataObject, DROPIMAGETYPE.DROPIMAGE_INVALID, string.Empty, string.Empty);
}
public static void DragEnter(HWND targetWindowHandle, IDragEvent e)
{
IComVisibleDataObject dataObject = e.DataObject;
if (dataObject != null) {
Point point = new Point(e.X, e.Y);
DragEnter(targetWindowHandle, dataObject, ref point, e.Effect);
}
}
public unsafe static void DragEnter(HWND targetWindowHandle, IComVisibleDataObject dataObject, ref Point point, DROPEFFECT effect)
{
ComScope<IDropTargetHelper> scope = new ComScope<IDropTargetHelper>(null);
try {
if (TryGetDragDropHelper<IDropTargetHelper>((IDropTargetHelper**)ref scope)) {
ComScope<IDataObject> scope2 = ComHelpers.GetComScope<IDataObject>((object)dataObject);
try {
scope.Value->DragEnter(targetWindowHandle, (IDataObject*)ref scope2, ref point, effect);
} finally {
scope2.Dispose();
}
}
} finally {
scope.Dispose();
}
}
public unsafe static void DragLeave()
{
ComScope<IDropTargetHelper> scope = new ComScope<IDropTargetHelper>(null);
try {
if (TryGetDragDropHelper<IDropTargetHelper>((IDropTargetHelper**)ref scope))
scope.Value->DragLeave();
} finally {
scope.Dispose();
}
}
public unsafe static void DragOver(IDragEvent e)
{
ComScope<IDropTargetHelper> scope = new ComScope<IDropTargetHelper>(null);
try {
if (TryGetDragDropHelper<IDropTargetHelper>((IDropTargetHelper**)ref scope)) {
Point point = new Point(e.X, e.Y);
scope.Value->DragOver(&point, e.Effect);
}
} finally {
scope.Dispose();
}
}
public unsafe static void Drop(IDragEvent e)
{
ComScope<IDropTargetHelper> scope = new ComScope<IDropTargetHelper>(null);
try {
if (TryGetDragDropHelper<IDropTargetHelper>((IDropTargetHelper**)ref scope)) {
IDataObject.Interface dataObject = e.DataObject;
if (dataObject != null) {
Point ppt = new Point(e.X, e.Y);
ComScope<IDataObject> scope2 = ComHelpers.GetComScope<IDataObject>((object)dataObject);
try {
scope.Value->Drop((IDataObject*)ref scope2, ref ppt, e.Effect);
} finally {
scope2.Dispose();
}
}
}
} finally {
scope.Dispose();
}
}
public unsafe static bool IsInDragLoop(IDataObjectInternal dataObject)
{
ArgumentNullException.ThrowIfNull(dataObject, "dataObject");
if (dataObject.GetDataPresent("InShellDragLoop")) {
DragDropFormat dragDropFormat = dataObject.GetData("InShellDragLoop") as DragDropFormat;
if (dragDropFormat != null)
try {
void* ptr = PInvokeCore.GlobalLock(dragDropFormat.Medium.hGlobal);
return ptr != null && *(BOOL*)ptr == (BOOL)true;
} finally {
PInvokeCore.GlobalUnlock(dragDropFormat.Medium.hGlobal);
}
}
return false;
}
public static bool IsInDragLoopFormat(FORMATETC format)
{
string name = DataFormatsCore<TDataFormat>.GetOrAddFormat(format.cfFormat).Name;
if (!name.Equals("DragContext") && !name.Equals("DragImageBits") && !name.Equals("DragSourceHelperFlags") && !name.Equals("DragWindow") && !name.Equals("DropDescription") && !name.Equals("InShellDragLoop") && !name.Equals("IsShowingLayered") && !name.Equals("IsShowingText"))
return name.Equals("UsingDefaultDragImage");
return true;
}
public static void ReleaseDragDropFormats(IComVisibleDataObject dataObject)
{
string[] formats = dataObject.GetFormats();
foreach (string format in formats) {
(dataObject.GetData(format) as DragDropFormat)?.Dispose();
}
}
private unsafe static void SetBooleanFormat(IComVisibleDataObject dataObject, string format, bool value)
{
ArgumentNullException.ThrowIfNull(dataObject, "dataObject");
ArgumentException.ThrowIfNullOrEmpty(format, "format");
FORMATETC fORMATETC = default(FORMATETC);
fORMATETC.cfFormat = (ushort)(short)PInvokeCore.RegisterClipboardFormat(format);
fORMATETC.dwAspect = 1;
fORMATETC.lindex = -1;
fORMATETC.ptd = null;
fORMATETC.tymed = 1;
FORMATETC fORMATETC2 = fORMATETC;
STGMEDIUM sTGMEDIUM = default(STGMEDIUM);
sTGMEDIUM.pUnkForRelease = null;
sTGMEDIUM.tymed = TYMED.TYMED_HGLOBAL;
sTGMEDIUM.hGlobal = PInvokeCore.GlobalAlloc(GLOBAL_ALLOC_FLAGS.GHND, (UIntPtr)(void*)sizeof(BOOL));
STGMEDIUM sTGMEDIUM2 = sTGMEDIUM;
if (sTGMEDIUM2.hGlobal.IsNull)
throw new Win32Exception(Marshal.GetLastSystemError(), System.Private.Windows.Core.Resources.SR.ExternalException);
void* intPtr = PInvokeCore.GlobalLock(sTGMEDIUM2.hGlobal);
if (intPtr == null) {
PInvokeCore.GlobalFree(sTGMEDIUM2.hGlobal);
throw new Win32Exception(Marshal.GetLastSystemError(), System.Private.Windows.Core.Resources.SR.ExternalException);
}
*(BOOL*)intPtr = value;
PInvokeCore.GlobalUnlock(sTGMEDIUM2.hGlobal);
dataObject.SetData(&fORMATETC2, &sTGMEDIUM2, true);
}
public static void SetDragImage(IComVisibleDataObject dataObject, IGiveFeedbackEvent e)
{
ArgumentNullException.ThrowIfNull(e, "e");
SetDragImage(dataObject, e.DragImage, e.CursorOffset, e.UseDefaultDragImage);
}
public unsafe static void SetDragImage(IComVisibleDataObject dataObject, [Nullable(2)] IBitmap dragImage, Point cursorOffset, bool usingDefaultDragImage)
{
ArgumentNullException.ThrowIfNull(dataObject, "dataObject");
ComScope<IDragSourceHelper2> scope = new ComScope<IDragSourceHelper2>(null);
try {
if (TryGetDragDropHelper<IDragSourceHelper2>((IDragSourceHelper2**)ref scope)) {
if (!IsInDragLoop(dataObject))
SetInDragLoop(dataObject, true);
using (HBITMAP hBITMAP = dragImage?.GetHbitmap() ?? HBITMAP.Null) {
SHDRAGIMAGE sHDRAGIMAGE = default(SHDRAGIMAGE);
sHDRAGIMAGE.hbmpDragImage = hBITMAP;
sHDRAGIMAGE.sizeDragImage = (dragImage?.Size ?? default(Size));
sHDRAGIMAGE.ptOffset = cursorOffset;
sHDRAGIMAGE.crColorKey = (COLORREF)PInvokeCore.GetSysColor(SYS_COLOR_INDEX.COLOR_WINDOW);
SHDRAGIMAGE pshdi = sHDRAGIMAGE;
HRESULT hRESULT = scope.Value->SetFlags(1);
if (hRESULT.Failed)
PInvokeCore.DeleteObject(hBITMAP);
else {
ComScope<IDataObject> scope2 = ComHelpers.GetComScope<IDataObject>((object)dataObject);
try {
hRESULT = scope.Value->InitializeFromBitmap(ref pshdi, (IDataObject*)ref scope2);
if (!hRESULT.Failed) {
SetIsShowingText(dataObject, true);
SetUsingDefaultDragImage(dataObject, usingDefaultDragImage);
}
} finally {
scope2.Dispose();
}
}
}
}
} finally {
scope.Dispose();
}
}
public static void SetDropDescription(IDragEvent e)
{
IComVisibleDataObject dataObject = e.DataObject;
if (dataObject != null) {
if (e.Message == null) {
string text = e.Message = string.Empty;
}
if (e.MessageReplacementToken == null) {
string text = e.MessageReplacementToken = string.Empty;
}
SetDropDescription(dataObject, e.DropImageType, e.Message, e.MessageReplacementToken);
}
}
public unsafe static void SetDropDescription(IComVisibleDataObject dataObject, DROPIMAGETYPE dropImageType, string message, string messageReplacementToken)
{
ArgumentNullException.ThrowIfNull(dataObject, "dataObject");
if ((dropImageType < DROPIMAGETYPE.DROPIMAGE_INVALID || dropImageType > DROPIMAGETYPE.DROPIMAGE_NOIMAGE) ? true : false)
throw new InvalidEnumArgumentException("dropImageType", (int)dropImageType, typeof(DROPIMAGETYPE));
if (message.Length >= 260)
throw new ArgumentOutOfRangeException("message");
if (messageReplacementToken.Length >= 260)
throw new ArgumentOutOfRangeException("messageReplacementToken");
FORMATETC fORMATETC = default(FORMATETC);
fORMATETC.cfFormat = (ushort)(short)PInvokeCore.RegisterClipboardFormat("DropDescription");
fORMATETC.dwAspect = 1;
fORMATETC.lindex = -1;
fORMATETC.ptd = null;
fORMATETC.tymed = 1;
FORMATETC fORMATETC2 = fORMATETC;
STGMEDIUM sTGMEDIUM = default(STGMEDIUM);
sTGMEDIUM.pUnkForRelease = null;
sTGMEDIUM.tymed = TYMED.TYMED_HGLOBAL;
sTGMEDIUM.hGlobal = PInvokeCore.GlobalAlloc(GLOBAL_ALLOC_FLAGS.GHND, (UIntPtr)(void*)sizeof(DROPDESCRIPTION));
STGMEDIUM sTGMEDIUM2 = sTGMEDIUM;
if (sTGMEDIUM2.hGlobal.IsNull)
throw new Win32Exception(Marshal.GetLastSystemError(), System.Private.Windows.Core.Resources.SR.ExternalException);
void* intPtr = PInvokeCore.GlobalLock(sTGMEDIUM2.hGlobal);
if (intPtr == null) {
PInvokeCore.GlobalFree(sTGMEDIUM2.hGlobal);
throw new Win32Exception(Marshal.GetLastSystemError(), System.Private.Windows.Core.Resources.SR.ExternalException);
}
DROPDESCRIPTION* ptr = (DROPDESCRIPTION*)intPtr;
ptr->type = dropImageType;
ptr->szMessage = message;
ptr->szInsert = messageReplacementToken;
PInvokeCore.GlobalUnlock(sTGMEDIUM2.hGlobal);
if (!IsInDragLoop(dataObject))
SetInDragLoop(dataObject, true);
dataObject.SetData(&fORMATETC2, &sTGMEDIUM2, true);
SetIsShowingText(dataObject, true);
}
public static void SetInDragLoop(IComVisibleDataObject dataObject, bool inDragLoop)
{
SetBooleanFormat(dataObject, "InShellDragLoop", inDragLoop);
if (!inDragLoop)
ReleaseDragDropFormats(dataObject);
}
private static void SetIsShowingText(IComVisibleDataObject dataObject, bool isShowingText)
{
SetBooleanFormat(dataObject, "IsShowingText", isShowingText);
}
private static void SetUsingDefaultDragImage(IComVisibleDataObject dataObject, bool usingDefaultDragImage)
{
SetBooleanFormat(dataObject, "UsingDefaultDragImage", usingDefaultDragImage);
}
[NullableContext(0)]
private unsafe static bool TryGetDragDropHelper<[IsUnmanaged] TDragHelper>(TDragHelper** dragDropHelper) where TDragHelper : struct, IComIID
{
((IOleServices)).EnsureThreadState();
Guid rclsid = CLSID.DragDropHelper;
return PInvokeCore.CoCreateInstance<TDragHelper>(ref rclsid, null, CLSCTX.CLSCTX_INPROC_SERVER, out *dragDropHelper).Succeeded;
}
}
}