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);
}
}
Related
we send emails with attachments by the ews-service with the follwing code...
EmailMessage message = new EmailMessage(ExchangeServiceController);
EmailAddress sender = new EmailAddress
{
MailboxType = MailboxType.Mailbox,
Address = senderAdress
};
message.From = sender;
message.ToRecipients.AddRange(email.SendTo.GetEmailAdressRange());
message.Subject = email.Subject;
message.Body = new MessageBody(BodyType.HTML, emailText);
string[] fileEntries = SignatureController.GetSignatureImagesFiles();
int i = 0;
foreach (string fileName in fileEntries.Where(f => f.Contains("image")))
{
FileInfo info = new FileInfo(fileName);
message.Attachments.AddFileAttachment(info.Name, fileName);
message.Attachments[i].IsInline = true;
message.Attachments[i].ContentId = info.Name;
i++;
}
foreach (string filename in GetAttachmentsAsFilePathList())
{
message.Attachments.AddFileAttachment(filename);
}
var mailbox = new Mailbox(sender.Address);
var folderIdDrafts = new FolderId(WellKnownFolderName.Drafts, mailbox);
var folderIdSent = new FolderId(WellKnownFolderName.SentItems, mailbox);
// save email at draft to get the mime-content
message.Save(folderIdDrafts);
message.Load(new PropertySet(ItemSchema.MimeContent));
email.EmlContent = message.MimeContent;
if (template.SaveMessageOnSend)
{
message.SendAndSaveCopy(folderIdSent);
}
else
{
message.Send();
}
The email saved in draft. After this we get sometimes an follwoing error...
Type: Microsoft.Exchange.WebServices.Data.ServiceResponseException
Source: Microsoft.Exchange.WebServices
Message: The operation can't be performed because the item is out of date. Reload the item and try again.
HResult: -2146233088 (0x80131500)
InnerException: null
HashedSignature: 49E2BF981546C920B6A4BC0B27476A40
StackTrace:
bei Microsoft.Exchange.WebServices.Data.ServiceResponse.InternalThrowIfNecessary()
bei Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.Execute()
bei Microsoft.Exchange.WebServices.Data.ExchangeService.SendItem(Item item, FolderId savedCopyDestinationFolderId)
bei Microsoft.Exchange.WebServices.Data.EmailMessage.InternalSend(FolderId parentFolderId, MessageDisposition messageDisposition)
It happens often with attachment over 6 MB.
The timeout is over 1 minute and the error becomes before this time.
The maxsize is over 10 MB
When i take this email at outlook and send it from the draft it works fine.
Have anybody an idea to solve this problem?
best regards steve
The reason you get the error is because the changekey is stale at the time you submit the message. This can happen if another client makes a modification on the message or Store level AV can do it see changekey in https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid?redirectedfrom=MSDN. With the line
message.Load(new PropertySet(ItemSchema.MimeContent));
This will be forcing the Exchange Store to do an one the fly conversion of the Message to Mime. I'd would try doing another load before sending the message and just ask for the IdOnly
message.Load(new PropertySet(ItemSchema.MimeContent));
email.EmlContent = message.MimeContent;
message.Load(new PropertySet(BasePropertySet.IdOnly));
That should ensure you have the latest changekey before send, otherwise you could probably just build the MIME message with something like MailKit and send it in one operation rather then saving the draft.
I am getting emails from a mailbox using exchange webservices using a custom code block in C# (I'm not versed in C# at all so forgive code quality!) in UiPath. I am passing the exchange service and folder ID as arguments to the code block. I noticed that when there was a large attachment on the email it took significantly longer. I am not interested in the attachment I just want to be able to access some information about the email. This was my initial code:
//Search for oldest email
ItemView objView = new ItemView(1);
objView.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
FindItemsResults<Item> lstItems = objServer.FindItems(in_FolderID, objView);
//Bind to email from result
if(lstItems.Count() == 0)
{
Console.WriteLine("Inbox appears to be empty");
out_ExchangeMessage = null;
out_InternetMessageID = null;
}
else
{
Item objItem = lstItems.ElementAt(0);
Console.WriteLine("Retrieving email: " + objItem.Subject);
PropertySet objPropertySet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.MimeContent, EmailMessageSchema.IsRead,ItemSchema.Attachments,ItemSchema.TextBody);
out_ExchangeMessage = EmailMessage.Bind(objServer,objItem.Id, objPropertySet);
out_InternetMessageID = out_ExchangeMessage.InternetMessageId;
Console.WriteLine("Message Retrieved: " + out_ExchangeMessage.InternetMessageId);
}
I tried removing ItemSchema.Attachments so this line reads as follows. But the email still takes significantly longer to download
PropertySet objPropertySet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.MimeContent, EmailMessageSchema.IsRead,ItemSchema.TextBody);
Is there a way to speed up the retrieving of emails with large attachments?
Because your including ItemSchema.MimeContent in your propertyset that is going to give you the whole MimeStream of the Message including attachments. If you don't require the MimeStream don't request it. Eg you should be able to get all the other properties of the Message eg body,subject,headers etc from other properties so it would only be required if you wanted to save the message.
What I have done so far is to connect to EWS, access my inbox, create an item (email) with some info in Body, Subject, From, and To, save it to the Draft folder, and finally move it to my inbox. It works, however, I get a draft in the inbox instead of an email.
Is it possible to get the message as an email with the above scenario and how can I achieve that?
Below is my code. Any input would be very appreciated.
try {
message.Save();
}
catch(Exception e21) {;
}
message.Load(PS);
message.From = new EmailAddress("someone#abc.com");
message.ToRecipients.Add("me#abc.com");
message.Body = "This is A test take 1";
message.Subject = "Testing to send as someone else...";
// add in the attachments......
message.Update(ConflictResolutionMode.AlwaysOverwrite); // require this here why????
message.Copy(theTempFolder.Id); // get the item as a draft in my mailbox instead of an email
}
catch(Exception e99) {
Console.WriteLine("Exception fail to connect to office 365 on the cloud: " + e99.Message);
}
You need to set the MessageFlags property https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/pidtagmessageflags-canonical-property to 1 (before you call update) eg
ExtendedPropertyDefinition PR_MESSAGE_FLAGS_msgflag_read = new ExtendedPropertyDefinition(3591, MapiPropertyType.Integer);
message.SetExtendedProperty(PR_MESSAGE_FLAGS_msgflag_read, 1);
Which will then make the message look like it was received. The other way is just import an EML like https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-import-items-by-using-ews-in-exchange
Have you tried to send it to yourself with either message.Send() or message.SendAndSaveCopy() ? see more here
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 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