Getting attachments from Outlook - c#

I am new 2 C# and I have been given a task... I have to write a C# code to download the sent email attachments and subject of email from Outlook 2007 to a local drive or any specified location. How do I do that? I am able to get the attachments which are in inbox. Can any one please help me getting the mails sent through Outlook?
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.NewMail += new
Microsoft.Office.Interop.Outlook.
ApplicationEvents_11_NewMailEventHandler(ThisApplication_NewMail);
}
private void ThisApplication_NewMail()
{
Outlook.MAPIFolder SentMail = this.Application.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
Outlook.Items SentMailItems = SentMail.Items;
Outlook.MailItem newEmail = null;
//SentMailItems = SentMailItems.Restrict("[Unread] = true");
try
{
foreach (object collectionItem in SentMailItems)
{
newEmail = collectionItem as Outlook.MailItem;
if (newEmail != null)
{
if (newEmail.Attachments.Count > 0)
{
for (int i = 1; i <= newEmail.Attachments.Count; i++)
{
newEmail.Attachments[i].SaveAsFile(#"C:\TestFileSave\" + newEmail.Attachments[i].FileName);
}
}
}
}
}
catch (Exception ex)
{
string errorInfo = (string)ex.Message
.Substring(0, 11);
if (errorInfo == "Cannot save")
{
MessageBox.Show(#"Create Folder C:\TestFileSave");
}
}
}
Thanks in Advance

I've tested your code, the SaveAsFile() method of Attachment class instance throws:
{System.IO.DirectoryNotFoundException: Cannot save the attachment.
Path does not exist. Verify the path is correct. at
Microsoft.Office.Interop.Outlook.Attachment.SaveAsFile(String Path)
... }
So, it is necessary to make sure that the directory for saving attachments exists.
private void ThisApplication_NewMail()
{
const string destinationDirectory = #"C:\TestFileSave";
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
MAPIFolder sentMail = Application.ActiveExplorer().Session.GetDefaultFolder(OlDefaultFolders.olFolderSentMail);
Items sentMailItems = sentMail.Items;
try
{
foreach (object collectionItem in sentMailItems)
{
MailItem newEmail = collectionItem as MailItem;
if (newEmail == null) continue;
if (newEmail.Attachments.Count > 0)
{
for (int i = 1; i <= newEmail.Attachments.Count; i++)
{
string filePath = Path.Combine(destinationDirectory, newEmail.Attachments[i].FileName);
newEmail.Attachments[i].SaveAsFile(filePath);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Hope this helps.

Maintain a list which will store the sender's name then check a condition that the recent mail's sender contains in the list if yes than Download.
private void ThisApplication_NewMail()
{
const string destinationDirectory = #"C:\TestFileSave";
List<string> senderList = new List<string>();
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
MAPIFolder sentMail = Application.ActiveExplorer().Session.GetDefaultFolder(OlDefaultFolders.olFolderSentMail);
Items sentMailItems = sentMail.Items;
try
{
foreach (object collectionItem in sentMailItems)
{
MailItem newEmail = collectionItem as MailItem;
senderEmailAdd = newEmail.SenderEmailAddress;
if (newEmail == null) continue;
if (newEmail.Attachments.Count > 0 && senderList.Contains(senderEmailAdd))
{
for (int i = 1; i <= newEmail.Attachments.Count; i++)
{
string filePath = Path.Combine(destinationDirectory, newEmail.Attachments[i].FileName);
newEmail.Attachments[i].SaveAsFile(filePath);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

Related

(solved)IO exception The process cannot access the file because it is being used by another process

I'm trying to delete a line from a txt file with my program, but the program has the text file open so I can't. What do I do?
private void btnDeleteTransaction_Click(object sender, EventArgs e)
{
string line = null;
string delete = txtDeleteTransId.Text;
using (reader = new StreamReader(txtFilePath.Text))
{
try {
using (writer = new StreamWriter(txtFilePath.Text, true))
{
while ((line = reader.ReadLine()) != null)
{
if (String.Compare(line + "|", delete) == 0)
continue;
writer.WriteLine(line);
}
}
}
catch (Exception ex)
{
txtMessages.Text = "exception deleting transaction: " + ex.Message;
}
}
}
Figured it out below.
I'm dumb and was trying to write from the reader which you can't do. Here is my working code if anyone is as new as me.
private void btnDeleteTransaction_Click(object sender, EventArgs e)
{
List<string> records = new List<string>();
found = false;
using (reader = new StreamReader(txtFilePath.Text))
{
while (!reader.EndOfStream)
{
record = reader.ReadLine();
if (record.StartsWith(txtDeleteTransId.Text + "|"))
{
found = true;
}
else
{
records.Add(record);
}
}
if (!found)
{
txtMessages.Text = "Record ID was not found";
return;
}
}
try
{
using (writer = new StreamWriter(txtFilePath.Text, false))
{
foreach (var item in records)
{
writer.WriteLine(item);
}
}
txtMessages.Text = "Record deleted";
}
catch (Exception ex)
{
txtMessages.Text = "exception deleting record: " + ex.Message;
}
}
```

How can I pause thread?

Relevant code:
private static Thread m_thread = null;
private static Boolean m_stop = false;
public static Boolean Start(SearcherParams pars)
{
Boolean success = false;
if (m_thread == null)
{
// Perform a reset of all variables,
// to ensure that the state of the searcher is the same on every new start:
ResetVariables();
// Remember the parameters:
m_pars = pars;
// Start searching for FileSystemInfos that match the parameters:
m_thread = new Thread(new ThreadStart(SearchThread));
m_thread.Start();
success = true;
}
return success;
}
private static void SearchThread()
{
Boolean success = true;
String errorMsg = "";
// Search for FileSystemInfos that match the parameters:
if ((m_pars.SearchDir.Length >= 3) && (Directory.Exists(m_pars.SearchDir)))
{
if (m_pars.FileNames.Count > 0)
{
// Convert the string to search for into bytes if necessary:
if (m_pars.ContainingChecked)
{
if (m_pars.ContainingText != "")
{
try
{
m_containingBytes =
m_pars.Encoding.GetBytes(m_pars.ContainingText);
}
catch (Exception)
{
success = false;
errorMsg = "The string\r\n" + m_pars.ContainingText +
"\r\ncannot be converted into bytes.";
}
}
else
{
success = false;
errorMsg = "The string to search for must not be empty.";
}
}
if (success)
{
// Get the directory info for the search directory:
DirectoryInfo dirInfo = null;
try
{
dirInfo = new DirectoryInfo(m_pars.SearchDir);
}
catch (Exception ex)
{
success = false;
errorMsg = ex.Message;
}
if (success)
{
// Search the directory (maybe recursively),
// and raise events if something was found:
SearchDirectory(dirInfo);
}
}
}
else
{
success = false;
errorMsg = "Please enter one or more filenames to search for.";
}
}
else
{
success = false;
errorMsg = "The directory\r\n" + m_pars.SearchDir + "\r\ndoes not exist.";
}
// Remember the thread has ended:
m_thread = null;
// Raise an event:
if (ThreadEnded != null)
{
ThreadEnded(new ThreadEndedEventArgs(success, errorMsg));
}
}
private static void SearchDirectory(DirectoryInfo dirInfo)
{
if (!m_stop)
{
try
{
foreach (String fileName in m_pars.FileNames)
{
FileSystemInfo[] infos = dirInfo.GetFileSystemInfos(fileName);
foreach (FileSystemInfo info in infos)
{
if (m_stop)
{
break;
}
if (MatchesRestrictions(info))
{
// We have found a matching FileSystemInfo,
// so let's raise an event:
if (FoundInfo != null)
{
FoundInfo(new FoundInfoEventArgs(info));
}
}
}
}
if (m_pars.IncludeSubDirsChecked)
{
DirectoryInfo[] subDirInfos = dirInfo.GetDirectories();
foreach (DirectoryInfo subDirInfo in subDirInfos)
{
if (m_stop)
{
break;
}
// Recursion:
SearchDirectory(subDirInfo);
}
}
}
catch (Exception)
{
}
}
}
The stop is working fine I wanted to add also a pause button to pause and resume the thread.
I added a button click event but how do I make the pause/resume actions and where?
This is a link for the complete code : https://pastebin.com/fYYnHBB6
You can use the ManualResetEvent object.
Here is the simplified version.
In your class, create the object:
public static ManualResetEvent _mrsevent = new ManualResetEvent(false);
In your thread function, as part of the loops that search for files/directories:
private static void SearchThread()
{
foreach (String fileName in m_pars.FileNames)
{
_mrsevent.WaitOne();
}
}
From outside the thread you can call:
a) _mrsevent.Set(); // resume the thread.
b) _mrsevent.Reset(); // pause

Field is never assigned to, and will always have its default value null (CS0649)

I've been searching for answers everywhere and I can't seem to solve mine. Anyone know a solution for this? I'm getting the following errors:
Line 25: Field 'Champion_Item_List_Downloader.MainForm.championsList' is never assigned to, and will always have its default value null (CS0649)
Line 26: Field 'Champion_Item_List_Downloader.MainForm.rolesList' is never assigned to, and will always have its default value null (CS0649)
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.ComponentModel;
namespace Champion_Item_List_Downloader
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
const string listFile = "Lists.txt";
private System.Collections.Generic.List<string> championsList;
private System.Collections.Generic.List<string> rolesList;
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
loadList(listFile);
}
public void loadList(string file){
try {
using (StreamReader r = new StreamReader(file))
{
string line;
bool isChamp = false;
while ((line = r.ReadLine()) != null)
{
if (line == #"[Champions]") {
isChamp = true;
}
if(line != #"[Champions]" && line != #"[Types]" && line != "")
{
if(isChamp == true){
championsList.Add(line);
} else {
rolesList.Add(line);
}
}
}
}
} catch (Exception) {
}
}
public void loadStringList(string file, List<string> list){
try {
using (StreamReader r = new StreamReader(file))
{
string line;
while ((line = r.ReadLine()) != null)
{
list.Add(line);
}
}
} catch (Exception) {
}
}
void Btn_DownloadClick(object sender, EventArgs e)
{
WebClient webClient = new WebClient();
progressBar.Maximum = championsList.Count * rolesList.Count;
int count = 0;
progressBar.Value = 0;
string fileName = "";
string url = "";
string path = "";
foreach (string c in championsList)
{
foreach (string r in rolesList)
{
try {
fileName = c + #"_" + r + #"_scrape.json";
url = #"http://www.lolflavor.com/champions/" + c + #"/Recommended/" + fileName;
path = #"Champions\" + c + #"\Recommended\";
Directory.CreateDirectory(path);
webClient.DownloadFile(new Uri(url), path + fileName);
count++;
progressBar.Value = count;
} catch (Exception) {
}
}
}
progressBar.Value = progressBar.Maximum;
MessageBox.Show("Download completed!\n" + count.ToString() + " item lists successfully downloaded.");
}
void MainFormLoad(object sender, System.EventArgs e)
{
throw new NotImplementedException();
}
}
}
You have not initialized your lists anywhere.
Try initializing them where they are declared:
private System.Collections.Generic.List<string> championsList = new System.Collections.Generic.List<string>();
private System.Collections.Generic.List<string> rolesList = new System.Collections.Generic.List<string>();
Or within the constructor:
private System.Collections.Generic.List<string> championsList;
private System.Collections.Generic.List<string> rolesList;
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
// loadList uses these lists so they should be initialized before the call to loadList to avoid a NullReferenceException.
championsList = new System.Collections.Generic.List<string>();
rolesList = new System.Collections.Generic.List<string>();
loadList(listFile);
}
Or lazily at the time they are needed:
private System.Collections.Generic.List<string> championsList;
private System.Collections.Generic.List<string> rolesList;
public void loadList(string file){
if (championsList == null) championsList = new System.Collections.Generic.List<string>();
if (rolesList == null) rolesList = new System.Collections.Generic.List<string>();
try {
using (StreamReader r = new StreamReader(file))
{
...
}
} catch (Exception) {
}
}
void Btn_DownloadClick(object sender, EventArgs e)
{
WebClient webClient = new WebClient();
if (championsList == null) championsList = new System.Collections.Generic.List<string>();
if (rolesList == null) rolesList = new System.Collections.Generic.List<string>();
progressBar.Maximum = championsList.Count * rolesList.Count;
int count = 0;
progressBar.Value = 0;
string fileName = "";
string url = "";
string path = "";
foreach (string c in championsList)
{
foreach (string r in rolesList)
{
...
}
}
progressBar.Value = progressBar.Maximum;
MessageBox.Show("Download completed!\n" + count.ToString() + " item lists successfully downloaded.");
}
P.S. because you already have using System.Collections.Generic declared, you could write it more simply as:
private List<string> championsList = new List<string>();
private List<string> rolesList = new List<string>();
There is never a call to championsList = new System.Collections.Generic.List<string>().
Thus it's never initialized

Skydrive wp7 App: GetAsync method stopped working

On my windows phone 7 Mango app I use the Microsoft.Live and Microsoft.Live.Controls references to download and upload on Skydrive. Logging in and uploading files works fine but whenever I call the "client.GetAsync("/me/skydrive/files");" on the LiveConnectClient the Callback result is empty just containing the error: "An error occurred while retrieving the resource. Try again later."
I try to retrieve the list of files with this method.
This error suddenly occured without any change on the source code of the app (I think..) which worked perfectly fine for quite a while until recently. At least I didn't change the code section for up- or downloading.
I tried out the "two-step verification" of Skydrive, still the same: can log in but not download.
Also updating the Live refercences to 5.5 didn't change anything.
Here is the code I use. The error occurs in the "getDir_Callback" in the "e" variable.
Scopes (wl.skydrive wl.skydrive_update) and clientId are specified in the SignInButton which triggers "btnSignin_SessionChanged".
private void btnSignin_SessionChanged(object sender, LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
processing = true;
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(UploadCompleted);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompleted);
client.DownloadProgressChanged += new EventHandler<LiveDownloadProgressChangedEventArgs>(client_DownloadProgressChanged);
infoTextBlock.Text = "Signed in. Retrieving file IDs...";
client.GetAsync("me");
}
else
{
connected = false;
infoTextBlock.Text = "Not signed into Skydrive.";
client = null;
}
}
private void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
if (e.Error == null)
{
client.GetCompleted -= new EventHandler<LiveOperationCompletedEventArgs>(OnGetCompleted);
client.GetCompleted += getDir_Callback;
client.GetAsync("/me/skydrive/files");
client_id = (string)e.Result["id"];
if (e.Result.ContainsKey("first_name") && e.Result.ContainsKey("last_name"))
{
if (e.Result["first_name"] != null && e.Result["last_name"] != null)
{
infoTextBlock.Text = "Hello, " +
e.Result["first_name"].ToString() + " " +
e.Result["last_name"].ToString() + "!";
}
}
else
{
infoTextBlock.Text = "Hello, signed-in user!";
}
}
else
{
infoTextBlock.Text = "Error calling API: " +
e.Error.ToString();
}
processing = false;
}
public void getDir_Callback(object s, LiveOperationCompletedEventArgs e)
{
client.GetCompleted -= getDir_Callback;
//filling Dictionary with IDs of every file on SkyDrive
if (e.Result != null)
ParseDir(e);
if (!String.IsNullOrEmpty(e.Error.Message))
infoTextBlock.Text = e.Error.Message;
else
infoTextBlock.Text = "File-IDs loaded";
}
Yo shall use client.GetAsync("me/SkyDrive/files", null) in place of client.GetAsync("me");
This is my code and it works fine.
private void btnSignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
try
{
if (e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(e.Session);
client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_GetCompleted);
client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(client_UploadCompleted);
client.GetAsync("me/SkyDrive/files", null);
client.DownloadAsync("me/SkyDrive", null);
canUpload = true;
ls = e.Session;
}
else
{
if (client != null)
{
MessageBox.Show("Signed out successfully!");
client.GetCompleted -= client_GetCompleted;
client.UploadCompleted -= client_UploadCompleted;
canUpload = false;
}
else
{
canUpload = false;
}
client = null;
canUpload = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void client_GetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
try
{
List<System.Object> listItems = new List<object>();
if (e.Result != null)
{
listItems = e.Result["data"] as List<object>;
for (int x = 0; x < listItems.Count(); x++)
{
Dictionary<string, object> file1 = listItems[x] as Dictionary<string, object>;
string fileName = file1["name"].ToString();
if (fileName == lstmeasu[0].Title)
{
folderID = file1["id"].ToString();
folderAlredyPresent = true;
}
}
}
}
catch (Exception)
{
MessageBox.Show("An error occured in getting skydrive folders, please try again later.");
}
}
It miraculously works again without me changing any code. It seems to be resolved on the other end. I have no clue what was wrong.

Need to send email using background worker process

I have code written for sending email in C#, but the application hangs up when the application is sending mails size of attachments more than 2 MB. I was advised to use background worker process for the same by SO users.
I have gone thru the background worker process example from MSDN and google it also, but i don't know how to integrate in my code.
Please guide me in that...
thanks
UPDATED: Email-Code Added
public static void SendMail(string fromAddress, string[] toAddress, string[] ccAddress, string[] bccAddress, string subject, string messageBody, bool isBodyHtml, ArrayList attachments, string host, string username, string pwd, string port)
{
Int32 TimeoutValue = 0;
Int32 FileAttachmentLength = 0;
{
try
{
if (isBodyHtml && !htmlTaxExpression.IsMatch(messageBody))
isBodyHtml = false;
// Create the mail message
MailMessage objMailMsg;
objMailMsg = new MailMessage();
if (toAddress != null) {
foreach (string toAddr in toAddress)
objMailMsg.To.Add(new MailAddress(toAddr));
}
if (ccAddress != null) {
foreach (string ccAddr in ccAddress)
objMailMsg.CC.Add(new MailAddress(ccAddr));
}
if (bccAddress != null) {
foreach (string bccAddr in bccAddress)
objMailMsg.Bcc.Add(new MailAddress(bccAddr));
}
if (fromAddress != null && fromAddress.Trim().Length > 0) {
//if (fromAddress != null && fromName.trim().length > 0)
// objMailMsg.From = new MailAddress(fromAddress, fromName);
//else
objMailMsg.From = new MailAddress(fromAddress);
}
objMailMsg.BodyEncoding = Encoding.UTF8;
objMailMsg.Subject = subject;
objMailMsg.Body = messageBody;
objMailMsg.IsBodyHtml = isBodyHtml;
if (attachments != null) {
foreach (string fileName in attachments) {
if (fileName.Trim().Length > 0 && File.Exists(fileName)) {
Attachment objAttachment = new Attachment(fileName);
FileAttachmentLength=Convert.ToInt32(objAttachment.ContentStream.Length);
if (FileAttachmentLength >= 2097152) {
TimeoutValue = 900000;
} else {
TimeoutValue = 300000;
}
objMailMsg.Attachments.Add(objAttachment);
//objMailMsg.Attachments.Add(new Attachment(fileName));
}
}
}
//prepare to send mail via SMTP transport
SmtpClient objSMTPClient = new SmtpClient();
if (objSMTPClient.Credentials != null) { } else {
objSMTPClient.UseDefaultCredentials = false;
NetworkCredential SMTPUserInfo = new NetworkCredential(username, pwd);
objSMTPClient.Host = host;
objSMTPClient.Port = Int16.Parse(port);
//objSMTPClient.UseDefaultCredentials = false;
objSMTPClient.Credentials = SMTPUserInfo;
//objSMTPClient.EnableSsl = true;
//objSMTPClient.DeliveryMethod = SmtpDeliveryMethod.Network;
}
//objSMTPClient.Host = stmpservername;
//objSMTPClient.Credentials
//System.Net.Configuration.MailSettingsSectionGroup mMailsettings = null;
//string mailHost = mMailsettings.Smtp.Network.Host;
try {
objSMTPClient.Timeout = TimeoutValue;
objSMTPClient.Send(objMailMsg);
//objSMTPClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
objMailMsg.Dispose();
}
catch (SmtpException smtpEx) {
if (smtpEx.Message.Contains("secure connection")) {
objSMTPClient.EnableSsl = true;
objSMTPClient.Send(objMailMsg);
}
}
}
catch (Exception ex)
{
AppError objError = new AppError(AppErrorType.ERR_SENDING_MAIL, null, null, new AppSession(), ex);
objError.PostError();
throw ex;
}
}
}
I can't modify the code here as it is common method that is called whenever mail is to be sent from my application.
You could start a background thread to continuously loop and send email:
private void buttonStart_Click(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
this.Controls.Add(bw);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
private bool quit = false;
void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (!quit)
{
// Code to send email here
}
}
Alternative way to do it:
private void buttonStart_Click(object sender, EventArgs e)
{
System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient();
client.SendCompleted += new System.Net.Mail.SendCompletedEventHandler(client_SendCompleted);
client.SendAsync("from#here.com", "to#there.com", "subject", "body", null);
}
void client_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error == null)
MessageBox.Show("Successful");
else
MessageBox.Show("Error: " + e.Error.ToString());
}
Specific to your example, you should replace the following:
try
{
objSMTPClient.Timeout = TimeoutValue;
objSMTPClient.Send(objMailMsg);
//objSMTPClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
objMailMsg.Dispose();
}
catch (SmtpException smtpEx)
{
if (smtpEx.Message.Contains("secure connection"))
{
objSMTPClient.EnableSsl = true;
objSMTPClient.Send(objMailMsg);
}
}
with the following:
objSMTPClient.Timeout = TimeoutValue;
objSMTPClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
objSMTPClient.SendAsync(objMailMsg, objSMTPClient);
and further down, include:
void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
if (e.Error == null)
MessageBox.Show("Successful");
else if (e.Error is SmtpException)
{
if ((e.Error as SmtpException).Message.Contains("secure connection"))
{
(e.UserState as SmtpClient).EnableSsl = true;
(e.UserState as SmtpClient).SendAsync(objMailMsg, e.UserState);
}
else
MessageBox.Show("Error: " + e.Error.ToString());
}
else
MessageBox.Show("Error: " + e.Error.ToString());
}
There's a great example of how to do this with a "Service Broker" in Chapter 8 of Richard Kiessig's book "Ultra-Fast ASP.NET."
Here's the link to the book on the publisher's website where you can download the sample code from the book. Again, chapter 8...
http://apress.com/book/view/9781430223832

Categories

Resources