在同一Windows窗体应用程序的实例之间拖放

我已经创建了一个小的Windows窗体测试应用程序来尝试一些拖放代码。 该表单由三个PictureBoxes组成。 我的目的是从一个PictureBox中抓取一张图片,在拖动操作期间将其显示为自定义光标,然后将其放在另一个PictureBox目标上。

只要它们在同一个窗体上,这从一个PictureBox到另一个PictureBox就可以正常工作。

如果我打开同一个应用程序的两个实例并尝试在它们之间拖放,我会收到以下神秘错误:

此远程处理代理没有通道接收器,这意味着服务器没有正在侦听的已注册服务器通道,或者此应用程序没有合适的客户端通道与服务器通信。

但是,出于某种原因,它可以拖放到Wordpad(但不是MS Word或画笔)。

三个PictureBoxes将它们的事件连接起来如下:

foreach (Control pbx in this.Controls) { if (pbx is PictureBox) { pbx.AllowDrop = true; pbx.MouseDown += new MouseEventHandler(pictureBox_MouseDown); pbx.GiveFeedback += new GiveFeedbackEventHandler(pictureBox_GiveFeedback); pbx.DragEnter += new DragEventHandler(pictureBox_DragEnter); pbx.DragDrop += new DragEventHandler(pictureBox_DragDrop); } } 

