Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C#
Article

Drag-and-Drop Attached File From Outlook (97 and above) to C# Window Form

Rate me:
Please Sign up or sign in to vote.
4.55/5 (31 votes)
23 May 20048 min read 276.9K   2K   89   64
How to drag-and-drop a file attached to an e-mail message to another program

Introduction

Recently, I decided to write a quick program that would allow me to drag-and-drop an attached zip file from an e-mail message to a C# Windows Form program so that I could extract specific types of files to be printed. Little did I know that I would be stepping into a relatively unknown area of drag-and-drop technology, especially it appears as it relates to e-mail attachments.

The purpose of this article is to provide a simple example of how to drag-and-drop e-mail attachments from Outlook to a C# Windows Form program. This article will not teach the fundamentals of drag-and-drop. There are existing articles at the CodeProject.com site that cover the introduction to this technology. Hopefully, others will find this a good template for approaching such tasks.

Background

I teach computer programming (C++ mainly) at a local college. During this quarter, I offered to let the the students e-mail their completed homework to me. Little did I understand where this would lead me.

The issue became the amount of manual work required to extract the "solution" sets for potentially each homework problem, open a solution, print each of the source files, open the next solution, etc. When at first only two students sent in homework this way, no problem. But when ten students did it during week 3, I was spending more time getting the source to do the grading than I was grading each problem. Being essentially lazy like all good programmers, I decided to program a solution.

Since the students were sending their complete solutions for each problem as a zipped file via e-mail, logically it made sense to create a solution that allowed me to drag-and-drop the attached zip file direct from the email message to the program. I could have extracted the file to a directory and then drag-and-drop to the program, this seemed like wasted effort. Drag-and-drop is supposed to be easy and I had implemented file drap-and-drop at least twice before in programs, so I saw no reason to not start at the e-mail message. Besides, I figured I could find references on the internet if I had problems.

Little did I realize that I was about to step into quicksand.

Having written code for 35 years and C/C++ code for over 25 years, I have over the last two years begun to transition to C# for all but the highest performance needs. I have found C# to be a relatively easy transition from C++ and a great boon for speeding up the development process. I have written Windows code before, during and now after MFC. I also do work with VB and VBA, so I had long missed the easy Windows program development of VB when working with C++, my preferred language (I am a geek!). C# has brought the of VB Windows programming to C++ programmers and increased my efficiency by orders of magnitude when I need to provide an interface to users. While I have not tried to do it, the concepts should work under VB.NET as I have implemented drag-and-drop on a VB.NET program awhile back.

And now we start the trip .....

Using the code

As I mentioned above, I decided to write a program that would allow me to drag a zip file attached to an email and drop it onto my Windows Form program which would extract the sources and print them for me. This example will not go that far. We will walk through creating a Windows Form program in C# which will create a copy of an attached file from an e-mail. Hopefully, this will be enough of a template for other users to implement this feature in their own code.

Whenever I start a new project or task, I first focus on the stuff I don't know. So started with my usual research and did a Google search for drag-and-drop on Outlook email. The Google search returned less than 100 references, unusual for what I figured was well-known technology. Digging into the references, I only found three that actually referred to this type of operation. One reference was for C++ and the second for a Pascal program (which really amazed me). I fully expected to find 100s of references to similar work, so you imagine my surprise. The third valid reference was a response to an e-mail that gave me the clues as how to accomplish what I wanted to do. With the information from the email response, I coded and finished the work in about an hour using a zip file class I had earlier developed to use the WinZip command line program.

My sample program will give you the basic gory details of handling the drag-and-drop of a single file from Outlook. The program simply copies the file to the TEMP directory and makes sure that it was created. Nothing fancy here. First I walk through setting up a Windows Form program and put in the basic event handlers for drag-and-drop. Then I get into the adding the code necessary to deal with dropping an email attachment from Outlook into the program.

Step 1: Create a C# Windows. Start up Visual Studio.Net and create a new project. Select a C# Windows Application. For our test purposes, give it the name TestEmailDragDrop. Let it build the basic structure.

Step 2: Select the properties tab for the form. Find the "Allow Drop" property and change it from False to True. This will allow us to handle the drag-and-drop events.

Step 3: Select the Events option (the lightning strike to right of properties) and find the "DragEnter" event. Click in the blank area to the right of the "DragEnter" portion and double-click. This will create the code below:

Step 4: Again in the Events list, find the "DragDrop" event. Click in the blank area to the right and double-click. This will create the code below:

