32

I'm working on a windows client written in WPF with C# on .Net 3.5 Sp1, where a requirement is that data from emails received by clients can be stored in the database. Right now the easiest way to handle this is to copy and paste the text, subject, contact information and time received manually using an arthritis-inducing amount of ctrl-c/ctrl-v.

I thought that a simple way to handle this would be to allow the user to drag one or more emails from Outlook (they are all using Outlook 2007 currently) into the window, allowing my app to extract the necessary information and send it to the backend system for storage.

However, a few hours googling for information on this seem to indicate a shocking lack of information about this seemingly basic task. I would think that something like this would be useful in a lot of different settings, but all I've been able to find so far have been half-baked non-solutions.

Does anyone have any advice on how to do this? Since I am just going to read the mails and not send anything out or do anything evil, it would be nice with a solution that didn't involve the hated security pop ups, but anything beats not being able to do it at all.

Basically, if I could get a list of all the mail items that were selected, dragged and dropped from Outlook, I will be able to handle the rest myself!

Thanks!

Rune

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
Rune Jacobsen
  • 9,907
  • 11
  • 58
  • 75
  • Rune are you still looking for an answer to this? – cgreeno Dec 23 '08 at 10:25
  • Hi Chris - yes I am, I have just been on a completely different project for the last month. I will try to follow up as soon as I have my head above water. – Rune Jacobsen Feb 05 '09 at 08:09
  • Shouldnt you tie into your Exchange server, and access the Outlook mailbox that way? Why not look into CDO Objects? This allows you to get message collections from Exchange server. http://msdn.microsoft.com/en-us/library/ms978698.aspx – D3vtr0n Jun 10 '09 at 16:46
  • Devtron - It is a requirement in this case to handle drag'n'drop directly from Outlook to the WPF app. Thus I have to interpret the data somehow. Otherwise, CDO or Redemption would definately be considered! – Rune Jacobsen Jun 12 '09 at 06:24

5 Answers5

37

I found a great article that should do exactly what you need to.

UPDATE

I was able to get the code in that article working in WPF with a little tweaking, below are the changes you need to make.

Change all references from System.Windows.Forms.IDataObject to System.Windows.IDataObject

In the OutlookDataObject constructor, change

FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);

To

FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance);

Change all DataFormats.GetFormat calls to DataFormats.GetDataFormat

Change the SetData implementation from

public void SetData(string format, bool autoConvert, object data)
{
    this.underlyingDataObject.SetData(format, autoConvert, data);
}

TO

public void SetData(string format, object data, bool autoConvert)
{
    this.underlyingDataObject.SetData(format, data, autoConvert);
}

With those changes, I was able to get it to save the messages to files as the article did. Sorry for the formatting, but numbered/bulleted lists don't work well with code snippets.

Bryce Kahle
  • 8,159
  • 1
  • 23
  • 26
  • 1
    Thanks - I also found this one and tried to use it, however I am banging my head into the differences between System.Windows.Forms.IDataObject (the one used in Windows Forms and this article) and System.Windows.IDataObject (the one that WPF uses). I don't know nearly enough COM (or whatever that scary looking stuff is) to get this to work, it would seem..! – Rune Jacobsen Jun 10 '09 at 06:46
  • I have updated my post to reflect the changes needed to work in WPF. – Bryce Kahle Jun 11 '09 at 14:37
  • 1
    Thanks again - I am pretty close to this working for me, just have to deal with some exceptions when dragging multiple messages. Congrats on figuring out a difficult one. :) – Rune Jacobsen Jun 14 '09 at 20:39
  • did you find any solution to this problem? – yrahman Apr 01 '15 at 21:14
5

I found a lot of solutions suggesting you use the “FileGroupDescriptor” for all the file names and the “FileContents” on the DragEventArgs object to retrieve the data of each file. The “FileGroupDescriptor” works fine for the email message names, but “FileContents” returns a null because the implementation of the IDataObject in .Net cannot handle the IStorage object that is returned by COM.

David Ewen has a great explanation, excellent sample and code download that works great at http://www.codeproject.com/KB/office/outlook_drag_drop_in_cs.aspx.

