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:
private void Form1_DragEnter(object sender,
System.Windows.Forms.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;}
}
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:
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);
foreach( string fileName in fileNames)
{
}
}
else if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
Stream theStream = (Stream) e.Data.GetData("FileGroupDescriptor");
byte [] fileGroupDescriptor = new byte[512];
theStream.Read(fileGroupDescriptor,0,512);
StringBuilder fileName = new StringBuilder("");
for(int i=76; fileGroupDescriptor[i]!=0; i++)
{ fileName.Append(Convert.ToChar(fileGroupDescriptor[i]));}
theStream.Close();
string path = Path.GetTempPath();
string theFile = path+fileName.ToString();
MemoryStream ms = (MemoryStream) e.Data.GetData(
"FileContents",true);
byte [] fileBytes = new byte[ms.Length];
ms.Position = 0;
ms.Read(fileBytes,0,(int)ms.Length);
FileStream fs = new FileStream(theFile,FileMode.Create);
fs.Write(fileBytes,0,(int)fileBytes.Length);
fs.Close();
FileInfo tempFile = new FileInfo(theFile);
if ( tempFile.Exists == true)
{
tempFile.Delete();
}
else
{ Trace.WriteLine("File was not created!");}
}
}
catch (Exception ex)
{
Trace.WriteLine("Error in DragDrop function: " + ex.Message);
}
}
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.