Get only text of Item from ItemAttachment (EWS Managed API) - c#

Is there a way to remove HTML tags from Item which is from ItemAttachment?
I can get only text from Item. But not from Item which is from ItemAttachment.
Here is my code:
foreach (ItemAttachment itemAttach in item.Attachments.OfType<ItemAttachment>())
{
Console.WriteLine(itemAttach.Name);
itemAttach.Load();
PropertySet propSet = new PropertySet();
propSet.RequestedBodyType = BodyType.Text;
propSet.BasePropertySet = BasePropertySet.FirstClassProperties;
itemAttach.Item.Load(propSet);
Console.WriteLine(itemAttach.Item.Body.Text);
}
It will get this exception
This operation isn't supported on attachments
I tried binding to the exchange service with item ID.
It also gives me some exception!
Please give some advice on how I can do.

Jin,
The exception you are getting has to do with the property set you are creating. I don't see your code for getting the items so I can't determine the exact cause. I was able to get the following code to work on my machine. You should be able to modify it for your needs.
// Return the first ten items.
ItemView view = new ItemView(10);
// Set the query string to only find emails with attachments.
string querystring = "HasAttachments:true Kind:email";
// Find the items in the Inbox.
FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Inbox, querystring, view);
// Loop through the results.
foreach (EmailMessage email in results)
{
// Load the email message with the attachments
email.Load(new PropertySet(EmailMessageSchema.Attachments));
// Loop through the attachments.
foreach (Attachment attachment in email.Attachments)
{
// Only process item attachments.
if (attachment is ItemAttachment)
{
ItemAttachment itemAttachment = attachment as ItemAttachment;
// Load the attachment.
itemAttachment.Load(new PropertySet(EmailMessageSchema.TextBody));
// Output the body.
Console.WriteLine(itemAttachment.Item.TextBody);
}
}
For each email that had an item attachment I was able to see the body of the item with the HTML tags removed.
I hope this helps. If this solves your problem, please mark this post as answered.
Thanks,
--- Bob ---

Related

How to read emails message BodyText through EWS? C#

I would like to get all messages.BodyText in my email. I have already some code, tried many things, but didn't catch what really will work.
My code:
ExchangeService service;
service = new ExchangeService
{
Credentials = new WebCredentials("mail.com", #"password")
};
List<String> items = new List<String>();
// This is the office365 webservice URL
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ItemView view = new ItemView(50);
FindItemsResults<Item> Items = service.FindItems(WellKnownFolderName.Inbox, view); // line 34
// 'Object reference not set to an instance of an object.' / on line 34
foreach (Item item in Items)
{
if (item is EmailMessage)
{
item.Load();
string subject = item.Subject;
string mailMessage = item.Body;
}
items.Add(item.TextBody); // line 44
//You must load or assign this property before you can read its value / on line 44
}
foreach (var item in items)
{
Console.WriteLine(item);
}
When I'm trying to run my code, then I got two errors:
You must load or assign this property before you can read its value / on line 44
'Object reference not set to an instance of an object.' / on line 34
The second error is working from time to time, I'm not sure what is wrong.
Thank you in advance!
If you want the Text body of the message then you need to use a propertyset that will specifically request that. A message may not have a Text body so in that instance the Exchange store will do an on the fly conversion what you code should look like is
PropertySet psPropSet = new PropertySet(BasePropertySet.FirstClassProperties)
{
RequestedBodyType = BodyType.Text
};
foreach (Item item in Items)
{
if (item is EmailMessage)
{
item.Load(psPropSet);
string subject = item.Subject;
string mailMessage = item.Body;
}
items.Add(item.TextBody);
}
Keep in mind this will make a call to the Exchange server every time you call Load which is really inefficient and done on a large scale will mean you code with be throttled. This can be batched using LoadPropertiesForItems in EWS eg https://learn.microsoft.com/en-us/answers/questions/614111/processing-120k-emails-takes-more-than-a-day-using.html
Try calling the load method before attempting to assign a value.
In your code here:
foreach (Item item in Items)
{
if (item is EmailMessage)
{
item.Load();
string subject = item.Subject;
string mailMessage = item.Body;
}
items.Add(item.TextBody); // line 44
//You must load or assign this property before you can read its value / on line 44
}
You are checking if an item is EmailMessage. There may be some in the list that are not an EmailMessage which would cause them to be accessed before using the Load method.
You could try putting the item load outside of the conditional and that would most likely fix the issue.
Please see this question here:
Error when I try to read/update the .Body of a Task via EWS Managed API - "You must load or assign this property before you can read its value."

Get mail from folder within Inbox - EWS

I want to access emails in a folder called "ITServiceDesk" in my exchange inbox.
I can access the folder but i cant figure out how to read the mail inside that folder.
I am accessing the folder here:
var view = new FolderView(100);
view.Traversal = FolderTraversal.Deep;
var fileview = new ItemView(100);
var filter = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "ITServiceDesk");
// Read 100 mails
foreach (var item in _service.FindFolders(WellKnownFolderName.Inbox, filter, view))
{
MessageBox.Show(item.DisplayName);
foreach (EmailMessage email in _service.FindItems(WellKnownFolderName.Inbox, filter, fileview))
{
email.Load(new PropertySet(EmailMessageSchema.ConversationTopic, ItemSchema.Attachments,
ItemSchema.TextBody));
MessageBox.Show(email.ConversationTopic);
MessageBox.Show(email.TextBody);
}
}
Nothing happens when i get inside the second foreach loop. The message box shows that it can find the folder as the item.displayname is correct.
If you are finding the folder with you code then just call the findItem method on the Folder object that is returned eg
foreach (var Folder in _service.FindFolders(WellKnownFolderName.Inbox, filter, view))
{
MessageBox.Show(Folder.DisplayName);
foreach (EmailMessage email in Folder.FindItems(fileview))
{
email.Load(new PropertySet(EmailMessageSchema.ConversationTopic, ItemSchema.Attachments,
ItemSchema.TextBody));
MessageBox.Show(email.ConversationTopic);
MessageBox.Show(email.TextBody);
}
}
Here is an example from my website:
FindItemsResults<Item> findResults
= service.FindItems(WellKnownFolderName.Inbox, new ItemView( 10 ) );
foreach ( Item item in findResults.Items )
Console.WriteLine( item.Subject );
See C#: Getting All Emails From Exchange using Exchange Web Services

How to save attached EmailMessage from ItemAttachment object in Exchange EWS?

I've been fighting this problem for some time now, and have failed to find an answer online that works. I am using the Exchange EWS API to do some email processing. One of the things I need to process is an EmailMessage that has attachments on it. One of those attachments happens to be another EmailMessage. I will refer to this as the attached EmailMessage.
I want to convert this EmailMessage to a byte[], however every time I try, I get an Exception. Below is my code:
if (((ItemAttachment)attachment).Item is EmailMessage)
{
EmailMessage msg = ((ItemAttachment)attachment).Item as EmailMessage;
msg.Load(new PropertySet(ItemSchema.MimeContent));
byte[] content = msg.MimeContent.Content;
}
The problem is no matter what I try to load, I get an exception thrown saying
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Exchange.WebServices.dll but was not handled in user code
Additional information: This operation isn't supported on attachments.
If I don't call msg.Load(), I get a different error saying I need to load the content.
I don't understand this. If I do the same operation on an EmailMessage that was not attached to anything, it works just fine. Why does it matter that the EmailMessage was an attachment at one point in time? How can I get the EWS/.NET/Whatever is throwing the Exception to treat the attached EmailMessage as an EmailMessage and not an ItemAttachment?
You need to use the GetAttachments operations on Each on the Embedded Attachments with a propertyset that includes the MimeContent. eg something like (this can made a lot more effienct by grouping the GetAttachment requests if your processing multiple message etc).
PropertySet psPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
psPropSet.Add(ItemSchema.MimeContent);
foreach (Attachment attachment in CurrentMessage.Attachments)
{
if (attachment is ItemAttachment)
{
attachment.Load();
if (((ItemAttachment)attachment).Item is EmailMessage)
{
EmailMessage ebMessage = ((ItemAttachment)attachment).Item as EmailMessage;
foreach (Attachment ebAttachment in ebMessage.Attachments)
{
if (ebAttachment is ItemAttachment)
{
Attachment[] LoadAttachments = new Attachment[1];
LoadAttachments[0] = ebAttachment;
ServiceResponseCollection<GetAttachmentResponse> getAttachmentresps = service.GetAttachments(LoadAttachments, BodyType.HTML, psPropSet);
foreach (GetAttachmentResponse grResp in getAttachmentresps)
{
EmailMessage msg = ((ItemAttachment)grResp.Attachment).Item as EmailMessage;
msg.Load(new PropertySet(ItemSchema.MimeContent));
byte[] content = msg.MimeContent.Content;
}
}
}
}
}
}
You have to load the attachment with the flag to load MimeContent:
{
if (attachment is ItemAttachment ia)
{
ia.Load(ItemSchema.MimeContent);
}
}

Use attachments from calendar items - Outlook - C#

I'm trying to use the attachments included in calendar items pulled progmatically.
I have a list of chosen calendar subject lines from a previous dialog box, and while the subject is transferring properly, the body isn't working well (another question altogether) but the attachments aren't working whatsoever.
Here's my foreach loop where the attachments are being placed into an Attachments array for use later:
string[] subjects = new string[dialog.chosen.Count];
string[] bodies = new string[dialog.chosen.Count];
Attachments[] attach = new Attachments[dialog.chosen.Count];
foreach (Outlook.AppointmentItem appt in rangeAppts)
{
foreach (string text in dialog.chosen)
{
if (text == appt.Subject)
{
subjects[i] = appt.Subject;
bodies[i] = Convert.ToString(appt.Body);
attach[i] = appt.Attachments;
i = i + 1;
}
}
}
And then here's where I actually call the method:
sendEmailTemplate(bodies[i], subject, to, "", attachment: attach[i]);
And then the method itself:
public void sendEmailTemplate(string body, string subject, string to, string cc , Attachments attachment = null)
{
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook._MailItem oMailItem = (Microsoft.Office.Interop.Outlook._MailItem)oApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
oMailItem.HTMLBody = body;
oMailItem.Subject = subject;
try
{
oMailItem.Attachments.Add(attachment);
}
catch {}
oMailItem.To = to;
oMailItem.CC = cc;
oMailItem.Display(false);
oMailItem.Application.ActiveInspector().WindowState = Microsoft.Office.Interop.Outlook.OlWindowState.olNormalWindow;
}
I've tried several things, however when I actually go to send the e-mail, I end up getting:
Exception: Member not found. HRESULT: 0x80020003
And then I haven't been able to get anything else to work. The try/catch loop on the method is to prevent the above exception as I was getting that exception regardless of whether or not an attachment was present, and now attachments just aren't being added.
I'm using Interop that comes with Office along with C#. Winforms if that makes a difference.
MailItem.Attachments takes either a string (fully qualified file name), or another Outlook item (MailItem, ContactItem, etc.).
You cannot pass Attachments object as an argument. If you need to copy the attachments, loop through all attachments in the Attachments collection, call Attachment.SaveAsFile for each attachment, pass the file name to MailItem.Attachments.Add, delete thee temporary file.

EWS error when retrieving complex property

We have an Exchange Web Services application that is throwing errors when an email is referenced that doesn't have a subject.
Our automated process needs to use the subject of the email, so the code is trying to reference it. However, when the subject is missing for an email in the inbox, instead of throwing an error, I want to change the behaviour.
Here is my code:
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(common.strInboxURL);
//creates a folder object that will point to inbox fold
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
... code removed fro brevity ...
// Find the first email message in the Inbox that has attachments. This results in a FindItem operation call to EWS.
FindItemsResults<Item> results = service.FindItems(fid, searchFilterCollection, view);
if (results.Count() > 0)
{
do
{
// set the prioperties we need for the entire result set
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived,
ItemSchema.DisplayTo, EmailMessageSchema.ToRecipients,
EmailMessageSchema.From, EmailMessageSchema.IsRead,
EmailMessageSchema.HasAttachments, ItemSchema.MimeContent,
EmailMessageSchema.Body, EmailMessageSchema.Sender,
ItemSchema.Body) { RequestedBodyType = BodyType.Text };
// load the properties for the entire batch
service.LoadPropertiesForItems(results, view.PropertySet);
so in that code, the error is being thrown on the complex property get on the line service.LoadPropertiesForItems(results, view.PropertySet); at the end.
So, I know I am going to have to do something like a Try..Catch here, however, I would need to check that the Subject property of the mail item exists before I can reference it to see what it is - some kind of chicken and egg problem.
If there is no subject, I need to mark the email as read, send a warning email off to a team, and then gon onwith the next unread email in the mailbox.
Any suggestions about the best way to approach this would be appreciated.
Thanks
Is the Subject not set or is it blank ?
You should be able to isolate any of these type of Emails with a SearchFitler eg use an Exists Search filter for the Subject property and then negate it so it will return any items where the Subject is not set
SearchFilter sfSearchFilteri = new SearchFilter.Exists(ItemSchema.Subject);
SearchFilter Negatesf = new SearchFilter.Not(sfSearchFilteri);
service.FindItems(WellKnownFolderName.Inbox, Negatesf, new ItemView(1000));
Then just exclude those items from your LoadPropertiesForItems
Cheers
Glen

Categories