Results 1 to 8 of 8

Thread: Drag/Drop Outlook message to form

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    2,012

    Drag/Drop Outlook message to form

    I want to be able to drag & drop an email message from Outlook onto my form & then save the message to a specified location & also save any attachments to a specified location. I created a label that will accept drag/drop & have the required event handlers in my code. The problem now is I don't know how to cast the dropped message to an Outlook.MailItem object.

    When I drop a message onto the label I get this error:
    "Unable to cast object of type 'System.Windows.Forms.DataObject' to type 'Microsoft.Office.Interop.Outlook._MailItem'"

    Is there a way to convert the DataObject directly to an MailItem object. Thanks for any help...

    This is the code I am trying.

    Code:
    Imports Microsoft.Office.Interop
    
    
        Private Sub Label1_DragEnter(sender As Object, e As DragEventArgs) Handles Label1.DragEnter
    
            Dim dataFormat = e.Data.GetFormats(False)(1)
            If dataFormat = "RenPrivateLatestMessages" Then
                e.Effect = DragDropEffects.Copy
            End If
    
        End Sub
    
        Private Sub Label1_DragDrop(sender As Object, e As DragEventArgs) Handles Label1.DragDrop
    
            Try
    
                If e.Data.GetFormats(False)(1) = "RenPrivateLatestMessages" Then
    
                    Dim oApp As Outlook._Application
                    Dim oMsg As Outlook._MailItem
    
                    oApp = New Outlook.Application
                    oMsg = CType(e.Data, Outlook._MailItem)  <-- stops here
    
                End If
    
            Catch ex As Exception
                'handle error
            End Try
    
        End Sub

  2. #2
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    26,424

    Re: Drag/Drop Outlook message to form

    You can find some helpful information here…

    https://learn.microsoft.com/en-us/do...ew=outlook-pia

  3. #3
    Fanatic Member
    Join Date
    Jul 2022
    Location
    Buford, Ga USA
    Posts
    631

    Re: Drag/Drop Outlook message to form

    I don't believe this can be handled natively. You have to build in the feature of accepting an OLE object, instead of the few simple items it can handle. This GitHub repository has some info on doing this https://github.com/yasoonOfficial/outlook-dndprotocol, it is in C# but maybe you can get enough information from the setup to make it work, or just write your Winform app in C#.

  4. #4

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    2,012

    Re: Drag/Drop Outlook message to form

    Quote Originally Posted by .paul. View Post
    You can find some helpful information here…

    https://learn.microsoft.com/en-us/do...ew=outlook-pia
    Thanks for the reply, but not really what I'm looking for. I know how to use the MailItem object. I just can't figure out how to cast the dropped object to a MailItem object.

  5. #5
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    26,424

    Re: Drag/Drop Outlook message to form

    Quote Originally Posted by nbrege View Post
    Thanks for the reply, but not really what I'm looking for. I know how to use the MailItem object. I just can't figure out how to cast the dropped object to a MailItem object.
    I don't think you can cast it. While searching, i saw something about creating a new MailItem from an object. Not sure if you can do that with a DataObject

  6. #6
    Fanatic Member
    Join Date
    Jul 2022
    Location
    Buford, Ga USA
    Posts
    631

    Re: Drag/Drop Outlook message to form

    I played around with the Winform (they're using .Net Framework 4.5) project from the GitHub link. It didn't have the OleDataReader that the WPF project did, so I moved that over. In this code it takes the dragged object and retrieves the mail item EntryID and then uses that to grab the mail item from outlook and you can do with it what you normally can.


    This is still C#; I've not converted it over to vb.net yet.
    Code:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using Outlook = Microsoft.Office.Interop.Outlook;
    
    namespace OutlookDndWinForms
    {
        public partial class Form1 : Form
        {
            private LocalDropTarget myDropTarget = null;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            public IOleDropTarget GetDropTarget()
            {
                this.myDropTarget = new LocalDropTarget();
                return myDropTarget;
            }
    
            class LocalDropTarget : IOleDropTarget
            {
                private OleOutlookData _outlookObj;
    
                public OleOutlookData outlookObj
                {
                    get => _outlookObj; 
                }
    
            public void OnDragDrop(System.Windows.DataObject d)
                {
                    Trace.WriteLine("OnDragDrop");
    
                    var formats = d.GetFormats();
    
                    foreach (var format in formats)
                        Trace.WriteLine(format);
    
                    System.Windows.DataObject obj = d;
                    MemoryStream data = (MemoryStream)obj.GetData("RenPrivateMessages");
    
                    if (data is MemoryStream)
                    {
    
                        OleDataReader reader = new OleDataReader(data);
                        _outlookObj = reader.ReadOutlookData();
    
                        Trace.WriteLine($"Subject:{_outlookObj.Items[0].Subject}");
                        Trace.WriteLine($"Entry ID:{_outlookObj.Items[0].EntryId}");
                    }
                }
    
                public int OleDragEnter(object pDataObj, int grfKeyState, long pt, ref int pdwEffect)
                {
                    Trace.WriteLine("OleDragEnter");
                    Marshal.FinalReleaseComObject(pDataObj);
                    return 0;
                }
    
                public int OleDragOver(int grfKeyState, long pt, ref int pdwEffect)
                {
                    Trace.WriteLine("OleDragOver");
                    return 0;
                }
    
                public int OleDragLeave()
                {
                    Trace.WriteLine("OleDragEnter");
                    return 0;
                }
    
                public int OleDrop(object pDataObj, int grfKeyState, long pt, ref int pdwEffect)
                {
                    Trace.WriteLine("OleDrop");
                    System.Windows.DataObject data = new System.Windows.DataObject(pDataObj);
    
                    OnDragDrop(data);
                    Marshal.FinalReleaseComObject(pDataObj);
                    return 0;
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
    
                var res = RegisterDragDrop(this.panel1.Handle, GetDropTarget());
                lbInfo.Items.Add("registered");
                
            }
    
            //Native imports
            [DllImport("ole32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
            public static extern int RegisterDragDrop(IntPtr hwnd, IOleDropTarget target);
    
            [ComImport(), Guid("00000122-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IOleDropTarget
            {
                [PreserveSig]
                int OleDragEnter(
                    [In, MarshalAs(UnmanagedType.Interface)]
                    object pDataObj,
                    [In, MarshalAs(UnmanagedType.U4)]
                    int grfKeyState,
                    [In, MarshalAs(UnmanagedType.U8)]
                    long pt,
                    [In, Out]
                    ref int pdwEffect);
    
                [PreserveSig]
                int OleDragOver(
                    [In, MarshalAs(UnmanagedType.U4)]
                    int grfKeyState,
                    [In, MarshalAs(UnmanagedType.U8)]
                    long pt,
                    [In, Out]
                    ref int pdwEffect);
    
                [PreserveSig]
                int OleDragLeave();
    
                [PreserveSig]
                int OleDrop(
                    [In, MarshalAs(UnmanagedType.Interface)]
                    object pDataObj,
                    [In, MarshalAs(UnmanagedType.U4)]
                    int grfKeyState,
                    [In, MarshalAs(UnmanagedType.U8)]
                    long pt,
                    [In, Out]
                    ref int pdwEffect);
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
    
            }
    
            private void btnFindMail_Click(object sender, EventArgs e)
            {
                OleOutlookData outlookData = myDropTarget.outlookObj;
    
                lbInfo.Items.Add($"Searching Outlook for Entry-ID: {outlookData.Items[0].EntryId}");
    
                lbInfo.Items.Add("Getting Outlook reference");
    
                Outlook.Application oApp = new Outlook.Application();
                var ns = oApp.GetNamespace("MAPI");
                var item = ns.GetItemFromID(outlookData.Items[0].EntryId) as Outlook.MailItem;
    
                lbInfo.Items.Add(item.SenderName);
                lbInfo.Items.Add(item.SenderEmailAddress);
                lbInfo.Items.Add(item.To);
    
                //oApp.Quit();
            }
        }
    }
    Attached Images Attached Images   

  7. #7
    Fanatic Member
    Join Date
    Jul 2022
    Location
    Buford, Ga USA
    Posts
    631

    Re: Drag/Drop Outlook message to form

    Here is the VB.NET version:

    Code:
    Imports System.Runtime.InteropServices
    Imports Outlook = Microsoft.Office.Interop.Outlook
    Imports System.Windows
    Imports System.IO
    
    Public Class Form1
    
        Dim myDropTarget As LocalDropTarget = Nothing
    
        Public Function GetDropTarget() As IOleDropTarget
            Me.myDropTarget = New LocalDropTarget
            Return myDropTarget
        End Function
    
        Friend Class LocalDropTarget
            Implements IOleDropTarget
    
            Private _outlookObj As OleOutlookData
    
            Public ReadOnly Property outlookObj As OleOutlookData
                Get
                    Return _outlookObj
                End Get
            End Property
    
            Public Sub OnDragDrop(d As System.Windows.DataObject)
                Trace.WriteLine("OnDragDrop")
    
                Dim frmts = d.GetFormats()
    
                For Each fmt In frmts
                    Trace.WriteLine(fmt)
                Next
    
                Dim obj As System.Windows.DataObject = d
                Dim data = CType(obj.GetData("RenPrivateMessages"), MemoryStream)
    
                If TypeOf data Is MemoryStream Then
    
                    Dim reader As OleDataReader = New OleDataReader(data)
                    _outlookObj = reader.ReadOutlookData()
    
                    Trace.WriteLine($"Subject:{_outlookObj.Items(CInt(0)).Subject}")
                    Trace.WriteLine($"Entry ID:{_outlookObj.Items(CInt(0)).EntryId}")
                End If
            End Sub
    
            Private Function IOleDropTarget_OleDragEnter(pDataObj As Object, grfKeyState As Integer, pt As Long, ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragEnter
    
                Trace.WriteLine("OleDragEnter")
                Marshal.FinalReleaseComObject(pDataObj)
                Return 0
            End Function
    
            Private Function IOleDropTarget_OleDragOver(grfKeyState As Integer, pt As Long, ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragOver
    
                Trace.WriteLine("OleDragOver")
                Return 0
            End Function
    
            Private Function IOleDropTarget_OleDragLeave() As Integer Implements IOleDropTarget.OleDragLeave
    
                Trace.WriteLine("OleDragLeave")
                Return 0
            End Function
    
            Private Function IOleDropTarget_OleDrop(pDataObj As Object, grfKeyState As Integer, pt As Long, ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDrop
    
                Trace.WriteLine("OleDrop")
                Dim data As System.Windows.DataObject = New System.Windows.DataObject(pDataObj)
    
                Me.OnDragDrop(data)
                Marshal.FinalReleaseComObject(pDataObj)
                Return 0
            End Function
        End Class
    
        <ComImport(),
        Guid("00000122-0000-0000-C000-000000000046"),
        InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)>
        Public Interface IOleDropTarget
    
            <PreserveSig()>
            Function OleDragEnter(<[In], MarshalAs(UnmanagedType.Interface)> ByVal pDataObj As Object,
                                  <[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
                                  <[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
                                  <[In]> ByRef pdwEffect As Integer) As Integer
    
            <PreserveSig()>
            Function OleDragOver(<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
                                 <[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
                                 <[In]> ByRef pdwEffect As Integer) As Integer
    
            <PreserveSig()>
            Function OleDragLeave() As Integer
    
            <PreserveSig()>
            Function OleDrop(<[In], MarshalAs(UnmanagedType.Interface)> ByVal pDataObj As Object,
                             <[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
                             <[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
                             <[In]> ByRef pdwEffect As Integer) As Integer
        End Interface
    
        'Native imports
        <DllImport("ole32.dll", ExactSpelling:=True, CharSet:=CharSet.Auto)>
        Private Shared Function RegisterDragDrop(ByVal hwnd As IntPtr, ByVal target As IOleDropTarget) As Integer
        End Function
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            'registering at load doesn't work, might need a timer or something
    
            'Dim Res = RegisterDragDrop(Me.Panel1.Handle, GetDropTarget())
    
            'If Res <> 0 Then
            '    MsgBox("Registering DragDrop was unsuccessful")
            '    Exit Sub
            'End If
    
            'lbInfo.Items.Add("registered")
        End Sub
    
        Private Sub btnFindMail_Click(sender As Object, e As EventArgs) Handles btnFindMail.Click
    
            Dim outlookData As OleOutlookData = myDropTarget.outlookObj
            lbInfo.Items.Add($"Searching Outlook for Entry-ID: {outlookData.Items(0).EntryId}")
            lbInfo.Items.Add("Getting Outlook reference")
    
            Dim oApp As New Outlook.Application
            Dim oNS As Outlook.NameSpace = oApp.GetNamespace("MAPI")
            Dim mailItem As Outlook.MailItem = oNS.GetItemFromID(outlookData.Items(0).EntryId)
    
            lbInfo.Items.Add(mailItem.SenderName)
            lbInfo.Items.Add(mailItem.SenderEmailAddress)
            lbInfo.Items.Add(mailItem.To)
    
        End Sub
    
        Private Sub btnRegister_Click(sender As Object, e As EventArgs) Handles btnRegister.Click
            Dim Res = RegisterDragDrop(Me.Panel1.Handle, GetDropTarget())
    
            If Res <> 0 Then
                MsgBox("Registering DragDrop was unsuccessful")
                Exit Sub
            End If
    
            lbInfo.Items.Add("registered")
        End Sub
    End Class
    OleOutlookData
    Code:
    Friend Class OleOutlookData
        Public Property StoreId As String
        Public Property FolderId As String
        Public Property Items As OleOutlookItemData()
    End Class
    
    Friend Class OleOutlookItemData
        Public Property Subject As String
        Public Property EntryId As String
        Public Property SearchKey As String
        Public Property MessageClass As String
    End Class
    OleDataReader
    Code:
    'converted from:
    ' --------------------------------------------------------------------------
    ' Licensed under MIT License.
    '
    ' Outlook DnD Data Reader
    ' 
    ' File     : OleDataReader.cs
    ' Author   : Tobias Viehweger <[email protected] / @mnkypete>
    '
    ' -------------------------------------------------------------------------- 
    Imports System.IO
    Imports System.Text
    
    Friend Class OleDataReader
        Private stream As MemoryStream
    
        Public Sub New(inStream As MemoryStream)
            stream = inStream
        End Sub
    
        Public Function ReadOutlookData() As OleOutlookData
            Dim reader As BinaryReader = New BinaryReader(stream)
    
            '1. First 4 bytes are the length of the FolderId (In bytes)
            ' Note: These are possibly uint? We don't expect it to be that long nevertheless..
            Dim folderIdLength As Integer = reader.ReadInt32()
    
            '2. Read FolderId
            Dim folderId = reader.ReadBytes(folderIdLength)
            Dim folderIdHex = ByteArrayToString(folderId)
    
            '3. Next 4 bytes are the StoreId length (In bytes)
            Dim storeIdLength As Integer = reader.ReadInt32()
    
            '4. Read StoreId
            Dim storeId = reader.ReadBytes(storeIdLength)
            Dim storeIdHex = ByteArrayToString(storeId)
    
            '5. There are now some bytes which are not identified yet..
            reader.ReadBytes(4)
            reader.ReadBytes(4)
            reader.ReadBytes(4) ' <== These appear to be folder dependent somehow..
    
            '6. Read items count, again, we assume int instead of uint because that much items
            '   => Other problems =)
            Dim itemCount As Integer = reader.ReadInt32()
    
            Dim items = New OleOutlookItemData(itemCount - 1) {}
    
            For i = 0 To itemCount - 1
                'First 4 bytes, represent the MAPI property 0x8014 ("SideEffects" in OlSpy)
                Dim sideEffects As Integer = reader.ReadInt32()
    
                'Next byte tells us the length of the message class string (i.e. IPM.Note)
                Dim classLength As Byte = reader.ReadByte()
    
                'Now, read type
                Dim messageClass = Encoding.ASCII.GetString(reader.ReadBytes(classLength))
    
                'Next, read the unicode char (!) count of the subject 
                ' Note: It seems that Outlook limits this to 255, cross reference mail spec sometime..
                Dim subjectLength As Byte = reader.ReadByte()
    
                'Read the subject, note that this is unicode, so we need to read 2 bytes per char!
                Dim subject = Encoding.Unicode.GetString(reader.ReadBytes(subjectLength * 2))
    
                'Next up: EntryID including it's length (same as for store + folder)
                Dim entryIdLength As Integer = reader.ReadInt32()
                Dim entryId = reader.ReadBytes(entryIdLength)
                Dim entryIdHex = ByteArrayToString(entryId)
    
                'Now the SearchKey MAPI property of the item
                Dim searchKeyLength As Integer = reader.ReadInt32()
                Dim searchKey = reader.ReadBytes(searchKeyLength)
                Dim searchKeyHex = ByteArrayToString(searchKey)
    
                'Some more stuff which is not quite clear, the next 4 bytes seem to be always
                ' => E0 80 E9 5A
                reader.ReadBytes(4)
    
                'The next 24 byte are some more flags which are not worked out yet, afterwards
                ' the next item begins
                reader.ReadBytes(24)
    
                items(i) = New OleOutlookItemData With {
                    .EntryId = entryIdHex,
                    .MessageClass = messageClass,
                    .SearchKey = searchKeyHex,
                    .Subject = subject
                }
            Next
    
            Dim data As OleOutlookData = New OleOutlookData()
            data.StoreId = storeIdHex
            data.FolderId = folderIdHex
            data.Items = items
    
            Return data
        End Function
    
        Private Function ByteArrayToString(ba As Byte()) As String
            Dim hex As StringBuilder = New StringBuilder(ba.Length * 2)
            For Each b In ba
                hex.AppendFormat("{0:x2}", b)
            Next
            Return hex.ToString()
        End Function
    End Class
    Attached Images Attached Images  

  8. #8
    Fanatic Member
    Join Date
    Jul 2022
    Location
    Buford, Ga USA
    Posts
    631

    Re: Drag/Drop Outlook message to form

    Here is the VS2019 project if interested https://1drv.ms/u/s!AkG6_LvJpkR7j6hn...wsshA?e=fxXieL

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width