none
How best to avoid the land mine(s) planted in the IDataObject returned from Clipboard.GetDataObject? RRS feed

  • Question

  • I just discovered that .NET's Clipboard code deposits a nice time bomb in the IDataObject it returns from GetDataObject:

    dataObject.GetFormats() will return "EnhancedMetafile" as a format that you can pull out of the object with GetData, but if you foolishly do dataObject.GetData("EnhancedMetafile") it will throw an ExecutionEngineException which is uncatchable (and so will bring down any app)!!!

    So, that leads to three questions:

    (1)  Are there other time bombs that the Clipboard plants that I need to avoid?

    (2)  Is there good documentation on what Object is actually returned by GetData for each data format returned by GetFormats()?  (at least the standard ones injected by Windows, Office, and other Microsoft products)??

    (3)  Is there a better way to interact with the Clipboard in .NET that is free of buggy, poorly-managed code?



    • Edited by TCC Developer Tuesday, August 28, 2012 8:40 PM okay, its not a time bomb, its a land mine that blows up in your face when you step on it
    Tuesday, August 28, 2012 4:25 PM

Answers

All replies

  • Can you supply a simple code example that illustrates what you are trying to describe?  When I put this code into a button click, I do not have any issue such as you describe, so obviously I am missing something in your scenario.
     
      void b_Click(object sender, EventArgs e)
      {
       IDataObject data = (IDataObject)Clipboard.GetData(DataFormats.EnhancedMetafile);
      }

    --
    Mike
    Wednesday, August 29, 2012 1:41 AM
  • Mike, thanks for looking into this.

    When you ran that, did you actually have an EnhancedMetafile on the Clipboard?

    If not, then yes that code runs fine.

    If you go into Word and select something and copy it on the clipboard, then run your code, you should get the same Exception I do.

    (My code is a little different, but I copied your code in front of mine, and it throws just the same as mine.)

     private void BoomButton_Click(object sender, EventArgs e)
            {
                IDataObject data = (IDataObject)Clipboard.GetData(DataFormats.EnhancedMetafile); // throws
    
                IDataObject dataObj = Clipboard.GetDataObject();
                object obj = dataObj.GetData(DataFormats.EnhancedMetafile);  // throws
                Image image = obj as Image;
            }



    • Edited by TCC Developer Wednesday, August 29, 2012 12:55 PM moved a comment on code
    Wednesday, August 29, 2012 2:30 AM
  • Hi TCC,

    To the first exception, you can avoid it by checking if Clipboard contains this special format by using Clipboard.ContainsData Method.

    To the second exception, what's the error message? The method may throw ExternalException and ThreadStateException, you can handle ExternalException in a try catch exception. For more details about this two exception, see http://msdn.microsoft.com/en-us/library/system.windows.forms.clipboard.getdataobject

    By the way, you can get a list of formats by calling DataObject.GetFormats Method.

    Best Regards,


    Bob Wu [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, August 29, 2012 8:33 AM
    Moderator
  • To the first exception, you can avoid it by checking if Clipboard contains this special format by using Clipboard.ContainsData Method.

    Actually, that method returns true.  And yes, the "EnhancedMetafile" format is being returned from DataObject.GetFormats.

    Both of these versions of the function throw the same exception (if there is EnhancedMetafile on the Clipboard; fine if not):

            private void BoomButton_Click(object sender, EventArgs e)
            {
                if (Clipboard.ContainsData(DataFormats.EnhancedMetafile))
                {
                    IDataObject data = (IDataObject)Clipboard.GetData(DataFormats.EnhancedMetafile);
                }
            }
    
            private void BoomButton2_Click(object sender, EventArgs e)
            {
                IDataObject dataObj = Clipboard.GetDataObject();
                if (dataObj.GetDataPresent(DataFormats.EnhancedMetafile))
                {
                    object obj = dataObj.GetData(DataFormats.EnhancedMetafile);
                    Image image = obj as Image;
                }
            }
    

    Wednesday, August 29, 2012 12:50 PM
  • To the second exception, what's the error message? The method may throw ExternalException and ThreadStateException, you can handle ExternalException in a try catch exception.

    Both Exceptions are the same... ExecutionEngineException... and you can NOT handle it with a try-catch... the ExecutionEngineException is uncatchable.  (Its a bug in .NET... I am wondering whether there are other formats that might throw that.)

    Here's the code with (ineffectual) Exception handling added.

            private void BoomButton_Click(object sender, EventArgs e)
            {
                try
                {
                    if (Clipboard.ContainsData(DataFormats.EnhancedMetafile))
                    {
                        // next line will throw if "EnhancedMetafile" is on Clipboard
                        IDataObject data = (IDataObject)Clipboard.GetData(DataFormats.EnhancedMetafile);
                    }
                }
                catch (ExecutionEngineException eee)
                {
                    Debug.Assert(false, "We never get here.");
                }
                catch (Exception ee)
                {
                    Debug.Assert(false, "We never get here either.");
                }
            }
    
            private void BoomButton2_Click(object sender, EventArgs e)
            {
                try
                {
                    IDataObject dataObj = Clipboard.GetDataObject();
                    if (dataObj.GetDataPresent(DataFormats.EnhancedMetafile))
                    {
                        // next line will throw if "EnhancedMetafile" is on Clipboard
                        object obj = dataObj.GetData(DataFormats.EnhancedMetafile);
                        Image image = obj as Image;
                    }
                }
                catch (Exception ee)
                {
                    Debug.Assert(false, "Never happens, despite the ExecutionEngineException thrown.");
                }
            }
    
        }
    

    Wednesday, August 29, 2012 1:08 PM
  • This is all academic as without special code, all that can be done by a .NET app with a metafile placed on the clipboard by Word is to paste the MetafilePict into a RichTextBox.  Metafiles are a confusing area, primarily because they are thought of as images and they are not.
    Wednesday, August 29, 2012 2:17 PM
  • This is all academic as without special code, all that can be done by a .NET app with a metafile placed on the clipboard by Word is to paste the MetafilePict into a RichTextBox.  Metafiles are a confusing area, primarily because they are thought of as images and they are not.

    Hmmm... this might be worthy of a thread unto itself... "Metafiles are thought of as images and they are not."?!?!?!?

    Metafile is derived from Image.

    PictureBox (and most other image-handling things) take Image as input and can display any derived Image, including Metafile.  Contrary to your statement above, I have code that is showing a Word-generated Metafile in a PictureBox.  PictureBox is happy to display Metafiles.  Image.FromStream(stream) will process Metafiles just as well as Bitmaps.

    .wmf and .emf files are considered image files, open in image viewers, and are editable by many vector graphic image editors.

    In most any discussion of images, you are taught there are two major classes of images:  raster images and vector images.  In .NET, Bitmap (derived from Image) is used for all Raster image formats while Metafile (derived from Image) is used for all Vector image formats.

    But actually Enhanced Metafile (EMF+) is the best of the image formats as it can hold bitmaps as well as vector all in one format, where each bitmap can be compressed however it was originally compressed (JPG vs. PNG vs. GIF, etc)... so it can be argued that EMF+ is better at raster than the raster formats.  (Actually, John Wein showed me that in another thread a couple weeks back... thanks, John.)

    Soooo, in what way are Metafiles not Images???

    (other than the fact that whoever wrote the .NET Metafile class and functionality was completely clueless and wrote possibly the buggiest code in all of .NET and for some reason Microsoft has never bothered to fix that... the only way to work with Enhanced Metafiles is to resort to the underlying Win32 code via externs.... but that doesn't make them not images, just makes them hard to use properly in .NET)


    • Edited by TCC Developer Wednesday, August 29, 2012 2:44 PM added kudos to John Wein
    Wednesday, August 29, 2012 2:37 PM
  • This is all academic as without special code, all that can be done by a .NET app with a metafile placed on the clipboard by Word is to paste the MetafilePict into a RichTextBox.  Metafiles are a confusing area, primarily because they are thought of as images and they are not.

    Hmmm... this might be worthy of a thread unto itself... "Metafiles are thought of as images and they are not."?!?!?!?

    Metafile is derived from Image.

    PictureBox (and most other image-handling things) take Image as input and can display any derived Image, including Metafile.  Contrary to your statement above, I have code that is showing a Word-generated Metafile in a PictureBox.  PictureBox is happy to display Metafiles.  Image.FromStream(stream) will process Metafiles just as well as Bitmaps.

    .wmf and .emf files are considered image files, open in image viewers, and are editable by many vector graphic image editors.

    In most any discussion of images, you are taught there are two major classes of images:  raster images and vector images.  In .NET, Bitmap (derived from Image) is used for all Raster image formats while Metafile (derived from Image) is used for all Vector image formats.

    But actually Enhanced Metafile (EMF+) is the best of the image formats as it can hold bitmaps as well as vector all in one format, where each bitmap can be compressed however it was originally compressed (JPG vs. PNG vs. GIF, etc)... so it can be argued that EMF+ is better at raster than the raster formats.  (Actually, John Wein showed me that in another thread a couple weeks back... thanks, John.)

    Soooo, in what way are Metafiles not Images???

    (other than the fact that whoever wrote the .NET Metafile class and functionality was completely clueless and wrote possibly the buggiest code in all of .NET and for some reason Microsoft has never bothered to fix that... the only way to work with Enhanced Metafiles is to resort to the underlying Win32 code via externs.... but that doesn't make them not images, just makes them hard to use properly in .NET)


    If you write a program that loads an image file and displays the file in a picturebox, is your program an image?

    That's what metafiles are, programs.  To get an image from a metafile, you play the records in the metafile.

    You're really late to this party.  For detailed discussion of .NEt's Windows Forms handling of metafiles, look for forum entries in the 2005 to 2008 time frame.

    Wednesday, August 29, 2012 2:52 PM

  • You're really late to this party.  For detailed discussion of .NEt's Windows Forms handling of metafiles, look for forum entries in the 2005 to 2008 time frame.

    Pretty much any WinForms activity is "late to the party".  And yes, I've been reading lots of old posts (back to 2003 even)... some of these bugs date back to .NET 1.1.  HOWEVER, I have found very little that gives me clear guidance on best practices in working with EMF's.  So, my apologies if my search skills are at fault... but I'd really rather not re-discover problems that everybody knew in 2008... and I'd really rather not re-invent the wheel in working around these problems.

    For example, I have found MS KB article (http://support.microsoft.com/kb/323530) that admits that there are bugs in the .NET functionality to put Metafiles on the Clipboard and gives you some Win32 code to work around it.  But it NEVER mentions that the reverse operation not only does not work, but throws an uncatchable Exception!  So, I have crafted a reverse GetEnhMetafileOnClipboard method (following similar pattern) that seems to behave properly, but probably is less than ideal.

    Actually, from my prior learning in some recent threads on how to create a new empty Metafile and how to copy a PNG or JPG into a Metafile, I'm pretty sure that KB article code can be written better.  However, I'm still figuring this stuff out... 

    And the realization that innocent MANAGED code can throw uncatchable Exceptions that will cause my application to go "poof" has me running a little scared now.  So, rather than craft my own unmanaged code to work-around broken managed code, I am hoping to find some guidance from those people that have code that has been working well since 2005-2008.  That would make me feel MUCH better.

    Wednesday, August 29, 2012 3:07 PM
  • The bug in the Windows Forms IDataObject implementation as it pertains to obtaining an enhanced metafile from the data object is that the GetData does not handle when the native IDataObject::GetData method returns a STGMEDIUM structure that specifies TYMED_ENHMF and contains an enhanced metafile handle. It instead treats the enhanced metafile handle as an IStream object, even though the STGMEDIUM structure does not contain the TYMEF_ISTREAM flag. The ExecutionEngineException occurs when the IDataObject class tries to marshal the “native IStream” into a managed object.

    A workaround for the problem in this scenario is to use the native OleGetClipboard function to get a native IDataObject object. You can then call the native IDataObject::GetData to retrieve the enhanced metafile handle from the clipboard. The enhanced metafile handle can then be passed to the constructor of the Metafile class.

    Alternately, you can also use the native OpenClipboard and GetClipboardData functions to obtain the enhanced metafile handle.


    Trevor Hancock (Microsoft)
    Please remember to "Mark As Answer" the replies that help.

    Thursday, August 30, 2012 5:03 PM
    Moderator
  • The bug in the Windows Forms IDataObject implementation as it pertains to obtaining an enhanced metafile from the data object is that the GetData does not handle when the native IDataObject::GetData method returns a STGMEDIUM structure that specifies TYMED_ENHMF and contains an enhanced metafile handle. It instead treats the enhanced metafile handle as an IStream object, even though the STGMEDIUM structure does not contain the TYMEF_ISTREAM flag. The ExecutionEngineException occurs when the IDataObject class tries to marshal the “native IStream” into a managed object.

    Trevor, thanks for that explanation.  Key question:  

    Are there any other clipboard formats that suffer from this bug?  In particular, MetaFilePict?  Or can I safely use the .NET IDataObject GetData function in all other cases?

    Thursday, August 30, 2012 5:34 PM
  • Any clipboard format that uses a TYMED other than TYMED_ISTREAM, TYMED_HGLOBAL or TYMED_GDI could potentially encounter this problem, depending on the implementation of the IDataObject the data source copied to the clipboard. This can affect the following standard clipboard formats such as CF_ENHMETAFILE, CF_METAFILEPICT and CF_PALETTE. Registered (application-defined) clipboard formats that use TYMEDs other than TYMED_ISTREAM, TYMED_HGLOBAL or TYMED_GDI could also encounter the problem.

    TYMED enumeration
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms691227(v=vs.85).aspx

    Standard Clipboard Formats
    http://msdn.microsoft.com/en-us/library/windows/desktop/ff729168(v=vs.85).aspx


    Trevor Hancock (Microsoft)
    Please remember to "Mark As Answer" the replies that help.

    • Marked as answer by TCC Developer Monday, September 3, 2012 1:52 PM
    Friday, August 31, 2012 4:38 PM
    Moderator
  • Has this been fixed in .NET framework 4.5?  I am still using 4.

    I have the same problem but instead of the clipboard, I am trying to pick it up from the dragDrop data:

        Private Sub Form1_DragDrop(sender As Object, e As System.Windows.Forms.DragEventArgs) Handles Me.DragDrop
            If e.Data.GetDataPresent(DataFormats.EnhancedMetafile, False) Then
                Dim mf As Metafile = e.Data.GetData(DataFormats.EnhancedMetafile, False)
                If mf IsNot Nothing Then
                    Me.CreateGraphics().EnumerateMetafile(mf, New Point(0, 0), New Graphics.EnumerateMetafileProc(AddressOf MetafileCallback))
                End If
            End If
        End Sub

    If a Metafile is dragged and dropped, the GetData blows up in a shower of sparks.  I need to get the "real" Metafile because I am interested in parsing the comments in it.

    Friday, December 12, 2014 10:21 PM
  • Here is the solution how to read from the clipboard an enhanced meta file

    http://stackoverflow.com/questions/18602171/copy-enhanced-metafile-from-clipboard-and-save-it-as-an-image

    Metafile emf = null;
    if (OpenClipboard(IntPtr.Zero))
    {
       
    if (IsClipboardFormatAvailable(CF_ENHMETAFILE))
       
    {
           
    var ptr = GetClipboardData(CF_ENHMETAFILE);
           
    if (!ptr.Equals(IntPtr.Zero))

                     {
                emf
    = new Metafile(ptr, true);

                             emf.Save(@"D:\Emails\abc.png");

                         IntPtrmetaHandle = emf.GetHenhmetafile();

                   IntPtr metaHandle2 = CopyEnhMetaFile(metaHandle, @"d:\Emails\abc.emf");

                     DeleteEnhMetaFile(metaHandle2);

                   }

        }

       // You must close ir, or it will be locked
       CloseClipboard();
    }


    Sunday, November 1, 2015 7:45 PM