Drag and Drop between Instances of the same Windows Forms Application

后端 未结 4 425
一生所求
一生所求 2020-12-08 03:23

I have created a small Windows Forms test application to try out some drag/drop code. The form consists of three PictureBoxes. My intention was to grab a picture from one Pi

4条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-08 04:01

    Following hours and hours of frustration with steam coming out of my ears, I finally arrived at a second solution to this problem. Exactly which solution is the most elegant is probably in the eyes of the beholder. I hope that Michael's and my solutions will both aid frustrated programmers and save them time when they embark on similar quests.

    First of all, one thing that did strike me was that Wordpad was able to receive the drag/drop images just out of the box. Thus the packaging of the file was probably not the problem, but there was perhaps something fishy going on at the receiving end.

    And fishy there was. It turns out there are seveal types of IDataObjects floating about the .Net framework. As Michael pointed out, OLE drag and drop support attempts to use .Net remoting when interacting between applications. This actually puts a System.Runtime.Remoting.Proxies.__TransparentProxy where the image is supposed to be. Clearly this is not (entirely) correct.

    The following article gave me a few pointers in the right direction:

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

    Windows Forms defaults to System.Windows.Forms.IDataObject. However, since we're dealing with different processes here, I decided to give System.Runtime.InteropServices.ComTypes.IDataObject a shot instead.

    In the dragdrop event, the following code solves the problem:

    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;
    

    The two GetData functions only share the same name. One returns an object, the other is defined to return void and instead passes the info into the stgMedium out parameter:

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

    Finally, to avoid memory leaks, it's probably a good idea to call the OLE function ReleaseStgMedium:

    ReleaseStgMedium(ref stgMedium);
    

    That function can be included as follows:

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

    ...and this code seems to work perfectly with drag and drop operations (of bitmaps) between two applications. The code could easily be extended to other valid clipboard formats and probably custom clipboard formats too. Since nothing was done with the packaging part, you can still dragdrop an image to Wordpad, and since it accepts bitmap formats, you can also drag an image from Word into the application.

    As a side note, dragging and dropping an image directly from IE does not even raise the DragDrop event. Strange.

提交回复
热议问题