Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1082 lines
36 KiB
C#

// 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)
// Srikanth Madikeri (csri_1986@yahoo.com) - Win32 Drop files.
//
// 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 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");
static 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)
{
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 control = Control.FromHandle (window);
if (control != null) {
control.DndDrop (DragDropEventArgs);
return S_FALSE;
}
Marshal.WriteInt32 (pdwEffect, (int) DragDropEventArgs.Effect);
return S_OK;
}
}
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: {
b = XplatUIWin32.StringToAnsi ((string)data);
hmem = XplatUIWin32.CopyToMoveableMemory (b);
break;
}
case ClipboardFormats.CF_UNICODETEXT: {
b = XplatUIWin32.StringToUnicode ((string)data);
hmem = XplatUIWin32.CopyToMoveableMemory (b);
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.CopyToMoveableMemory (b);
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);
}
}