I want to move/copy my Email Attachments to new folder in outlook ;/ and my code doesn't work properly.
foreach(Item item in findResults.Items)
{
EmailMessage email = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Attachments));
if(false)
{
// OTC Marker HTML Body
}
else
{
if (email.HasAttachments)
{
foreach (Attachment attachment in email.Attachments)
{
EmailMessage emailAttachment = EmailMessage.Bind(service, attachment.Id, new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Attachments));
ItemAttachment itemAttachment = attachment as ItemAttachment;
itemAttachment.Load();
EmailMessage mess = itemAttachment.Item as EmailMessage;
moveToTestFolder (mess, #"TestFolder");
}
}
else
{
//to do
}
}
}
And my moveToTestFolder method:
private void moveToTestFolder (EmailMessage item, string folderName)
{
Folder rootfolder = Folder.Bind(service, WellKnownFolderName.MsgFolderRoot);
rootfolder.Load();
var folders = rootfolder.FindFolders(new FolderView(20));
var folderItemToMove = folders.FirstOrDefault(f => f.DisplayName.Equals(folderName, StringComparison.OrdinalIgnoreCase));
item.Move(folderItemToMove.Id);
}
I'am trying to move attachment (if it is an email) to special folder in outlook. Moving the normal message is working now.
That won't work because you can only use the Move and Copy operations to copy an actual Mailbox Item not Attachments (you should be getting an error about and Invalid Id). One workaround for this is to get the MimeContent for the Email Attachment you want to move and then create a New object from that MimeCotent and save it to the folder you want to move the Item to eg
foreach (Attachment Attach in EWSItem.Attachments)
{
if (Attach is ItemAttachment)
{
PropertySet psProp = new PropertySet(BasePropertySet.FirstClassProperties);
psProp.Add(ItemSchema.MimeContent);
((ItemAttachment)Attach).Load(psProp);
if (((ItemAttachment)Attach).Item.MimeContent != null)
{
EmailMessage NewMessage = new EmailMessage(service);
NewMessage.MimeContent = ((ItemAttachment)Attach).Item.MimeContent;
NewMessage.SetExtendedProperty(new ExtendedPropertyDefinition(3591, MapiPropertyType.Integer), "1");
NewMessage.Save(folderItemToMove.Id);
}
}
}
You don't get full Fidelity of all the Exchange properties on the Message with this method as only the MimeContent is copied which is generally not a problem with Email but will be an issue for other objects types like Contacts, Tasks etc.
Cheers
Glen
Related
I have a (Microsoft.Office.Interop.Outlook.MailItem mail) mail object.
I want this mail to attach as an attachment to another mail.
but not able to find any solution.So can anyone please help.
I have created another mail object as shown below :
Microsoft.Office.Interop.Outlook.MailItem toSendMail = this.Application.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
As per your requirement you want to send an existing mail object as an attachment to another mail in outlook.
One way of doing this is by saving the existing mailItem as an attachment to other. Try this:
private void AddMessageAsAttachment(Microsoft.Office.Interop.Outlook.MailItem
mailContainer,Microsoft.Office.Interop.Outlook.MailItem mailToAttach)
{
Microsoft.Office.Interop.Outlook.Attachments attachments = null;
Microsoft.Office.Interop.Outlook.Attachment attachment = null;
try
{
attachments = mailContainer.Attachments;
attachment = attachments.Add(mailToAttach,
Microsoft.Office.Interop.Outlook.OlAttachmentType.olEmbeddeditem, 1, "The attached e-mail");
mailContainer.Save();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (attachment != null) Marshal.ReleaseComObject(attachment);
if (attachments != null) Marshal.ReleaseComObject(attachments);
}
}
reference : https://www.add-in-express.com/creating-addins-blog/2011/08/12/how-to-add-existing-e-mail-message-as-attachment/
Get the mail, which should be added as attachment.
then call «SaveAs({filename}, Microsoft.Office.Interop.Outlook.OlSaveAsType.olMSG)» and add this file to your new mail
I'm currently writing a Windows Service to log in to a specific Exchange account, get any new emails, parse them, and save the email in the appropriate folder.
Everything is working perfectly, except saving the email.
Relevant code blocks (try / catch blocks and irrelevant stuff removed to keep it short):-
Set up the Service
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(emailAddress, password);
service.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback);
CheckForNewEmails(service);
Getting and checking new emails
private static void CheckForNewEmails(ExchangeService service)
{
int offset = 0;
int pageSize = 50;
bool more = true;
ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
view.PropertySet = PropertySet.IdOnly;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
while (more)
{
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items)
{
emails.Add((EmailMessage)item);
}
more = findResults.MoreAvailable;
if (more)
{
view.Offset += pageSize;
}
}
if (emails.Count > 0)
{
PropertySet properties = (BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(emails, properties);
var mailItems = new List<DatabaseService.MailItem>();
var dbService = new DatabaseService();
var defaultUser = dbService.GetDefaultUser(defaultUserID);
foreach (var email in emails)
{
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
}
}
}
Finding who it should be linked to
public void FindLinks(ExchangeService service, ref MailItem mailItem, User defaultUser)
{
string address = mailItem.Sender;
// get file hash
var tempPath = Path.GetTempPath();
var fileName = GetFilenameFromSubject(mailItem);
var fullName = Path.Combine(tempPath, fileName);
SaveAsEML(service, mailItem, fullName);
var sha = new SHA256Managed();
mailItem.Hash = Convert.ToBase64String(sha.ComputeHash(File.OpenRead(fullName)));
File.Delete(fullName);
using (var db = DatabaseHelpers.GetEntityModel())
{
// Do all the linking stuff
}
}
And finally, the problem area: Saving the file to disk (in this case to a temporary folder so I can get the file hash, to check it's not a duplicate (e.g. CC'd etc))
Looking through StackOverflow and various other sources, there seem to be two ways to do this:-
1) Using the MailItem.Load method
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
mailItem.mail.Load(new PropertySet(ItemSchema.MimeContent));
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Using the above code, the file is created and has the correct content.
However, attempting to access any of the email properties AFTER this point results in a Null Exception crash (big crash too, no Try/Catch or UnhandledException trap will pick it up, just kills the Service)
In the above code, this line crashes the entire service:-
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
Specifically, referencing email.DateTimeSent
I added in some diagnostic code directly before this line:-
if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }
The first line logs NOT null, so email still exists.
However, the second line instantly crashes with a null exception error, without logging anything.
If I comment out the line SaveAsEML(service, mailItem, fullName); from FindLinks then everything works perfectly (except of course the file isn't saved).
2) Binding a Property Set
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Doing it this way, nothing crashes, it goes through every email just fine, and can reference email.DateTimeSent and all the other properties.
Unfortunately, it creates zero-length files, no content.
Been banging my head against the wall for hours now over this (took me an hour of adding diagnostics everywhere just to track down the crash happening when referencing properties), and it's undoubtedly something trivial I've overlooked, so if some kind soul could point out my stupidity I'd be most grateful!
Edit:
I was able to work around the above problem easily enough by simply saving the value of the properties before using them:-
foreach (var email in emails)
{
var dateTimeSent = email.DateTimeSent;
var sender = email.Sender;
var subject = email.Subject;
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID,
dateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
sender,
subject,
mailItem.Hash ?? "No Hash",
mailItem.LinkString ?? "No Linkstring"
));
}
This allowed me to use the MailItem.Load method which saved the file, and thus do what I was after in this specific case.
However there are other things this Service will need to do, and I really don't want to have to save a copy of every single property I'll need to access.
The reason this would fail
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Is that you have used Bind to Load the message with the MimeContent in the email variable and then you haven't used that. mailItem.mail won't be linked at all to email variable created as part of this operation (even though they are the same object on the server). Locally these are just two separate variables. EWS is client/server so you make a request and the Managed API will return a local object that represents the result of the operation. But that object is disconnected hence when you do the bind above it just generates another client object to represent the result of that operation. eg so the above should have been
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(email.MimeContent.Content, 0, email .MimeContent.Content.Length);
}
}
I'm using EWS in order to retrieve emails, but when I want to retrieve the attachments I have to call the following function for each:
fileAttachment.Load();
Everytime I do that, it goes to the server. Is it possible to retrieve all the attachments at once? Also, is it possible to retrieve all the attachments for several mail items?
The ExchangeService object has a GetAttachments method which basically allows you to do a batch GetAttachment request. So if you want to load the attachments on several messages at once you need to do something like (first call loadpropertiesforitems which does a batch GetItem to get the AttachmentIds)
FindItemsResults<Item> fItems = service.FindItems(WellKnownFolderName.Inbox,new ItemView(10));
PropertySet psSet = new PropertySet(BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(fItems.Items, psSet);
List<Attachment> atAttachmentsList = new List<Attachment>();
foreach(Item ibItem in fItems.Items){
foreach(Attachment at in ibItem.Attachments){
atAttachmentsList.Add(at);
}
}
ServiceResponseCollection<GetAttachmentResponse> gaResponses = service.GetAttachments(atAttachmentsList.ToArray(), BodyType.HTML, null);
foreach (GetAttachmentResponse gaResp in gaResponses)
{
if (gaResp.Result == ServiceResult.Success)
{
if (gaResp.Attachment is FileAttachment)
{
Console.WriteLine("File Attachment");
}
if (gaResp.Attachment is ItemAttachment)
{
Console.WriteLine("Item Attachment");
}
}
}
Cheers
Glen
I currently want to download all email messages (regardless in which folder they're in) to my SQL Server database.
Now while I know how to search for email messages or subscribe to streaming notifications, I've yet to learn on how to synchronize all messages from EWS to my database.
var emailMessages = GetItems<MSEmailMessage>(WellKnownFolderName.MsgFolderRoot);
foreach (var emailMessage in emailMessages)
{
Debug.WriteLine(emailMessage.Subject);
}
private IList<T> GetItems<T>(WellKnownFolderName wellKnownFolderName) where T : Item
{
IList<T> result = new List<T>();
Folder folder = Folder.Bind(_exchangeService, wellKnownFolderName);
if (folder.TotalCount > 0)
{
ItemView view = new ItemView(folder.TotalCount);
FindItemsResults<Item> items = _exchangeService.FindItems(wellKnownFolderName, view);
foreach (var resultItem in items.OfType<T>())
{
result.Add(resultItem);
}
}
return result;
}
This returns 0 email messages (it even threw an exception before checking for the folder.TotalCount before initializing a new ItemView...).
While checking for WellKnownFolderName.Inbox returns the email messages from the inbox, it does not allow me to query for sub folders to synchronize the entirety of the messages.
What am I missing?
You can build up a list of folders to search for mail in. Then iterate through each folder and get all the emails in that folder.
In the code snippet below we can create a folderSearchFilter with FolderTraversal set to Deep which will scan all sub folders of the target folder. We can then apply this filter to the two main well-known folders Inbox and SentItems
Once you have a list of folders to index, then you can use your own code to retrieve all the mails from that folder.
var view = new FolderView(int.MaxValue)
{
PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) { FolderSchema.DisplayName }
};
SearchFilter foldersearchFilter = new SearchFilter.IsGreaterThan(FolderSchema.TotalCount, 0);
view.Traversal = FolderTraversal.Deep;
List<Folder> searchFolders;
try
{
searchFolders = new List<Folder>
{
Folder.Bind(ExchangeService, WellKnownFolderName.Inbox),
Folder.Bind(ExchangeService, WellKnownFolderName.SentItems)
};
}
catch (ServiceResponseException e) {}
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.Inbox, foldersearchFilter, view).Folders);
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.SentItems, foldersearchFilter, view).Folders);
var results = new List<Item>();
foreach (var searchFolder in searchFolders)
{
//Get all emails in this folder
}
Right, the root folder likely has 0 messages in it. When you do FindItems in a folder, the results don't bubble up from subfolders. You need to iterate over every folder if you want to get to their messages.
I'm in the process of writing a simple console app that monitors a particular exchange mailbox, and when emails meeting particular criteria are received, the app will download an XML file attachment, and archive the email.
I've connected to EWS OK, and have been able to loop through any emails, but I'm struggling when it comes to create an EmailMessage object which I can use to access the attachments.
In the example code below, the EmailMessage message = EmailMessage.Bind(...) line executes without error, but doesn't return a valid message so when I access and properties or methods, I get an error: 'Object reference not set to an instance of an object'.
I'm new to C# let alone EWS so I'm struggling to know where to start...
Code Snippet:
public static void FindItems()
{
try
{
ItemView view = new ItemView(10);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
findResults = service.FindItems(
WellKnownFolderName.Inbox,
new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
new SearchFilter.ContainsSubstring(ItemSchema.Subject, "Sales Enquiry")),
view);
log2.LogInfo("Total number of items found: " + findResults.TotalCount.ToString());
foreach (Item item in findResults)
{
log2.LogInfo(item.Id);
EmailMessage message = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments));
Console.WriteLine(message.Subject.ToString());
if (message.HasAttachments && message.Attachments[0] is FileAttachment)
{
FileAttachment fileAttachment = message.Attachments[0] as FileAttachment;
fileAttachment.Load("C:\\temp\\" + fileAttachment.Name);
fileAttachment.Load();
Console.WriteLine("FileName: " + fileAttachment.FileName);
}
}
}
catch (Exception ex)
{
log2.LogError(ex.InnerException);
}
}
My code for accessing the attachments is straight from MSDN so I'm hoping it is there are thereabouts... Any ideas?
I'm afraid I revisited this problem and managed to cure it. Unfortunately I was too pressed at the time to come back here and document the solution. Time having passed, and my memory of what I changed has faded, but as far as I can remember it was a one line change:
EmailMessage message = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.FirstClassProperties, EmailMessageSchema.Attachments));
The key difference here is that we have specified BasePropertySet.FirstClassProperties as the first parameter of PropertySet, rather than the BasePropertySet.IdOnly that we originally had.
My original code was lifted from an example online that did precisely what I was trying to achieve, so either the example wasn't quite right, or I transcribed it incorrectly or misunderstood some facet of the problem.
foreach(EmailMessage message in findResults)
{
message.Load();
Console.WriteLine(message.Subject.ToString());
if (message.HasAttachments && message.Attachments[0] is FileAttachment)
{
FileAttachment fileAttachment = message.Attachments[0] as FileAttachment;
fileAttachment.Load("C:\\temp\\" + fileAttachment.Name);
fileAttachment.Load();
Console.WriteLine("FileName: " + fileAttachment.FileName);
}
}