metafile getData from dragDrop generates exception

118 views Asked by At

I am trying to get at comment data in a Metafile that has been dragDrop'd onto my form, however, this code generates the error:

Additional Information: The runtime has encountered a fatal error. The address of the error was at 0xeb556610, on thread 0x2080. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

Every code example I've seen talks about getting the image from the Metafile instead of the metafile itself. The error occurs on the "Dim mf = Metafile" in the DragDrop handler, it never gets to the enumeration code.

Private Sub Form1_DragEnter(sender As Object, e As System.Windows.Forms.DragEventArgs) Handles Me.DragEnter
    If e.Data.GetDataPresent(DataFormats.EnhancedMetafile, False) Then
        e.Effect = DragDropEffects.Copy
    End If
End Sub

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)
        Me.CreateGraphics().EnumerateMetafile(mf, New Point(0, 0), New Graphics.EnumerateMetafileProc(AddressOf MetafileCallback))
    End If
End Sub

Private Function MetafileCallback(ByVal recordType As EmfPlusRecordType, ByVal flags As Integer, ByVal dataSize As Integer, ByVal data As IntPtr, ByVal callbackData As PlayRecordCallback) As Boolean
    If recordType = EmfPlusRecordType.Comment Then
        Debug.WriteLine("Got comment")
    End If
End Function
1

There are 1 answers

0
Jon Boede On

Short answer: You can't dragDrop a metafile. You just can't. You can, however, put it on the clipboard and get it there with User32.dll magic.

Here's both the get and put code:

Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Public Class ClipboardMetafileHelper
    <DllImport("user32.dll", EntryPoint:="OpenClipboard", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
    End Function
    <DllImport("user32.dll", EntryPoint:="IsClipboardFormatAvailable", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function IsClipboardFormatAvailable(ByVal uFormat As Integer) As Boolean
    End Function
    <DllImport("user32.dll", EntryPoint:="EmptyClipboard", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function EmptyClipboard() As Boolean
    End Function
    <DllImport("user32.dll", EntryPoint:="SetClipboardData", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal hWnd As IntPtr) As IntPtr
    End Function
    <DllImport("user32.dll", EntryPoint:="GetClipboardData", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function GetClipboardData(ByVal uFormat As Integer) As IntPtr
    End Function
    <DllImport("user32.dll", EntryPoint:="CloseClipboard", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function CloseClipboard() As Boolean
    End Function
    <DllImport("gdi32.dll", EntryPoint:="CopyEnhMetaFileA", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function CopyEnhMetaFile(ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32.dll", EntryPoint:="DeleteEnhMetaFile", _
       SetLastError:=True, ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Function DeleteEnhMetaFile(ByVal hemfSrc As IntPtr) As Boolean
    End Function

    Private Const CF_ENHMETAFILE As Integer = 14

    ' Metafile mf is set to a state that is not valid inside this function.
    Public Shared Function PutEnhMetafileOnClipboard(ByVal hWnd As IntPtr, ByVal mf As Metafile) As Boolean
        Dim bResult As Boolean = False
        Dim hEMF, hEMF2 As IntPtr

        hEMF = mf.GetHenhmetafile() ' invalidates mf
        If Not hEMF.Equals(New IntPtr(0)) Then
            hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))
            If Not hEMF2.Equals(New IntPtr(0)) Then
                If OpenClipboard(hWnd) Then
                    If EmptyClipboard() Then
                        Dim hRes As IntPtr
                        hRes = SetClipboardData(CF_ENHMETAFILE, hEMF2)
                        bResult = hRes.Equals(hEMF2)
                        CloseClipboard()
                    End If
                End If
            End If
            DeleteEnhMetaFile(hEMF)
        End If

        Return bResult
    End Function

    Public Shared Function GetEnhMetafileOffClipboard(ByVal hWnd As IntPtr, ByRef mf As Metafile) As Boolean
        Dim bResult As Boolean = False
        Dim hEMF As IntPtr

        If OpenClipboard(hWnd) Then
            If IsClipboardFormatAvailable(CF_ENHMETAFILE) Then
                hEMF = GetClipboardData(CF_ENHMETAFILE)
                If Not hEMF.Equals(New IntPtr(0)) Then
                    mf = New Metafile(hEMF, True)
                    bResult = True
                End If
            End If
            CloseClipboard()
        End If

        Return bResult
    End Function

End Class