Step 5: Now we have the basic code in place to handle the drag-and-drop operation. Go to the DragEnter event handler and add the following code:

C#
private void Form1_DragEnter(object sender,
    System.Windows.Forms.DragEventArgs e)
{
  // for this program, we allow a file to be dropped from Explorer
  if (e.Data.GetDataPresent(DataFormats.FileDrop))
  {  e.Effect = DragDropEffects.Copy;}
  //    or this tells us if it is an Outlook attachment drop
  else if (e.Data.GetDataPresent("FileGroupDescriptor"))
  {  e.Effect = DragDropEffects.Copy;}
  //    or none of the above
  else
  {  e.Effect = DragDropEffects.None;}
}

We are setting up here to either receive a file from Explorer (DataFormats.FileDrop) and from Outlook ("FileGroupDescriptor"). I won't claim to understand all the details of this data format, but I was lucky enough to find a couple of examples of how to handle (one in Pascal of all languages) this format. At this point, we don't care about the format. We just want to have the effects property for our event set to the DragDropEffects.Copy value. If other than these two, simply set the effects to DragDropEffects.None.

Now that we have the DragEnter event handled, we turn our focus to the DataDrop event. This is where we get cute. Below is the code to handle the DragDrop event for both the FileDrop and the FileGroupDescriptor types of objects:

C#
private void Form1_DragDrop(object sender,
     System.Windows.Forms.DragEventArgs e)
{
  string [] fileNames = null;

  try
  {
    if ( e.Data.GetDataPresent(DataFormats.FileDrop,false) == true)
    {
      fileNames = (string []) e.Data.GetData(DataFormats.FileDrop);
      // handle each file passed as needed
      foreach( string fileName in fileNames)
      {
        // do what you are going to do with each filename
      }
    }
    else if (e.Data.GetDataPresent("FileGroupDescriptor"))
    {
      //
      // the first step here is to get the filename
      // of the attachment and
      // build a full-path name so we can store it
      // in the temporary folder
      //

      // set up to obtain the FileGroupDescriptor
      // and extract the file name
      Stream theStream = (Stream) e.Data.GetData("FileGroupDescriptor");
      byte [] fileGroupDescriptor = new byte[512];
      theStream.Read(fileGroupDescriptor,0,512);
      // used to build the filename from the FileGroupDescriptor block
      StringBuilder fileName = new StringBuilder("");
      // this trick gets the filename of the passed attached file
      for(int i=76; fileGroupDescriptor[i]!=0; i++)
      {  fileName.Append(Convert.ToChar(fileGroupDescriptor[i]));}
      theStream.Close();
      string path = Path.GetTempPath();
          // put the zip file into the temp directory
      string theFile = path+fileName.ToString();
          // create the full-path name

      //
      // Second step:  we have the file name.
      // Now we need to get the actual raw
      // data for the attached file and copy it to disk so we work on it.
      //

      // get the actual raw file into memory
      MemoryStream ms = (MemoryStream) e.Data.GetData(
          "FileContents",true);
      // allocate enough bytes to hold the raw data
      byte [] fileBytes = new byte[ms.Length];
      // set starting position at first byte and read in the raw data
      ms.Position = 0;
      ms.Read(fileBytes,0,(int)ms.Length);
      // create a file and save the raw zip file to it
      FileStream fs = new FileStream(theFile,FileMode.Create);
      fs.Write(fileBytes,0,(int)fileBytes.Length);

      fs.Close();  // close the file

      FileInfo tempFile = new FileInfo(theFile);

      // always good to make sure we actually created the file
      if ( tempFile.Exists == true)
      {
        // for now, just delete what we created
        tempFile.Delete();
      }
      else
      {  Trace.WriteLine("File was not created!");}
    }

  }
  catch (Exception ex)
  {
    Trace.WriteLine("Error in DragDrop function: " + ex.Message);

    // don't use MessageBox here - Outlook or Explorer is waiting !
  }

}

I won't attempt to explain the FileDrop type. This is better explained in other CodeProject examples. I will focus on the FileGroupDescriptor type.