然后是这样的四个事件:

 void pictureBox_MouseDown(object sender, MouseEventArgs e) { int width = (sender as PictureBox).Image.Width; int height = (sender as PictureBox).Image.Height; Bitmap bmp = new Bitmap(width, height); Graphics g = Graphics.FromImage(bmp); g.DrawImage((sender as PictureBox).Image, 0, 0, width, height); g.Dispose(); cursorCreatedFromControlBitmap = CustomCursors.CreateFormCursor(bmp, transparencyType); bmp.Dispose(); Cursor.Current = this.cursorCreatedFromControlBitmap; (sender as PictureBox).DoDragDrop((sender as PictureBox).Image, DragDropEffects.All); } 

 void pictureBox_GiveFeedback(object sender, GiveFeedbackEventArgs gfea) { gfea.UseDefaultCursors = false; } 

 void pictureBox_DragEnter(object sender, DragEventArgs dea) { if ((dea.KeyState & 32) == 32) { // ALT is pressed dea.Effect = DragDropEffects.Link; } else if ((dea.KeyState & 8) == 8) { // CTRL is pressed dea.Effect = DragDropEffects.Copy; } else if ((dea.KeyState & 4) == 4) { // SHIFT is pressed dea.Effect = DragDropEffects.None; } else { dea.Effect = DragDropEffects.Move; } } 

 void pictureBox_DragDrop(object sender, DragEventArgs dea) { if (((IDataObject)dea.Data).GetDataPresent(DataFormats.Bitmap)) (sender as PictureBox).Image = (Image)((IDataObject)dea.Data).GetData(DataFormats.Bitmap); } 

任何帮助将不胜感激!

经过大量咬牙切齿和拔毛之后,我才能想出一个可行的解决方案。 似乎有一些无证的陌生感在.NET及其OLE拖放支持下进行。 它似乎是在.NET应用程序之间执行拖放时尝试使用.NET远程处理,但这是否记录在任何地方? 不,我认为不是。

因此,我提出的解决方案涉及一个帮助程序类,以帮助编组进程之间的位图数据。 首先,这是课程。

 [Serializable] public class BitmapTransfer { private byte[] buffer; private PixelFormat pixelFormat; private Size size; private float dpiX; private float dpiY; public BitmapTransfer(Bitmap source) { this.pixelFormat = source.PixelFormat; this.size = source.Size; this.dpiX = source.HorizontalResolution; this.dpiY = source.VerticalResolution; BitmapData bitmapData = source.LockBits( new Rectangle(new Point(0, 0), source.Size), ImageLockMode.ReadOnly, source.PixelFormat); IntPtr ptr = bitmapData.Scan0; int bufferSize = bitmapData.Stride * bitmapData.Height; this.buffer = new byte[bufferSize]; System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, bufferSize); source.UnlockBits(bitmapData); } public Bitmap ToBitmap() { Bitmap bitmap = new Bitmap( this.size.Width, this.size.Height, this.pixelFormat); bitmap.SetResolution(this.dpiX, this.dpiY); BitmapData bitmapData = bitmap.LockBits( new Rectangle(new Point(0, 0), bitmap.Size), ImageLockMode.WriteOnly, bitmap.PixelFormat); IntPtr ptr = bitmapData.Scan0; int bufferSize = bitmapData.Stride * bitmapData.Height; System.Runtime.InteropServices.Marshal.Copy(this.buffer, 0, ptr, bufferSize); bitmap.UnlockBits(bitmapData); return bitmap; } } 

要以支持位图的.NET和非托管接收者的方式使用该类,DataObject类将用于拖放操作,如下所示。

要开始拖动操作:

 DataObject dataObject = new DataObject(); dataObject.SetData(typeof(BitmapTransfer), new BitmapTransfer((sender as PictureBox).Image as Bitmap)); dataObject.SetData(DataFormats.Bitmap, (sender as PictureBox).Image as Bitmap); (sender as PictureBox).DoDragDrop(dataObject, DragDropEffects.All); 

完成操作:

 if (dea.Data.GetDataPresent(typeof(BitmapTransfer))) { BitmapTransfer bitmapTransfer = (BitmapTransfer)dea.Data.GetData(typeof(BitmapTransfer)); (sender as PictureBox).Image = bitmapTransfer.ToBitmap(); } else if(dea.Data.GetDataPresent(DataFormats.Bitmap)) { Bitmap b = (Bitmap)dea.Data.GetData(DataFormats.Bitmap); (sender as PictureBox).Image = b; } 

首先执行对客户BitmapTransfer的检查,因此它优先于数据对象中存在的常规Bitmap。 BitmapTransfer类可以放在共享库中,以便与多个应用程序一起使用。 它必须标记为可序列化,如图所示,用于应用程序之间的拖放。 我通过在应用程序中,应用程序之间以及从.NET应用程序到Wordpad中拖放位图来测试它。

希望这可以帮助你。

我最近遇到了这个问题,并在剪贴板中使用自定义格式,使Interop更加困难。 无论如何,通过一些光reflection,我能够获得原始的System.Windows.Forms.DataObject,然后调用GetData并像平常一样从我的自定义项中取出。

 var oleConverterType = Type.GetType("System.Windows.DataObject+OleConverter, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); var oleConverter = typeof(System.Windows.DataObject).GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(e.Data); var dataObject = (System.Windows.Forms.DataObject)oleConverterType.GetProperty("OleDataObject").GetValue(oleConverter, null); var item = dataObject.GetData(this.Format); 

经过几个小时的沮丧,我的耳朵里涌出了蒸汽,我终于找到了解决这个问题的第二个方案。 究竟哪种解决方案最优雅的可能是在旁观者的眼中。 我希望迈克尔和我的解决方案能够帮助沮丧的程序员,并在他们开始类似任务时节省时间。

首先,有一件事让我感到震惊的是,Wordpad能够立即接收拖放图像。 因此,文件的打包可能不是问题,但接收端可能还有一些可疑的东西。

还有鱼腥味。 事实certificate,在.Net框架中存在着各种类型的IDataObjects。 正如Michael指出的那样,OLE拖放支持尝试在应用程序之间进行交互时使用.Net远程处理。 这实际上是一个System.Runtime.Remoting.Proxies .__ TransparentProxy应该是图像的位置。 显然,这不是(完全)正确的。

以下文章给了我一些正确方向的指示:

http://blogs.msdn.com/adamroot/archive/2008/02/01/shell-style-drag-and-drop-in-net-wpf-and-winforms.aspx

Windows窗体默认为System.Windows.Forms.IDataObject。 但是,由于我们在这里处理不同的进程,所以我决定改为给System.Runtime.InteropServices.ComTypes.IDataObject一个镜头。

在dragdrop事件中,以下代码解决了问题:

 const int CF_BITMAP = 2; System.Runtime.InteropServices.ComTypes.FORMATETC formatEtc; System.Runtime.InteropServices.ComTypes.STGMEDIUM stgMedium; formatEtc = new System.Runtime.InteropServices.ComTypes.FORMATETC(); formatEtc.cfFormat = CF_BITMAP; formatEtc.dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT; formatEtc.lindex = -1; formatEtc.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_GDI; 

两个GetData函数只共享相同的名称。 一个返回一个对象,另一个定义为返回void,而是将信息传递给stgMedium out参数:

 (dea.Data as System.Runtime.InteropServices.ComTypes.IDataObject).GetData(ref formatEtc, out stgMedium); Bitmap remotingImage = Bitmap.FromHbitmap(stgMedium.unionmember); (sender as PictureBox).Image = remotingImage; 

最后,为了避免内存泄漏,调用OLE函数ReleaseStgMedium可能是个好主意:

 ReleaseStgMedium(ref stgMedium); 

该function可以包括如下:

 [DllImport("ole32.dll")] public static extern void ReleaseStgMedium([In, MarshalAs(UnmanagedType.Struct)] ref System.Runtime.InteropServices.ComTypes.STGMEDIUM pmedium); 

…并且此代码似乎与两个应用程序之间的拖放操作(位图)完美配合。 代码可以很容易地扩展到其他有效的剪贴板格式,也可能是自定义剪贴板格式。 由于打包部件没有任何操作,您仍然可以将图像拖放到Wordpad,并且由于它接受位图格式,因此您还可以将图像从Word拖动到应用程序中。

作为旁注,直接从IE拖放图像甚至不会引发DragDrop事件。 奇怪。

出于好奇,在DragDrop方法中,您是否尝试过测试是否可以从DragEventArgs中获取位图图像? 没有做发送者演员? 我想知道图片框对象是否不可序列化,当您尝试在不同的应用程序域中使用发件人时会导致问题…