diff options
Diffstat (limited to 'mcs/class/Managed.Windows.Forms/System.Windows.Forms/Win32DnD.cs')
-rw-r--r-- | mcs/class/Managed.Windows.Forms/System.Windows.Forms/Win32DnD.cs | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Win32DnD.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Win32DnD.cs new file mode 100644 index 00000000000..b85f172e254 --- /dev/null +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Win32DnD.cs @@ -0,0 +1,1097 @@ +// 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); + } +} |