Search code examples
vb.netoutlookdrag-and-drop

Drag and Drop to Outlook Message to Windows Form


I currently have a Windows Forms application that accepts drag and dropped files from other applications. Everything seemed to be working smoothly until we discovered that if we added an email attached to an email, then things got weird.

For example, users had no problem dragging attached jpg or pdf files directly from emails into the application. But when an email (.msg file) was attached to an email, and it was the attached email that was trying to be added to the program, then here's what happens: the following code is able to correctly read the name of the selected attachment that was dragged into the program, but the actual file that is copied is the main containing e-mail (including all the attachments).

Is there any way that I can just pull out the selected/dragged message?

Thanks!!

Here's the entire function:

Public Shared Function HandleFileDrops(ByVal e As System.Windows.Forms.DragEventArgs) As String
    ' Based on and Borrowed from http://allandynes.com/2015/10/vb-net-drag-and-drop-from-outlook/
    Try
        If e.Data.GetDataPresent(DataFormats.FileDrop) Then
            ' We have a file so lets pass it to the calling form
            Dim Filename As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
            HandleFileDrops = Filename(0)
        ElseIf e.Data.GetDataPresent("FileGroupDescriptor") Then
            ' We have a embedded file. First lets try to get the file name out of memory
            Dim theStream As IO.Stream = CType(e.Data.GetData("FileGroupDescriptor"), IO.Stream)
            Dim fileGroupDescriptor(512) As Byte
            theStream.Read(fileGroupDescriptor, 0, 512)
            Dim fileName As System.Text.StringBuilder = New System.Text.StringBuilder("")
            Dim i As Integer = 76

            While Not (fileGroupDescriptor(i) = 0)
                fileName.Append(Convert.ToChar(fileGroupDescriptor(i)))
                System.Math.Min(System.Threading.Interlocked.Increment(i), i - 1)
            End While

            theStream.Close()
            ' We should have the file name or if its an email, the subject line. Create our temp file based on the temp path and this info
            Dim myTempFile As String = IO.Path.GetTempPath & fileName.ToString
            ' Look to see if this is a email message. If so save that temporarily and get the temp file.
            If InStr(myTempFile, ".msg") > 0 Then
                Dim objOL As New Microsoft.Office.Interop.Outlook.Application
                Dim objMI As Microsoft.Office.Interop.Outlook.MailItem
                If objOL.ActiveExplorer.Selection.Count > 1 Then
                    MsgBox("You can only drag and drop one item at a time into this screen. Only the first item you selected will be used.", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "One Item At A Time")
                End If
                For Each objMI In objOL.ActiveExplorer.Selection()
                    objMI.SaveAs(myTempFile)
                    Exit For
                Next
                objOL = Nothing
                objMI = Nothing
            Else
                ' If its a attachment we need to pull the file itself out of memory
                Dim ms As IO.MemoryStream = CType(e.Data.GetData("FileContents", True), IO.MemoryStream)
                Dim FileBytes(CInt(ms.Length)) As Byte
                ' read the raw data into our variable
                ms.Position = 0
                ms.Read(FileBytes, 0, CInt(ms.Length))
                ms.Close()
                ' save the raw data into our temp file
                Dim fs As IO.FileStream = New IO.FileStream(myTempFile, IO.FileMode.OpenOrCreate, IO.FileAccess.Write)
                fs.Write(FileBytes, 0, FileBytes.Length)
                fs.Close()
            End If
            ' Make sure we have a actual file and also if we do make sure we erase it when done
            If IO.File.Exists(myTempFile) Then
                ' Assign the file name to the add dialog
                HandleFileDrops = myTempFile
            Else
                HandleFileDrops = String.Empty
            End If
        Else
            Throw New System.Exception("An exception has occurred.")
        End If
    Catch ex As Exception
        MsgBox("Could not copy file from memory. Please save the file to your hard drive first and then retry your drag and drop.", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Drag and Drop Failed")
        HandleFileDrops = String.Empty
    End Try

End Function

This is the section in particular that handles the e-mail:

                Dim objOL As New Microsoft.Office.Interop.Outlook.Application
                Dim objMI As Microsoft.Office.Interop.Outlook.MailItem
                If objOL.ActiveExplorer.Selection.Count > 1 Then
                    MsgBox("You can only drag and drop one item at a time into this screen. Only the first item you selected will be used.", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "One Item At A Time")
                End If
                For Each objMI In objOL.ActiveExplorer.Selection()
                    objMI.SaveAs(myTempFile)
                    Exit For
                Next
                objOL = Nothing
                objMI = Nothing

Solution

  • After really looking at the code that I borrowed from elsewhere on the web, I now see exactly what it does. The section of code that saves Outlook messages creates a new instance of the current Outlook object and then calls that object to pull in its selected items, which will happen to be the main selected email - not the attached email that was dragged over. I was able to add a couple lines of code to do some comparisons on either the email or attachment name to see what was what.

    This now works well for my needs:

                    Dim objOL As New Microsoft.Office.Interop.Outlook.Application
                    Dim objMI As Microsoft.Office.Interop.Outlook.MailItem
                    If objOL.ActiveExplorer.Selection.Count > 1 Then
                        MsgBox("You can only drag and drop one item at a time into this screen. Only the first item you selected will be used.", MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "One Item At A Time")
                    End If
    
                    'If the message itself has the same name as fileName, then just save the selected MailObject
                    'Otherwise, iterate through all the attachments to the MailObject and save the one that hase that same name as the fileName
                    For Each objMI In objOL.ActiveExplorer.Selection()
                        If objMI.Subject = fileName.ToString() Then
                            objMI.SaveAs(myTempFile)
                        Else 
                            Dim objAttach As Microsoft.Office.Interop.Outlook.Attachments = objMI.Attachments
                            For Each attach As Microsoft.Office.Interop.Outlook.Attachment In objAttach
                                If attach.FileName = fileName.ToString() Then
                                    attach.SaveAsFile(myTempFile)
                                End If
                            Next
                        End If
    
                        Exit For
                    Next
                    objOL = Nothing
                    objMI = Nothing