I am trying to get unread emails and then mark them as read.
I am getting this exception when I run the code:
ServiceObjectPropertyException was unhandeld:
An unhandled exception of type
'Microsoft.Exchange.WebServices.Data.ServiceObjectPropertyException'
occurred in Microsoft.Exchange.WebServices.dll
This error occurred when I try to map my Exchange mail object to my business model object.
This is the Map method:
class MailMapper
{
public static PhishingMail Map(EmailMessage OutlookMail)
{
//Map Exchange email object op Business model email object
PhishingMail readMail = new PhishingMail();
readMail.Subject = OutlookMail.Subject;
return readMail;
}
}
This is the code where it should mark emails as read.
public List<PhishingMail> GetEmails()
{
phishingMailList = new List<PhishingMail>();
FolderId InboxId = new FolderId(WellKnownFolderName.Inbox, "A*******m#i*****nl");
FindItemsResults<Item> findResults = service.FindItems(InboxId, new ItemView(100));
foreach (Item phishingmail in findResults.Items)
{
((EmailMessage)phishingmail).Load(new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
if (!((EmailMessage)phishingmail).IsRead)
{
((EmailMessage)phishingmail).IsRead = true;
((EmailMessage)phishingmail)
.Update(ConflictResolutionMode.AutoResolve);
}
PhishingMail mail = MailMapper.Map((EmailMessage)phishingmail);
phishingMailList.Add(new PhishingMail());
/// Console.WriteLine(mail.Subject);
}
return phishingMailList;
}
What am I doing wrong? What is wrong with map method?
When you load an item, you speficy the properties to load.
item.Load(new PropertySet(PropertySet.FirstClassProperties));
If you need more properties after loading, you can do the following:
Service.LoadPropertiesForItems(items, PropertySet.FirstClassProperties);
I think the issue is that when you Load the email you're only asking for the ID and IsRead properties. Since your map routine fetches the Subject property, you would need to add that to the Load as well.
Related
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."
Hello I recently got into development around EWS. One of the issue came up to me is that a client ask me to import emails into database and he wants to detect the duplicate based on InternetMessageID this way he doesn't have to import the duplicate emails and my code came up to this point.
private static string GetInternetMessageID(Microsoft.Exchange.WebServices.Data.Item email)
{
EmailMessage emailMsg = email as EmailMessage;
string returnId = string.Empty;
if ((emailMsg != null)) {
try {
emailMsg.Load();
//loads additional info, without calling this ToRecipients (and more) is empty
} catch (ArgumentException ex) {
//retry
email.Load();
}
returnId = emailMsg.InternetMessageId;
} else {
//what to do?
}
return returnId;
}
I can handle regular emails, but for special exchange objects such as contact, Calendar, Posts etc it does not work because it could not cast it to an EmailMessage object.
And I know you can extract the internetMessageId from those objects. Because the client used to have another software that extract this ID for them, maybe the property is not called internetMessageID, I think I probally have to extract it from the internetMessageHeader. However when ever I try to get it from the item object it just throws me an error. How do I get the internet messageID from these "Special" exchange items?
PS i am aware of item.id.UniqueID however that is not what I want as this id changes if I move items from folder to another folder in exchange
Only objects that have been sent via the Transport service will have an InternetMessageId so things like Contacts and Tasks because they aren't messages and have never been routed via the Transport service will never have an Internet MessageId. You probably want to look at using a few properties to do this InternetMessageId can be useful for messages PidTagSearchKey https://msdn.microsoft.com/en-us/library/office/cc815908.aspx is one that can be used (if you good this there are various examples of using this property).
If your going to use it in Code don't use the method your using to load the property on each item this is very inefficient as it will make a separate call for each object. Because these I'd's are under 256 Kb just retrieve then when using FindItems. eg
ExtendedPropertyDefinition PidTagSearchKey = new ExtendedPropertyDefinition(0x300B, MapiPropertyType.Binary);
ExtendedPropertyDefinition PidTagInternetMessageId = new ExtendedPropertyDefinition(0x1035, MapiPropertyType.String);
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
psPropSet.Add(PidTagSearchKey);
psPropSet.Add(PidTagInternetMessageId);
ItemView ItemVeiwSet = new ItemView(1000);
ItemVeiwSet.PropertySet = psPropSet;
FindItemsResults<Item> fiRess = null;
do
{
fiRess = service.FindItems(WellKnownFolderName.Inbox, ItemVeiwSet);
foreach (Item itItem in fiRess)
{
Object SearchKeyVal = null;
if (itItem.TryGetProperty(PidTagSearchKey, out SearchKeyVal))
{
Console.WriteLine(BitConverter.ToString((Byte[])SearchKeyVal));
}
Object InternetMessageIdVal = null;
if (itItem.TryGetProperty(PidTagInternetMessageId, out InternetMessageIdVal))
{
Console.WriteLine(InternetMessageIdVal);
}
}
ItemVeiwSet.Offset += fiRess.Items.Count;
} while (fiRess.MoreAvailable);
If you need larger properties like the Body using the LoadPropertiesForItems Method https://blogs.msdn.microsoft.com/exchangedev/2010/03/16/loading-properties-for-multiple-items-with-one-call-to-exchange-web-services/
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);
}
}
I am using the Exchange Web Services Managed API 2.2 to monitor users inboxes and need to determine if an e-mail is a new item, a reply or a forwarded message.
I have seen various articles on SO such as how to notice if a mail is a forwarded mail? and Is there a way to determine if a email is a reply/response using ews c#? which both help in their specific cases but I still cannot work out how to distinguish between a reply and a forwarded item.
In the first article an extra 5 bytes is added each time (forward or reply) so I don't know what the last action was.
The second article suggests using the InReplyTo however when I examine the property for forwarded e-mails it contains the original senders e-mail address (not null).
I have seen other articles such as this or this that suggest using extended properties to examine the values in PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME.
My code looks as follows but never returns a value for lastVerbExecuted
var lastVerbExecutedProperty = new ExtendedPropertyDefinition(4225, MapiPropertyType.Integer);
var response = service.BindToItems(newMails, new PropertySet(BasePropertySet.IdOnly, lastVerbExecutedProperty));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
object lastVerb;
if (item.TryGetProperty(lastVerbExecutedProperty, out lastVerb))
{
// do something
}
}
PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME would only work to tell you if the recipient has acted on a message in their Inbox. Eg if the user had replied or forwarded a message in their inbox then these properties get set on the message in their Inbox. On the message that was responded to or forwarded these properties would not be set. I would suggest you use the In-Reply-To Transport header which should be set on any message that is replied to or forwarded, this should contain the internet messageid of the message that was replied to or forwarded eg.
FindItemsResults<Item> fiRs = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
PropertySet fiRsPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
ExtendedPropertyDefinition PR_TRANSPORT_MESSAGE_HEADERS = new ExtendedPropertyDefinition(0x007D, MapiPropertyType.String);
fiRsPropSet.Add(PR_TRANSPORT_MESSAGE_HEADERS);
service.LoadPropertiesForItems(fiRs.Items, fiRsPropSet);
foreach (Item itItem in fiRs)
{
Object TransportHeaderValue = null;
if(itItem.TryGetProperty(PR_TRANSPORT_MESSAGE_HEADERS,out TransportHeaderValue)) {
string[] stringSeparators = new string[] { "\r\n" };
String[] taArray = TransportHeaderValue.ToString().Split(stringSeparators, StringSplitOptions.None);
for (Int32 txCount = 0; txCount < taArray.Length; txCount++)
{
if (taArray[txCount].Length > 12)
{
if (taArray[txCount].Substring(0, 12).ToLower() == "in-reply-to:")
{
String OriginalId = taArray[txCount].Substring(13);
Console.WriteLine(OriginalId);
}
}
}
}
}
Apart from the Subject prefix that was discussed in the other link I don't know of any other proprieties that will differentiate between a reply or forward.
Cheers
Glen
The best way to rely is on the ResponeCode of Extended properties
Refer below scripts
private static int IsForwardOrReplyMail(ExchangeService service, EmailMessage messageToCheck)
{
try
{
// Create extended property definitions for PidTagLastVerbExecuted and PidTagLastVerbExecutionTime.
ExtendedPropertyDefinition PidTagLastVerbExecuted = new ExtendedPropertyDefinition(0x1081, MapiPropertyType.Integer);
ExtendedPropertyDefinition PidTagLastVerbExecutionTime = new ExtendedPropertyDefinition(0x1082, MapiPropertyType.SystemTime);
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.Subject, PidTagLastVerbExecutionTime, PidTagLastVerbExecuted);
messageToCheck = EmailMessage.Bind(service, messageToCheck.Id, propSet);
// Determine the last verb executed on the message and display output.
object responseType;
messageToCheck.TryGetProperty(PidTagLastVerbExecuted, out responseType);
if (responseType != null && ((Int32)responseType) == 104)
{
//FORWARD
return 104;
}
else if (responseType != null && ((Int32)responseType) == 102)
{
//REPLY
return 102;
}
}
catch (Exception)
{
return 0;
//throw new NotImplementedException();
}
}
To determine if it was a reply to a email, you can use the EmailMessage objects InReplyTo property, e.g:
EmailMessage mail = ((EmailMessage)Item.Bind(service, new ItemId(UniqueId)));
if (mail.InReplyTo == null)
return;
else
..your code
I am working with Exchange Web Services Managed API. I am adding a single extended property to mail items in inbox as they get processed based on some condition. Thus, not all mails will get this extended property attached to them.
Next I am refetching all mails in inbox and if they have this property attached to them, I process them again.
Below is the simple method getAllMailsInInbox(), I have written to refetch the mails in inbox:
class MyClass
{
private static Guid isProcessedPropertySetId;
private static ExtendedPropertyDefinition isProcessedPropertyDefinition = null;
static MyClass()
{
isProcessedPropertySetId = new Guid("{20F3C09F-7CAD-44c6-BDBF-8FCB324244}");
isProcessedPropertyDefinition = new ExtendedPropertyDefinition(isProcessedPropertySetId, "IsItemProcessed", MapiPropertyType.String);
}
public List<EmailMessage> getAllMailsInInbox()
{
List<EmailMessage> emails = new List<EmailMessage>();
ItemView itemView = new ItemView(100, 0);
FindItemsResults<Item> itemResults = null;
PropertySet psPropSet = new PropertySet(BasePropertySet.IdOnly);
itemView.PropertySet = psPropSet;
PropertySet itItemPropSet = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Attachments,
ItemSchema.Subject,
ItemSchema.Importance,
ItemSchema.DateTimeReceived,
ItemSchema.DateTimeSent,
ItemSchema.ItemClass,
ItemSchema.Size,
ItemSchema.Sensitivity,
EmailMessageSchema.From,
EmailMessageSchema.CcRecipients,
EmailMessageSchema.ToRecipients,
EmailMessageSchema.InternetMessageId,
ItemSchema.MimeContent,
isProcessedPropertyDefinition); //***
itemResults = service.FindItems(WellKnownFolderName.Inbox, itemView);
service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
String subject = itItem.Subject; //Exception: "You must load or assign this property before you can read its value."
//....
}
}
As you can see, on call service.LoadPropertiesForItems(), it does not load any properties, thus resulting in You must load or assign this property before you can read its value. exception while accessing any of those properties.
If I remove isProcessedPropertyDefinition from the itItemPropSet property set, it fetches all the properties properly.
So can I just know how can I fetch all built in EmailMessage properties along with the extended property?
Your GUID is two digits too short after the last dash. Strange that you're not seeing a FormatException. Still, you should update your code to inspect the GetItemResponse for each item. That way if some error occurs on one item, your code can be aware of it. That means you'll need to make another collection to return.
Update your code with this:
ServiceResponseCollection<ServiceResponse> responses = service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
foreach (ServiceResponse response in responses)
{
if (response.Result == ServiceResult.Error)
{
// Handle the error associated
}
else
{
String subject = (response as GetItemResponse).Item.Subject;
}
}
Instead of doing
service.LoadPropertiesForItems(itemResults.Items, itItemPropSet);
Try doing
itemResult.LoadPropertiesForItems(itItemPropSet);
Casue once you have the item, you can load the extended property of the item by loading the specific one.