Timo
  • 336
  • 3
  • 15
  • Thanks, this looks very promising - however, it is based on a WinForms (?) IDataObject, while the IDataObject used in WPF is slightly different. For this to work, I would need a "lossless" way to convert between the two, or...? – Rune Jacobsen Mar 04 '09 at 10:01
3

In your Xaml you need to set up your Event:

<TextBlock
        Name="myTextBlock"  
        Text="Drag something into here"
        AllowDrop="True" 
        DragDrop.Drop="myTextBlock_Drop"
        />

Once you have Set AllowDrop = True and Set you drop event then go to the code behind and set up your event:

private void myTextBlock_Drop(object sender, DragEventArgs e)
{
         // Mark the event as handled, so TextBox's native Drop handler is not called.
         e.Handled = true;
         Stream sr;

          //Explorer 
          if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
              //Do somthing

        //Email Message Subject 
        if (e.Data.GetDataPresent("FileGroupDescriptor"))
        {
            sr = e.Data.GetData("FileGroupDescriptor") as Stream;
                StreamReader sr = new StreamReader(sr2);//new StreamReader(strPath, Encoding.Default);
            //Message Subject
                    string strFullString = sr.ReadToEnd();
         }


}

If you wish to break it down further you can use: FILEDESCRIPTOR or FILECONTENTS as outline in the following article

your other option is to tie into outlooks MS Office Primary Interop Assemblies and break the message apart that way.

cgreeno
  • 31,943
  • 7
  • 66
  • 87
  • The FileGroupDescriptor doesn't contain text, just binary data. The resulting strFullString contains nothing readable – Thomas Levesque Jun 08 '09 at 10:50
  • I did test the code before I posted it, perhaps you need to change the Encoding? – cgreeno Jun 08 '09 at 13:25
  • 1
    cgreeno - I forgot to comment that I tried this, but all I get is a bunch of zeroes and somewhere in the middle, the subject of the message. I.e. if I drag a message with the subject "Adresse", I get this: \0\0\0 [etc., a lot of them] Adresse.msg \0\0\0\0\0\0 [a lot more of them]. If I remove the \0 characters I can get at the subject, but no sign of where I can find the message body, any attachments, the sender, or other information that is needed. Thanks for trying tho, much appreciated. :) – Rune Jacobsen Jun 09 '09 at 06:05
1

I think Shell Style Drag and Drop in .NET (WPF and WinForms) can help you. Once you can respond to drag drop using the COM Interfaces, you should be able to get the data out of outlook.

1

I assume that you have an Exchange server running behind Outlook.

What you can do is to retrieve the mail from the Exchange server and store its location in your database based on the mail's EntryID and StoreID. Here's a VB.Net snippet:

Imports Microsoft.Office.Interop

Public Class OutlookClientHandler

Private _application As Outlook.Application
Private _namespace As Outlook.NameSpace

Public Sub New()
    If Process.GetProcessesByName("outlook".ToLower).Length > 0 Then
        _application = New Outlook.Application
    Else
        Dim startInfo As ProcessStartInfo = New ProcessStartInfo("outlook.exe")
        startInfo.WindowStyle = ProcessWindowStyle.Minimized
        Process.Start(startInfo)

        _application = New Outlook.Application
    End If
End Sub

' Retrieves the specified e-mail from Outlook/Exchange via the MAPI
Public Function GetMailItem(ByVal entryID as String, ByVal storeID as String) As Outlook.MailItem
    _namespace = _application.GetNamespace("MAPI")
    Dim item As Outlook.MailItem
    Try
        item = _namespace.GetItemFromID(entryID, storeID)
    Catch comex As COMException
        item = Nothing ' Fugly, e-mail wasn't found!
    End Try

    Return item
End Function
End Class

I guess you are comfortable with using the MAPI, otherwise you can read up here: http://msdn.microsoft.com/en-us/library/cc765775(v=office.12).aspx

To retrieve the selected e-mails from outlook:

Public Function GetSelectedItems() As List(Of Object) 
    Dim items As List(Of Object) = New List(Of Object)

    For Each item As Object In _application.ActiveExplorer().Selection
        items.Add(item)
    Next

    Return items
End Function

After you've retrieved the e-mails from Outlook you can just push them into your database! Save their EntryID and StoreID (you might want to store their parent's (the folder's) EntryID and StoreID as well).

Zolomon
  • 9,359
  • 10
  • 36
  • 49