// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // // Authors: // Peter Bartok (pbartok@novell.com) // // // NOT COMPLETE using System; using System.Collections; using System.Drawing; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; namespace System.Windows.Forms { internal class Win32DnD { #region Local Variables private const uint DATADIR_GET = 1; private const uint S_OK = 0x00000000; private const uint S_FALSE = 0x00000001; private const uint DRAGDROP_S_DROP = 0x00040100; private const uint DRAGDROP_S_CANCEL = 0x00040101; private const uint DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102; private const uint E_NOTIMPL = unchecked((uint)0x80004001); private const uint E_NOINTERFACE = unchecked((uint)0x80004002); private const uint E_FAIL = unchecked((uint)0x80004005); private const uint OLE_E_ADVISENOTSUPPORTED = unchecked((uint)0x80040003); private const uint DV_E_FORMATETC = unchecked((uint)0x80040064); // To call function pointers private static MethodInfo GetDataMethod; private static object[] GetDataArgs; // IDataObject Delegates private static QueryInterfaceDelegate DOQueryInterface; private static AddRefDelegate DOAddRef; private static ReleaseDelegate DORelease; private static GetDataDelegate GetData; private static GetDataHereDelegate GetDataHere; private static QueryGetDataDelegate QueryGetData; private static GetCanonicalFormatEtcDelegate GetCanonicalFormatEtc; private static SetDataDelegate SetData; private static EnumFormatEtcDelegate EnumFormatEtc; private static DAdviseDelegate DAdvise; private static DUnadviseDelegate DUnadvise; private static EnumDAdviseDelegate EnumDAdvise; // IDropSource Delegates private static QueryInterfaceDelegate DSQueryInterface; private static AddRefDelegate DSAddRef; private static ReleaseDelegate DSRelease; private static QueryContinueDragDelegate QueryContinueDrag; private static GiveFeedbackDelegate GiveFeedback; // IDropTarget Delegates private static QueryInterfaceDelegate DTQueryInterface; private static AddRefDelegate DTAddRef; private static ReleaseDelegate DTRelease; private static DragEnterDelegate DragEnter; private static DragOverDelegate DragOver; private static DragLeaveDelegate DragLeave; private static DropDelegate Drop; private static DragEventArgs DragDropEventArgs; private static GiveFeedbackEventArgs DragFeedbackEventArgs; private static QueryContinueDragEventArgs DragContinueEventArgs; private static ArrayList DragFormats; private static FORMATETC[] DragFormatArray; private static ArrayList DragMediums; #endregion // Local Variables #region Delegate Definitions // IUnknown internal delegate uint QueryInterfaceDelegate(IntPtr @this, ref Guid riid, IntPtr ppvObject); internal delegate uint AddRefDelegate(IntPtr @this); internal delegate uint ReleaseDelegate(IntPtr @this); // IDataObject internal delegate uint GetDataDelegate(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pmedium); internal delegate uint GetDataHereDelegate(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium); internal delegate uint QueryGetDataDelegate(IntPtr @this, ref FORMATETC pformatetc); internal delegate uint GetCanonicalFormatEtcDelegate(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pformatetcOut); internal delegate uint SetDataDelegate(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, bool release); internal delegate uint EnumFormatEtcDelegate(IntPtr @this, uint direction, IntPtr ppenumFormatEtc); internal delegate uint DAdviseDelegate(IntPtr @this, ref FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection); internal delegate uint DUnadviseDelegate(IntPtr @this, uint pdwConnection); internal delegate uint EnumDAdviseDelegate(IntPtr @this, IntPtr ppenumAdvise); // IDropSource internal delegate uint QueryContinueDragDelegate(IntPtr @this, bool fEscapePressed, uint grfkeyState); internal delegate uint GiveFeedbackDelegate(IntPtr @this, uint pdwEffect); // IDropTarget internal delegate uint DragEnterDelegate(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); internal delegate uint DragOverDelegate(IntPtr @this, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); internal delegate uint DragLeaveDelegate(IntPtr @this); internal delegate uint DropDelegate(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); #endregion // Delegate Definitions [StructLayout(LayoutKind.Sequential)] internal struct FORMATETC { [MarshalAs(UnmanagedType.U2)] internal ClipboardFormats cfFormat; internal IntPtr ptd; internal DVASPECT dwAspect; internal int lindex; internal TYMED tymed; } [StructLayout(LayoutKind.Sequential)] internal struct STGMEDIUM { internal TYMED tymed; internal IntPtr hHandle; internal IntPtr pUnkForRelease; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] internal struct DROPFILES { internal uint pFiles; internal uint pt_x; internal uint pt_y; internal bool fNC; internal bool fWide; internal string pText; } internal enum DVASPECT { DVASPECT_CONTENT = 1, DVASPECT_THUMBNAIL = 2, DVASPECT_ICON = 4, DVASPECT_DOCPRINT = 8 } internal enum TYMED { TYMED_HGLOBAL = 1, TYMED_FILE = 2, TYMED_ISTREAM = 4, TYMED_ISTORAGE = 8, TYMED_GDI = 16, TYMED_MFPICT = 32, TYMED_ENHMF = 64, TYMED_NULL = 0 } private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); private static readonly Guid IID_IDataObject = new Guid("0000010e-0000-0000-C000-000000000046"); private static readonly Guid IID_IDropSource = new Guid("00000121-0000-0000-C000-000000000046"); private static readonly Guid IID_IDropTarget = new Guid("00000122-0000-0000-C000-000000000046"); internal Win32DnD() { // Required for all other OLE functions to work Win32OleInitialize(IntPtr.Zero); // We reuse those DragDropEventArgs = new DragEventArgs(new DataObject(DataFormats.FileDrop, new string[0]), 0, 0, 0, DragDropEffects.None, DragDropEffects.None); DragFeedbackEventArgs = new GiveFeedbackEventArgs(DragDropEffects.None, true); DragContinueEventArgs = new QueryContinueDragEventArgs(0, false, DragAction.Continue); DragFormats = new ArrayList(); DragFormatArray = new FORMATETC[0]; DragMediums = new ArrayList(); // Set up delegates // IDataObject DOQueryInterface = new QueryInterfaceDelegate(ComIDataObject.QueryInterface); DOAddRef = new AddRefDelegate(ComIDataObject.AddRef); DORelease = new ReleaseDelegate(ComIDataObject.Release); GetData = new GetDataDelegate(ComIDataObject.GetData); GetDataHere = new GetDataHereDelegate(ComIDataObject.GetDataHere); QueryGetData = new QueryGetDataDelegate(ComIDataObject.QueryGetData); GetCanonicalFormatEtc = new GetCanonicalFormatEtcDelegate(ComIDataObject.GetCanonicalFormatEtc); SetData = new SetDataDelegate(ComIDataObject.SetData); EnumFormatEtc = new EnumFormatEtcDelegate(ComIDataObject.EnumFormatEtc); DAdvise = new DAdviseDelegate(ComIDataObject.DAdvise); DUnadvise = new DUnadviseDelegate(ComIDataObject.DUnadvise); EnumDAdvise = new EnumDAdviseDelegate(ComIDataObject.EnumDAdvise); // IDropSource DSQueryInterface = new QueryInterfaceDelegate(ComIDropSource.QueryInterface); DSAddRef = new AddRefDelegate(ComIDropSource.AddRef); DSRelease = new ReleaseDelegate(ComIDropSource.Release); QueryContinueDrag = new QueryContinueDragDelegate(ComIDropSource.QueryContinueDrag); GiveFeedback = new GiveFeedbackDelegate(ComIDropSource.GiveFeedback); // IDropTarget DTQueryInterface = new QueryInterfaceDelegate(ComIDropTarget.QueryInterface); DTAddRef = new AddRefDelegate(ComIDropTarget.AddRef); DTRelease = new ReleaseDelegate(ComIDropTarget.Release); DragEnter = new DragEnterDelegate(ComIDropTarget.DragEnter); DragOver = new DragOverDelegate(ComIDropTarget.DragOver); DragLeave = new DragLeaveDelegate(ComIDropTarget.DragLeave); Drop = new DropDelegate(ComIDropTarget.Drop); } internal class ComIDataObject { [StructLayout(LayoutKind.Sequential)] internal struct DataObjectStruct { internal IntPtr vtbl; internal QueryInterfaceDelegate QueryInterface; internal AddRefDelegate AddRef; internal ReleaseDelegate Release; internal GetDataDelegate GetData; internal GetDataHereDelegate GetDataHere; internal QueryGetDataDelegate QueryGetData; internal GetCanonicalFormatEtcDelegate GetCanonicalFormatEtc; internal SetDataDelegate SetData; internal EnumFormatEtcDelegate EnumFormatEtc; internal DAdviseDelegate DAdvise; internal DUnadviseDelegate DUnadvise; internal EnumDAdviseDelegate EnumDAdvise; } internal static IntPtr GetUnmanaged() { DataObjectStruct data_object; IntPtr data_object_ptr; long offset; data_object = new DataObjectStruct(); data_object.QueryInterface = Win32DnD.DOQueryInterface; data_object.AddRef = Win32DnD.DOAddRef; data_object.Release = Win32DnD.DORelease; data_object.GetData = Win32DnD.GetData; data_object.GetDataHere = Win32DnD.GetDataHere; data_object.QueryGetData = Win32DnD.QueryGetData; data_object.GetCanonicalFormatEtc = Win32DnD.GetCanonicalFormatEtc; data_object.SetData = Win32DnD.SetData; data_object.EnumFormatEtc = Win32DnD.EnumFormatEtc; data_object.DAdvise = Win32DnD.DAdvise; data_object.DUnadvise = Win32DnD.DUnadvise; data_object.EnumDAdvise = Win32DnD.EnumDAdvise; data_object_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DataObjectStruct))); Marshal.StructureToPtr(data_object, data_object_ptr, false); // Update vtbl pointer offset = data_object_ptr.ToInt64(); offset += Marshal.SizeOf(typeof(IntPtr)); Marshal.WriteIntPtr(data_object_ptr, new IntPtr(offset)); return data_object_ptr; } internal static void ReleaseUnmanaged(IntPtr data_object_ptr) { Marshal.FreeHGlobal(data_object_ptr); } internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { try { if (IID_IUnknown.Equals(riid) || IID_IDataObject.Equals(riid)) { Marshal.WriteIntPtr(ppvObject, @this); return S_OK; } } catch (Exception e) { Console.WriteLine("Got exception {0}", e.Message); } Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); return E_NOINTERFACE; } internal static uint AddRef(IntPtr @this) { // We only use this for DnD, try and fake it return 1; } internal static uint Release(IntPtr @this) { // We only use this for DnD, try and fake it return 0; } internal static STGMEDIUM medium = new STGMEDIUM(); internal static uint GetData(IntPtr this_, ref FORMATETC pformatetcIn, IntPtr pmedium) { int index; index = FindFormat(pformatetcIn); if (index != -1) { medium.tymed = TYMED.TYMED_HGLOBAL; medium.hHandle = XplatUIWin32.DupGlobalMem(((STGMEDIUM)DragMediums[index]).hHandle); medium.pUnkForRelease = IntPtr.Zero; try { Marshal.StructureToPtr(medium, pmedium, false); } catch (Exception e) { Console.WriteLine("Error: {0}", e.Message); } return S_OK; } return DV_E_FORMATETC; } internal static uint GetDataHere(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium) { return DV_E_FORMATETC; } internal static uint QueryGetData(IntPtr @this, ref FORMATETC pformatetc) { if (FindFormat(pformatetc) != -1) { return S_OK; } return DV_E_FORMATETC; } internal static uint GetCanonicalFormatEtc(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pformatetcOut) { Marshal.WriteIntPtr(pformatetcOut, Marshal.SizeOf(typeof(IntPtr)), IntPtr.Zero); return E_NOTIMPL; } internal static uint SetData(IntPtr this_, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, bool release) { return E_NOTIMPL; } internal static uint EnumFormatEtc(IntPtr this_, uint direction, IntPtr ppenumFormatEtc) { if (direction == DATADIR_GET) { IntPtr ppenum_ptr; ppenum_ptr = IntPtr.Zero; DragFormatArray = new FORMATETC[DragFormats.Count]; for (int i = 0; i < DragFormats.Count; i++) { DragFormatArray[i] = (FORMATETC)DragFormats[i]; } Win32SHCreateStdEnumFmtEtc((uint)DragFormatArray.Length, DragFormatArray, ref ppenum_ptr); Marshal.WriteIntPtr(ppenumFormatEtc, ppenum_ptr); return S_OK; } return E_NOTIMPL; } internal static uint DAdvise(IntPtr this_, ref FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; } internal static uint DUnadvise(IntPtr this_, uint pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; } internal static uint EnumDAdvise(IntPtr this_, IntPtr ppenumAdvise) { return OLE_E_ADVISENOTSUPPORTED; } } internal class ComIDataObjectUnmanaged { [StructLayout(LayoutKind.Sequential)] internal struct IDataObjectUnmanaged { internal IntPtr QueryInterface; internal IntPtr AddRef; internal IntPtr Release; internal IntPtr GetData; internal IntPtr GetDataHere; internal IntPtr QueryGetData; internal IntPtr GetCanonicalFormatEtc; internal IntPtr SetData; internal IntPtr EnumFormatEtc; internal IntPtr DAdvise; internal IntPtr DUnadvise; internal IntPtr EnumDAdvise; } private static bool Initialized; private static MethodInfo GetDataMethod; private static MethodInfo GetDataHereMethod; private static MethodInfo QueryGetDataMethod; private static MethodInfo GetCanonicalFormatEtcMethod; private static MethodInfo SetDataMethod; private static MethodInfo EnumFormatEtcMethod; private static MethodInfo DAdviseMethod; private static MethodInfo DUnadviseMethod; private static MethodInfo EnumDAdviseMethod; private static object[] MethodArguments; private IDataObjectUnmanaged vtbl; private IntPtr @this; internal ComIDataObjectUnmanaged(IntPtr data_object_ptr) { if (!Initialized) { Initialize(); } vtbl = new IDataObjectUnmanaged(); @this = data_object_ptr; try { vtbl = (IDataObjectUnmanaged)Marshal.PtrToStructure(Marshal.ReadIntPtr(data_object_ptr), typeof(IDataObjectUnmanaged)); } catch (Exception e) { Console.WriteLine("Exception {0}", e.Message); } } private static void Initialize() { AssemblyName assembly; AssemblyBuilder assembly_builder; if (Initialized) { return; } assembly = new AssemblyName(); assembly.Name = "XplatUIWin32.FuncPtrInterface"; assembly_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run); MethodArguments = new object[6]; GetDataMethod = CreateFuncPtrInterface(assembly_builder, "GetData", typeof(uint), 3); GetDataHereMethod = CreateFuncPtrInterface(assembly_builder, "GetDataHere", typeof(uint), 3); QueryGetDataMethod = CreateFuncPtrInterface(assembly_builder, "QueryGetData", typeof(uint), 2); GetCanonicalFormatEtcMethod = CreateFuncPtrInterface(assembly_builder, "GetCanonicalFormatEtc", typeof(uint), 3); SetDataMethod = CreateFuncPtrInterface(assembly_builder, "SetData", typeof(uint), 4); EnumFormatEtcMethod = CreateFuncPtrInterface(assembly_builder, "EnumFormatEtc", typeof(uint), 3); DAdviseMethod = CreateFuncPtrInterface(assembly_builder, "DAdvise", typeof(uint), 5); DUnadviseMethod = CreateFuncPtrInterface(assembly_builder, "DUnadvise", typeof(uint), 2); EnumDAdviseMethod = CreateFuncPtrInterface(assembly_builder, "EnumDAdvise", typeof(uint), 2); Initialized = true; } internal uint QueryInterface(Guid riid, IntPtr ppvObject) { uint ret; IntPtr riid_ptr; riid_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); Marshal.StructureToPtr(riid, riid_ptr, false); MethodArguments[0] = vtbl.QueryInterface; MethodArguments[1] = this.@this; MethodArguments[2] = riid_ptr; MethodArguments[3] = ppvObject; try { ret = (uint)GetDataMethod.Invoke(null, MethodArguments); } catch (Exception e) { Console.WriteLine("Caught exception {0}", e.Message); ret = E_FAIL; } Marshal.FreeHGlobal(riid_ptr); return ret; } internal uint AddRef() { // We only use this for DnD, try and fake it return 1; } internal uint Release() { // We only use this for DnD, try and fake it return 0; } internal uint GetData(FORMATETC pformatetcIn, ref STGMEDIUM pmedium) { uint ret; IntPtr pformatetcIn_ptr; IntPtr pmedium_ptr; pformatetcIn_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FORMATETC))); Marshal.StructureToPtr(pformatetcIn, pformatetcIn_ptr, false); pmedium_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(STGMEDIUM))); MethodArguments[0] = vtbl.GetData; MethodArguments[1] = this.@this; MethodArguments[2] = pformatetcIn_ptr; MethodArguments[3] = pmedium_ptr; try { ret = (uint)GetDataMethod.Invoke(null, MethodArguments); Marshal.PtrToStructure(pmedium_ptr, pmedium); } catch (Exception e) { Console.WriteLine("Caught exception {0}", e.Message); ret = E_FAIL; } Marshal.FreeHGlobal(pformatetcIn_ptr); Marshal.FreeHGlobal(pmedium_ptr); return ret; } internal uint GetDataHere(FORMATETC pformatetc, ref STGMEDIUM pmedium) { return E_NOTIMPL; } internal uint QueryGetData(FORMATETC pformatetc) { uint ret; IntPtr pformatetc_ptr; pformatetc_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FORMATETC))); Marshal.StructureToPtr(pformatetc, pformatetc_ptr, false); MethodArguments[0] = vtbl.GetData; MethodArguments[1] = this.@this; MethodArguments[2] = pformatetc_ptr; try { ret = (uint)QueryGetDataMethod.Invoke(null, MethodArguments); } catch (Exception e) { Console.WriteLine("Caught exception {0}", e.Message); ret = E_FAIL; } Marshal.FreeHGlobal(pformatetc_ptr); return ret; } internal uint GetCanonicalFormatEtc(FORMATETC pformatetcIn, ref FORMATETC pformatetcOut) { return E_NOTIMPL; } internal uint SetData(FORMATETC pformatetc, STGMEDIUM pmedium, bool release) { return E_NOTIMPL; } internal uint EnumFormatEtc(uint direction, IntPtr ppenumFormatEtc) { return E_NOTIMPL; } internal uint DAdvise(FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; } internal uint DUnadvise(uint pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; } internal uint EnumDAdvise(IntPtr ppenumAdvise) { return OLE_E_ADVISENOTSUPPORTED; } } internal class ComIDropSource { [StructLayout(LayoutKind.Sequential)] internal struct IDropSource { internal IntPtr vtbl; internal IntPtr Window; internal QueryInterfaceDelegate QueryInterface; internal AddRefDelegate AddRef; internal ReleaseDelegate Release; internal QueryContinueDragDelegate QueryContinueDrag; internal GiveFeedbackDelegate GiveFeedback; } internal static IntPtr GetUnmanaged(IntPtr Window) { IDropSource drop_source; IntPtr drop_source_ptr; long offset; drop_source = new IDropSource(); drop_source.QueryInterface = Win32DnD.DSQueryInterface; drop_source.AddRef = Win32DnD.DSAddRef; drop_source.Release = Win32DnD.DSRelease; drop_source.QueryContinueDrag = Win32DnD.QueryContinueDrag; drop_source.GiveFeedback = Win32DnD.GiveFeedback; drop_source.Window = Window; drop_source_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(drop_source)); Marshal.StructureToPtr(drop_source, drop_source_ptr, false); // Update vtbl pointer offset = drop_source_ptr.ToInt64(); offset += 2 * Marshal.SizeOf(typeof(IntPtr)); Marshal.WriteIntPtr(drop_source_ptr, new IntPtr(offset)); return drop_source_ptr; } internal static void ReleaseUnmanaged(IntPtr drop_source_ptr) { Marshal.FreeHGlobal(drop_source_ptr); } internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { try { if (IID_IUnknown.Equals(riid) || IID_IDropSource.Equals(riid)) { Marshal.WriteIntPtr(ppvObject, @this); return S_OK; } } catch (Exception e) { Console.WriteLine("Got exception {0}", e.Message); } Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); return E_NOINTERFACE; } internal static uint AddRef(IntPtr @this) { // We only use this for DnD, try and fake it return 1; } internal static uint Release(IntPtr @this) { // We only use this for DnD, try and fake it return 0; } internal static uint QueryContinueDrag(IntPtr @this, bool fEscapePressed, uint grfkeyState) { IntPtr window; window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); // LAMESPEC? - according to MSDN, when the any mousebutton is *pressed* it defaults to Drop. // According to COM customary behaviour it's the other way round; which is what we do here if (fEscapePressed) { DragContinueEventArgs.drag_action = DragAction.Cancel; } else if ((grfkeyState & (1+2+16)) == 0) { // Left, middle and right mouse button not pressed DragContinueEventArgs.drag_action = DragAction.Drop; } else { DragContinueEventArgs.drag_action = DragAction.Continue; } DragContinueEventArgs.escape_pressed = fEscapePressed; DragContinueEventArgs.key_state = (int)grfkeyState; Control.FromHandle(window).DnDContinueDrag(DragContinueEventArgs); if (DragContinueEventArgs.drag_action == DragAction.Cancel) { return DRAGDROP_S_CANCEL; } else if (DragContinueEventArgs.drag_action == DragAction.Drop) { return DRAGDROP_S_DROP; } return S_OK; } internal static uint GiveFeedback(IntPtr @this, uint pdwEffect) { IntPtr window; window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); DragFeedbackEventArgs.effect = (DragDropEffects)pdwEffect; DragFeedbackEventArgs.use_default_cursors = true; Control.FromHandle(window).DnDFeedback(DragFeedbackEventArgs); if (DragFeedbackEventArgs.use_default_cursors) { return DRAGDROP_S_USEDEFAULTCURSORS; } return S_OK; } } internal class ComIDropTarget { [StructLayout(LayoutKind.Sequential)] internal struct IDropTarget { internal IntPtr vtbl; internal IntPtr Window; internal QueryInterfaceDelegate QueryInterface; internal AddRefDelegate AddRef; internal ReleaseDelegate Release; internal DragEnterDelegate DragEnter; internal DragOverDelegate DragOver; internal DragLeaveDelegate DragLeave; internal DropDelegate Drop; } internal static IntPtr GetUnmanaged(IntPtr Window) { IDropTarget drop_target; IntPtr drop_target_ptr; long offset; drop_target = new IDropTarget(); drop_target.QueryInterface = Win32DnD.DTQueryInterface; drop_target.AddRef = Win32DnD.DTAddRef; drop_target.Release = Win32DnD.DTRelease; drop_target.DragEnter = Win32DnD.DragEnter; drop_target.DragOver = Win32DnD.DragOver; drop_target.DragLeave = Win32DnD.DragLeave; drop_target.Drop = Win32DnD.Drop; drop_target.Window = Window; drop_target_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(drop_target)); Marshal.StructureToPtr(drop_target, drop_target_ptr, false); // Update vtbl pointer offset = drop_target_ptr.ToInt64(); offset += 2 * Marshal.SizeOf(typeof(IntPtr)); Marshal.WriteIntPtr(drop_target_ptr, new IntPtr(offset)); return drop_target_ptr; } internal static void ReleaseUnmanaged(IntPtr drop_target_ptr) { Marshal.FreeHGlobal(drop_target_ptr); } internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { try { if (IID_IUnknown.Equals(riid) || IID_IDropTarget.Equals(riid)) { Marshal.WriteIntPtr(ppvObject, @this); return S_OK; } } catch (Exception e) { Console.WriteLine("Got exception {0}", e.Message); } Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); return E_NOINTERFACE; } internal static uint AddRef(IntPtr @this) { // We only use this for DnD, try and fake it return 1; } internal static uint Release(IntPtr @this) { // We only use this for DnD, try and fake it return 0; } internal static uint DragEnter(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) { IntPtr window; window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); DragDropEventArgs.x = pt_x.ToInt32(); DragDropEventArgs.y = pt_y.ToInt32(); DragDropEventArgs.allowed_effect = (DragDropEffects)Marshal.ReadIntPtr(pdwEffect).ToInt32(); DragDropEventArgs.current_effect = DragDropEventArgs.AllowedEffect; DragDropEventArgs.keystate = (int)grfkeyState; Control.FromHandle(window).DndEnter(DragDropEventArgs); Marshal.WriteInt32(pdwEffect, (int)DragDropEventArgs.Effect); return S_OK; } internal static uint DragOver(IntPtr @this, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) { IntPtr window; window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); DragDropEventArgs.x = pt_x.ToInt32(); DragDropEventArgs.y = pt_y.ToInt32(); DragDropEventArgs.allowed_effect = (DragDropEffects)Marshal.ReadIntPtr(pdwEffect).ToInt32(); DragDropEventArgs.current_effect = DragDropEventArgs.AllowedEffect; DragDropEventArgs.keystate = (int)grfkeyState; Control.FromHandle(window).DndOver(DragDropEventArgs); Marshal.WriteInt32(pdwEffect, (int)DragDropEventArgs.Effect); return S_OK; } internal static uint DragLeave(IntPtr @this) { IntPtr window; window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); Control.FromHandle(window).DndLeave(EventArgs.Empty); return S_OK; } internal static uint Drop(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) { throw new Exception("Yeah baby, gimme a ride to WM_DROPFILES land"); #if InTheFuture ComIDataObjectUnmanaged data_object; FORMATETC format; STGMEDIUM medium; uint result; IntPtr mem; data_object = new ComIDataObjectUnmanaged(pDataObj); format = new FORMATETC(); format.cfFormat = ClipboardFormats.CF_HDROP; format.ptd = IntPtr.Zero; format.dwAspect = DVASPECT.DVASPECT_CONTENT; format.lindex = -1; format.tymed = TYMED.TYMED_HGLOBAL; medium = new STGMEDIUM(); medium.tymed = TYMED.TYMED_HGLOBAL; //mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr))); //result = data_object.QueryInterface(IID_IDataObject, mem); //Marshal.FreeHGlobal(mem); //result = data_object.AddRef(); result = data_object.QueryGetData(format); result = data_object.GetData(format, ref medium); return E_NOTIMPL; #endif } } internal static bool HandleWMDropFiles(ref MSG msg) { IntPtr hDrop; int count; StringBuilder sb; string[] dropfiles; hDrop = msg.wParam; count = Win32DragQueryFile(hDrop, -1, IntPtr.Zero, 0); dropfiles = new string[count]; sb = new StringBuilder(256); for (int i = 0; i < count; i++) { Win32DragQueryFile(hDrop, i, sb, sb.Capacity); dropfiles[i] = sb.ToString(); } DragDropEventArgs.Data.SetData(DataFormats.FileDrop, dropfiles); Control.FromHandle(msg.hwnd).DndDrop(DragDropEventArgs); return true; } private static bool AddFormatAndMedium(ClipboardFormats cfFormat, object data) { STGMEDIUM medium; FORMATETC format; IntPtr hmem; IntPtr hmem_ptr; byte[] b; switch(cfFormat) { case ClipboardFormats.CF_TEXT: { hmem = Marshal.StringToHGlobalAnsi((string)data); break; } case ClipboardFormats.CF_UNICODETEXT: { hmem = Marshal.StringToHGlobalUni((string)data); break; } case ClipboardFormats.CF_HDROP: { IEnumerator e; StringBuilder sb; long hmem_string_ptr; IntPtr string_buffer; int string_buffer_size; sb = new StringBuilder(); // Make sure object is enumerable; otherwise if ((data is string) || !(data is IEnumerable)) { sb.Append(data.ToString()); sb.Append('\0'); sb.Append('\0'); } else { e = ((IEnumerable)data).GetEnumerator(); while (e.MoveNext()) { sb.Append(e.Current.ToString()); sb.Append('\0'); } sb.Append('\0'); } string_buffer = Marshal.StringToHGlobalUni(sb.ToString()); string_buffer_size = (int)XplatUIWin32.Win32GlobalSize(string_buffer); // Write DROPFILES structure hmem = XplatUIWin32.Win32GlobalAlloc(XplatUIWin32.GAllocFlags.GMEM_MOVEABLE | XplatUIWin32.GAllocFlags.GMEM_DDESHARE, 0x14 + string_buffer_size); hmem_ptr = XplatUIWin32.Win32GlobalLock(hmem); Marshal.WriteInt32(hmem_ptr, 0x14); // pFiles Marshal.WriteInt32(hmem_ptr, 1 * Marshal.SizeOf(typeof(uint)), 0); // point.x Marshal.WriteInt32(hmem_ptr, 2 * Marshal.SizeOf(typeof(uint)), 0); // point.y Marshal.WriteInt32(hmem_ptr, 3 * Marshal.SizeOf(typeof(uint)), 0); // fNc Marshal.WriteInt32(hmem_ptr, 4 * Marshal.SizeOf(typeof(uint)), 1); // fWide hmem_string_ptr = (long)hmem_ptr; hmem_string_ptr += 0x14; XplatUIWin32.Win32CopyMemory(new IntPtr(hmem_string_ptr), string_buffer, string_buffer_size); Marshal.FreeHGlobal(string_buffer); XplatUIWin32.Win32GlobalUnlock(hmem_ptr); break; } case ClipboardFormats.CF_DIB: { b = XplatUIWin32.ImageToDIB((Image)data); hmem = XplatUIWin32.Win32GlobalAlloc(XplatUIWin32.GAllocFlags.GMEM_MOVEABLE | XplatUIWin32.GAllocFlags.GMEM_DDESHARE, b.Length); hmem_ptr = XplatUIWin32.Win32GlobalLock(hmem); Marshal.Copy(b, 0, hmem_ptr, b.Length); XplatUIWin32.Win32GlobalUnlock(hmem); break; } default: { hmem = IntPtr.Zero; break; } } if (hmem != IntPtr.Zero) { medium = new STGMEDIUM(); medium.tymed = TYMED.TYMED_HGLOBAL; medium.hHandle = hmem; medium.pUnkForRelease = IntPtr.Zero; DragMediums.Add(medium); format = new FORMATETC(); format.ptd = IntPtr.Zero; format.dwAspect = DVASPECT.DVASPECT_CONTENT; format.lindex = -1; format.tymed = TYMED.TYMED_HGLOBAL; format.cfFormat = cfFormat; DragFormats.Add(format); return true; } return false; } private static int FindFormat(FORMATETC pformatetc) { for (int i = 0; i < DragFormats.Count; i++) { if ((((FORMATETC)DragFormats[i]).cfFormat == pformatetc.cfFormat) && (((FORMATETC)DragFormats[i]).dwAspect == pformatetc.dwAspect) && ((((FORMATETC)DragFormats[i]).tymed & pformatetc.tymed)) != 0) { return i; } } return -1; } private static void BuildFormats(object data) { DragFormats.Clear(); DragMediums.Clear(); // Build our formats based on object data if (data is String) { AddFormatAndMedium(ClipboardFormats.CF_TEXT, data); AddFormatAndMedium(ClipboardFormats.CF_UNICODETEXT, data); AddFormatAndMedium(ClipboardFormats.CF_HDROP, data); } else if (data is Bitmap) { AddFormatAndMedium(ClipboardFormats.CF_DIB, data); } else if (data is ICollection) { // FIXME - test with .Net and found how this is handled AddFormatAndMedium(ClipboardFormats.CF_HDROP, data); } else if (data is ISerializable) { // FIXME - test with .Net and found how this is handled } } internal static DragDropEffects StartDrag(IntPtr Window, object data, DragDropEffects allowed) { IntPtr result; IntPtr data_object; IntPtr drop_source; BuildFormats(data); data_object = ComIDataObject.GetUnmanaged(); drop_source = ComIDropSource.GetUnmanaged(Window); result = (IntPtr)DragDropEffects.None; Win32DoDragDrop(data_object, drop_source, (IntPtr)allowed, ref result); // Cleanup again ComIDataObject.ReleaseUnmanaged(data_object); ComIDropSource.ReleaseUnmanaged(drop_source); DragFormats.Clear(); DragFormatArray = null; DragMediums.Clear(); return (DragDropEffects)result.ToInt32(); } internal static bool UnregisterDropTarget(IntPtr Window) { Win32RevokeDragDrop(Window); return true; } internal static bool RegisterDropTarget(IntPtr Window) { Hwnd hwnd; IntPtr drop_target; uint result; hwnd = Hwnd.ObjectFromWindow(Window); if (hwnd == null) { return false; } drop_target = ComIDropTarget.GetUnmanaged(Window); hwnd.marshal_free_list.Add(drop_target); result = Win32RegisterDragDrop(Window, drop_target); if (result != S_OK) { return false; } return true; } // Thanks, Martin static MethodInfo CreateFuncPtrInterface(AssemblyBuilder assembly, string MethodName, Type ret_type, int param_count) { ModuleBuilder mb; TypeBuilder tb; Type[] il_param_types; Type[] param_types; mb = assembly.DefineDynamicModule("XplatUIWin32.FuncInterface" + MethodName); tb = mb.DefineType ("XplatUIWin32.FuncInterface" + MethodName, TypeAttributes.Public); param_types = new Type[param_count]; il_param_types = new Type[param_count + 1]; il_param_types[param_count] = typeof(IntPtr); for (int i = 0; i < param_count; i++) { param_types[i] = typeof(IntPtr); il_param_types[i] = typeof(IntPtr); } MethodBuilder method = tb.DefineMethod ( MethodName, MethodAttributes.Static | MethodAttributes.Public, ret_type, il_param_types); ILGenerator ig = method.GetILGenerator (); if (param_count > 5) ig.Emit (OpCodes.Ldarg_S, 6); if (param_count > 4) ig.Emit (OpCodes.Ldarg_S, 5); if (param_count > 3) ig.Emit (OpCodes.Ldarg_S, 4); if (param_count > 2) ig.Emit (OpCodes.Ldarg_3); if (param_count > 1) ig.Emit (OpCodes.Ldarg_2); if (param_count > 0) ig.Emit (OpCodes.Ldarg_1); ig.Emit (OpCodes.Ldarg_0); ig.EmitCalli (OpCodes.Calli, CallingConvention.StdCall, ret_type, param_types); ig.Emit (OpCodes.Ret); Type t = tb.CreateType (); MethodInfo m = t.GetMethod (MethodName); return m; } [DllImport ("ole32.dll", EntryPoint="RegisterDragDrop", CallingConvention=CallingConvention.StdCall)] private extern static uint Win32RegisterDragDrop(IntPtr Window, IntPtr pDropTarget); [DllImport ("ole32.dll", EntryPoint="RevokeDragDrop", CallingConvention=CallingConvention.StdCall)] private extern static int Win32RevokeDragDrop(IntPtr Window); [DllImport ("ole32.dll", EntryPoint="DoDragDrop", CallingConvention=CallingConvention.StdCall)] private extern static uint Win32DoDragDrop(IntPtr pDataObject, IntPtr pDropSource, IntPtr dwOKEffect, ref IntPtr pdwEffect); [DllImport ("shell32.dll", EntryPoint="DragAcceptFiles", CallingConvention=CallingConvention.StdCall)] private extern static int Win32DragAcceptFiles(IntPtr Window, bool fAccept); [DllImport ("ole32.dll", EntryPoint="OleInitialize", CallingConvention=CallingConvention.StdCall)] private extern static int Win32OleInitialize(IntPtr pvReserved); [DllImport ("shell32.dll", EntryPoint="DragQueryFileW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] private extern static int Win32DragQueryFile(IntPtr hDrop, int iFile, IntPtr lpszFile, int cch); [DllImport ("shell32.dll", EntryPoint="DragQueryFileW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] private extern static int Win32DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); [DllImport ("shell32.dll", EntryPoint="SHCreateStdEnumFmtEtc", CallingConvention=CallingConvention.StdCall)] private extern static uint Win32SHCreateStdEnumFmtEtc(uint cfmt, FORMATETC[] afmt, ref IntPtr ppenumFormatEtc); } }