For the real geeks, the FileGroupDescriptor structure is explained at the MSDN web page MSDN FileGroupDescriptor (assuming Microsoft doesn't change the link address). Basically, it is a variable length structure with a 4-byte count at the lead followed by a FILEDESCRIPTOR Structure that holds the information on the file being passed. The problem is that the file does not exist other than in memory (the Clipboard) (Outlook 97 actually creates the file in a TEMP area) so we can't reference it directly. My simple approach here is to copy to a memory-based file before actually creating it in the TEMP directory. The user is welcome to modify the code to do something more to their liking.

In this case, we are assuming only one file is being passed. I didn't need to handle multiple files so I haven't coded that capability yet.

The first step in the code above is to get the FileGroupDescriptor structure by putting it into a byte array. This is simpler than trying to marshall the structure and works for this simple example. We obtain the byte array by casting the object that was dropped (the FileGroupDescriptor block) to a Stream object. We then read the FileGroupDescriptor into a byte array (fileGroupDescriptor). Once we have the FileGroupDescriptor, we simply point to the 76th byte in the array which is the starting point for the filename of the attached file and build up the file name by converting the byte to a Unicode char and appending it to a StringBuilder object. When we finish building the filename, we close the Stream object, get the path to the TEMP file area and append the filename after converting the StringBuilder object to a string (ToString()).

We now know the name of the file that we are being passed. If you don't care what the name is, you could skip this first step and go direct to Step 2.

We start the second step by getting the FileContents data and casting it to a MemoryStream object. We calculate the number of bytes needed, allocate a byte array of the correct size and copy (Read) the information into our byte array. We then create a FileStream object (fs) giving it the name that was passed in the FileGroupDescriptor block above. We copy (Write) the data to the file and close it. We now have a copy of the attached file.

The last thing the code does is check to make sure the file exists, just to be safe.

Voila! Drag-and-drop of an attached file in a Outlook email

History

  • March 5, 2004: (Library Version 1.0.0.0)
    • This is the initial release of the article. No changes.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
President Binary Star Technology, Inc
United States United States
have programmed since 1969, back in the good old days. Remember when C was first announced to the world and the how so much better it was over existing computer languages.

Know most languages but enjoy low-level and database programming most. Doing most of my work in C#, an absolutely great language.

Comments and Discussions

 
AnswerRe: FileContents returns null Pin
Marek.T2-Apr-09 2:41
Marek.T2-Apr-09 2:41 
GeneralRe: FileContents returns null Pin
shah.mihir@ymail.com28-Jun-12 16:55
shah.mihir@ymail.com28-Jun-12 16:55 
QuestionDrag and Drop from Outlook to ASP.Net Pin
sumibell8-Mar-07 23:23
sumibell8-Mar-07 23:23 
AnswerRe: Drag and Drop from Outlook to ASP.Net Pin
tgueth9-Mar-07 1:23
professionaltgueth9-Mar-07 1:23 
GeneralDrag and Drop from Outlook to ASP.Ne Pin
sumibell8-Mar-07 23:20
sumibell8-Mar-07 23:20 
GeneralVery useful and well put together! Pin
beckerben14-Dec-06 3:43
beckerben14-Dec-06 3:43 
GeneralRe: Very useful and well put together! Pin
tgueth18-Dec-06 13:48
professionaltgueth18-Dec-06 13:48 
Questionoutlook attachment disappears Pin
juergegli26-Nov-06 23:43
juergegli26-Nov-06 23:43 
hello,

the code above works fine. but for some reason, the attachments disappears after dropping it to a form. i have to press ctrl while dragging, otherwise its not possible - so it shouldn't be the problem that i move the file instead of copy it.

does enyone have an idea what the problem could be?

here is my code. it should be more or less the same as in the example.

thanks
juerg



---- code ----

private void tree_DragDrop(object sender, DragEventArgs e){
// mostly copied from http://www.codeproject.com/csharp/testemaildragdrop.asp

tree.SelectedItem = rowDroppedOn;

string tempFilePath = Path.GetTempPath();

string tempFileName = null;

try
{
if ( e.Data.GetDataPresent(DataFormats.FileDrop,false) == true){
string [] fileNames = null;
fileNames = (string []) e.Data.GetData(DataFormats.FileDrop);
// handle each file passed as needed
if (fileNames.Length > 0){
FileInfo origFileInfo = new FileInfo(fileNames[0]);
fileNames[0] = tempFilePath + origFileInfo.Name;

FileInfo tempFileInfo = new FileInfo(fileNames[0]);
if ( tempFileInfo.Exists == true){
// for now, just delete what we created
tempFileInfo.Delete();
}
File.Copy(origFileInfo.FullName, tempFileInfo.FullName) ;
tempFileName = tempFileInfo.FullName;
}
}
else if (e.Data.GetDataPresent("FileGroupDescriptor")) {
// the first step here is to get the filename
// of the attachment and
// build a full-path name so we can store it
// in the temporary folder
// set up to obtain the FileGroupDescriptor
// and extract the file name
Stream theStream = (Stream) e.Data.GetData("FileGroupDescriptor");
byte [] fileGroupDescriptor = new byte[512];
theStream.Read(fileGroupDescriptor,0,512);
// used to build the filename from the FileGroupDescriptor block
StringBuilder fileName = new StringBuilder("");
// this trick gets the filename of the passed attached file
for(int i=76; fileGroupDescriptor[i]!=0; i++)
{ fileName.Append(Convert.ToChar(fileGroupDescriptor[i]));}
theStream.Close();

// create the full-path name
string theFile = tempFilePath+fileName.ToString();
// Second step: we have the file name.
//Now we need to get the actual raw
// data for the attached file and copy it to disk so we work on it.
// get the actual raw file into memory
MemoryStream ms = (MemoryStream) e.Data.GetData("FileContents");
// allocate enough bytes to hold the raw data
byte [] fileBytes = new byte[ms.Length];
// set starting position at first byte and read in the raw data
ms.Position = 0;
ms.Read(fileBytes,0,(int)ms.Length) ;
// create a file and save the raw zip file to it
FileStream fs = new FileStream(theFile,FileMode.Create);
fs.Write(fileBytes,0,(int)fileBytes.Length);
ms.Close(); // close the memorystrem
fs.Close(); // close the file

FileInfo tempFile = new FileInfo(theFile);
tempFileName = tempFile.FullName;
}
if (tempFileName != null){
try {
dropedFilePath = tempFileName;
// projectspecific
((PPDMBreakdownViewSet)SolutionModel.Instance.ActiveModule.Container.CurrentViewSet).newDocumentDrop.PerformAction();
} catch (Exception ex) {
throw ex;
} finally {
//File.Delete(tempFileName);
}
}
}catch (Exception ex) {
// don't use MessageBox here - Outlook or Explorer is waiting !
Trace.WriteLine("Error in DragDrop function: " + ex.Message);
}

}


private void tree_DragEnter(object sender, DragEventArgs e){
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{e.Effect = DragDropEffects.Copy ;}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{e.Effect = DragDropEffects.Copy ;}
else
{ e.Effect = DragDropEffects.None;}
}



QuestionRe: outlook attachment disappears Pin
farahota28-Nov-09 15:58
farahota28-Nov-09 15:58 
AnswerRe: outlook attachment disappears Pin
Reto Ravasio20-May-10 1:53
Reto Ravasio20-May-10 1:53 
GeneralGoing to outlook Pin
Gowron44828-Sep-06 6:17
Gowron44828-Sep-06 6:17 
JokeGreat Sample Pin
gratajik22-Jun-06 5:14
gratajik22-Jun-06 5:14 
GeneralTrouble when dragged item is an attachment Pin
Balram Vedavyas16-May-05 8:53
sussBalram Vedavyas16-May-05 8:53 
GeneralGrabbing the whole e-mail Pin
DonStavneak28-Feb-05 10:00
DonStavneak28-Feb-05 10:00 
GeneralRe: Grabbing the whole e-mail Pin
DonStavneak26-Apr-05 2:34
DonStavneak26-Apr-05 2:34 
GeneralRe: Grabbing the whole e-mail Pin
Joe Hanna7-Jun-05 2:13
Joe Hanna7-Jun-05 2:13 
GeneralRe: Grabbing the whole e-mail Pin
DonStavneak26-Apr-05 2:34
DonStavneak26-Apr-05 2:34 
GeneralVery good, but is there a sample in C++ Pin
eddikonstantin10-Feb-05 22:28
eddikonstantin10-Feb-05 22:28 
GeneralRe: Very good, but is there a sample in C++ Pin
tgueth11-Feb-05 2:32
professionaltgueth11-Feb-05 2:32 
GeneralRe: Very good, but is there a sample in C++ Pin
eddikonstantin13-Feb-05 20:08
eddikonstantin13-Feb-05 20:08 
GeneralDouble-clicking an attachment Pin
SerialHobbyist11-Jun-04 23:31
SerialHobbyist11-Jun-04 23:31 
GeneralLink to my solution Pin
kvramana31-May-04 23:15
kvramana31-May-04 23:15 
GeneralProblem with Outlook XP Pin
Anonymous29-May-04 6:05
Anonymous29-May-04 6:05 
GeneralRe: Problem with Outlook XP Pin
tgueth31-May-04 14:49
professionaltgueth31-May-04 14:49 
GeneralRe: Problem with Outlook XP Pin
kvramana31-May-04 23:18
kvramana31-May-04 23:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.