Determining the Id of a Sent Message by Using Extended Properties with the EWS Managed API
Occasionally, an Exchange Web Services (EWS) development scenario requires that you attain the identifier of sent message. Although you may expect to find the identifier of the sent message in the XML response that’s returned from calling EmailMessage.SendAndSaveCopy() , no such information exists in the response. The following example shows the CreateItemResponse that’s returned from calling EmailMessage.SendAndSaveCopy() by using the EWS Managed API.
<m:CreateItemResponse
xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:CreateItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Items />
</m:CreateItemResponseMessage>
</m:ResponseMessages>
</m:CreateItemResponse>
Because EWS sends messages asynchronously, the identifier of the message that’s saved in the Sent Items folder when you call EmailMessage.SendAndSaveCopy() is not available immediately, and therefore is not included in the XML response.
So what’s a developer to do, if basic message properties like subject are not unique enough to identify the sent message? Well, have no fear, for extended properties are here! Simply stamp your e-mail message with a custom extended property when you create the message, and then use that extended property to find the message in the Sent Items folder after it has been sent.
The following code example shows you how to create a custom extended property on an e-mail message, send the message, and then find the message by searching the Sent Items folder for an item that has the specified extended property value.
// Create the message and set some basic properties.
EmailMessage message = new EmailMessage(service);
message.Subject = "Message Subject";
message.Body = "This message was sent by using the EWS Managed API.";
message.ToRecipients.Add("User1@example.com");
// Create a custom extended property and add it to the message.
Guid myPropertySetId = new Guid("{20B5C09F-7CAD-44c6-BDBF-8FCBEEA08544}");
ExtendedPropertyDefinition myExtendedPropertyDefinition = new ExtendedPropertyDefinition(myPropertySetId, "MyExtendedPropertyName", MapiPropertyType.String);
message.SetExtendedProperty(myExtendedPropertyDefinition, "MyExtendedPropertyValue");
// Send the message and save a copy.
message.SendAndSaveCopy();
// Wait one second (while EWS sends and saves the message).
System.Threading.Thread.Sleep(1000);
// Now, find the saved copy of the message by using the custom extended property.
ItemView view = new ItemView(5);
SearchFilter searchFilter = new SearchFilter.IsEqualTo(myExtendedPropertyDefinition, "MyExtendedPropertyValue");
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, myExtendedPropertyDefinition);
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.SentItems, searchFilter, view);
// Process results.
foreach (Item myItem in findResults.Items)
{
if (myItem is EmailMessage)
{
EmailMessage em = myItem as EmailMessage;
Console.WriteLine(em.Subject);
Console.WriteLine(em.Id.UniqueId);
}
}
Note: The string value that is used to instantiate the Guid in this example was generated by using guidgen.exe within Microsoft Visual Studio 2008, but you can also use other Guid generation tools.
The following is the XML request that is generated by calling FindItems in the above code example.
<m:FindItem Traversal="Shallow">
<m:ItemShape>
<t:BaseShape>IdOnly</t:BaseShape>
<t:AdditionalProperties>
<t:FieldURI FieldURI="item:Subject" />
<t:ExtendedFieldURI PropertySetId="20b5c09f-7cad-44c6-bdbf-8fcbeea08544" PropertyName="MyExtendedPropertyName" PropertyType="String" />
</t:AdditionalProperties>
</m:ItemShape>
<m:IndexedPageItemView MaxEntriesReturned="5" Offset="0" BasePoint="Beginning" />
<m:Restriction>
<t:IsEqualTo>
<t:ExtendedFieldURI PropertySetId="20b5c09f-7cad-44c6-bdbf-8fcbeea08544" PropertyName="MyExtendedPropertyName" PropertyType="String" />
<t:FieldURIOrConstant>
<t:Constant Value="MyExtendedPropertyValue" />
</t:FieldURIOrConstant>
</t:IsEqualTo>
</m:Restriction>
<m:ParentFolderIds>
<t:DistinguishedFolderId Id="sentitems" />
</m:ParentFolderIds>
</m:FindItem>
As the following XML response shows, the e-mail message that was created in the code example above is successfully located by using the custom extended property that was set when the e-mail message was created.
<m:FindItemResponse
xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:FindItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:RootFolder IndexedPagingOffset="1" TotalItemsInView="1" IncludesLastItemInRange="true">
<t:Items>
<t:Message>
<t:ItemId Id="aeBAAA=" ChangeKey="CQAAA+s" />
<t:Subject>Message Subject</t:Subject>
<t:ExtendedProperty>
<t:ExtendedFieldURI PropertySetId="20b5c09f-7cad-44c6-bdbf-8fcbeea08544" PropertyName="MyExtendedPropertyName" PropertyType="String" />
<t:Value>MyExtendedPropertyValue</t:Value>
</t:ExtendedProperty>
</t:Message>
</t:Items>
</m:RootFolder>
</m:FindItemResponseMessage>
</m:ResponseMessages>
</m:FindItemResponse>
Want to learn more about working with extended properties and search in the EWS Managed API? Check out the code examples that are available in the Working with Extended Properties and Working with Search sections of the Microsoft Exchange Web Services Managed API 1.0 SDK.
Comments
Anonymous
January 16, 2014
It works for me. Thank you very much. Saved my day.Anonymous
February 12, 2014
Thanks, It is working fine.Anonymous
June 12, 2014
While this works, it puts a significant load on the Exchange store. Let me suggest the following as a better way:
- Set your extended prop.
- Do a FindItem with NO restriction against the sent items folder, SORTED by creation date descending with a indexed page view of about 5. Include your extended prop in the PropertySet.
- Iterate across the results looking for your extended prop.
- Optionally update the found items and delete the extended prop so that it isn’t there on the next go around. If this is something that you do very often, then a possibility is to create a search folder that checks for Exists(YourExtendedProp). Create this at process startup if it doesn’t already exist – you can give your search folder some funny name like a GUID. Then you can do the above. Of course in that case, it behooves you to delete the extended prop after finding it so that your result set doesn’t grow each time. What you want to avoid is creating a bunch of “throw away” restrictions (ones that change regularly).
Anonymous
August 10, 2014
Hello David Sterling, I can not implement the proposal before you speak. Can you suggest a piece of working code? thank youAnonymous
February 18, 2015
i've diferent mail adress in ews credentials and msg sender. how i get the notifications of delivery or not in another mail adress? thank youAnonymous
December 08, 2015
The comment has been removedAnonymous
December 09, 2015
No, that article seems to say that the number of distinct custom extended properties tops out at 32768. You should be able to reuse the same custom extended property (with different values) as much as you want.Anonymous
December 11, 2015
Yes, Dan, you are right. After some research and tests i have to admit, this is the only method for tracking sent messages i was able to find, but it needs some tweaking. It's impossible to identify message, if extended property has always same id and value. We need new unique property for every message or use same property with different values as Dan suggest. Since first approach will lead to problem with properties limit, the conclusion is obvious. Another thing is "System.Threading.Thread.Sleep(1000);", guess it’s clear why it's not good. Code: // the max time waiting for message is MAX_COUNT * SLEEP_TIME ms int MAX_COUNT = 30; int SLEEP_TIME = 1000; //may be using integer instead of string will speed up search? string myMailId = Guid.NewGuid().ToString(); ExtendedPropertyDefinition myExtendedPropertyDefinition = new ExtendedPropertyDefinition(new Guid("{20B5C09F-7CAD-44c6-BDBF-8FCBEEA08544}"), "MyMailId", MapiPropertyType.String); message.SetExtendedProperty(myExtendedPropertyDefinition, myMailId); message.SendAndSaveCopy(); EmailMessage sentMessage = null; int count = MAX_COUNT; while (count > 0) { count--; System.Threading.Thread.Sleep(SLEEP_TIME); var findResults = message.Service.FindItems(WellKnownFolderName.SentItems , new SearchFilter.IsEqualTo(myExtendedPropertyDefinition, myMailId) , new ItemView(1) { PropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, myExtendedPropertyDefinition) } ); if (findResults.Items.Count > 0 && findResults.Items[0] is EmailMessage) { sentMessage = ((EmailMessage)findResults.Items[0]); break; } } if (sentMessage == null) { //process timeout error } else { //do what you need to do with sentMessage } My apologies to the author, for compromising this article without reason.- Anonymous
September 13, 2017
This doesn't work at all...What is happening in my scenario is that the message that ends up getting saved in the SentItems folder has a different Conversation Id to the one that lands in the target email address. So, when the target email sends a reply back, the conversation Ids don't match and this is completely useless.
- Anonymous
Anonymous
September 13, 2017
This doesn’t work at all…What is happening in my scenario is that the message that ends up getting saved in the SentItems folder has a different Conversation Id to the one that lands in the target email address. So, when the target email sends a reply back, the conversation Ids don’t match and this is completely useless.