I have developed an engine to automatically process the emails sent to a particular mailbox using EWS (Exchange Web Services) Push Subscription. Everything is working fine except, I need to set the follow-up flag text with some custom message like we do in outlook (screen-shots below):
Custom text:
Sample email after setting the flag text:
I am using below code to do that, however the text is not displayed on email, only dates are reflecting with below code:
public bool MoveToFolder(EmailMessage mail, string folderName, bool MarkForFollowUp, string FollowUpText)
{
try
{
var folderView = new FolderView(100);
if (MarkForFollowUp)
{
try
{
ExtendedPropertyDefinition followUpTextFlag = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ChangeDetails", MapiPropertyType.String);
Flag flag = new Flag();
flag.FlagStatus = ItemFlagStatus.Flagged;
flag.StartDate = DateTime.Now;
flag.DueDate = DateTime.Now.AddHours(1);
mail.Flag = flag;
workLog.WriteVerbose($"Setting flag with followup-text: {FollowUpText}", "Notify.cs > MoveToFolder()");
mail.SetExtendedProperty(followUpTextFlag, FollowUpText);
mail.Update(ConflictResolutionMode.AutoResolve);
workLog.WriteVerbose($"Message follow-up flag set successfully.", "Notify.cs > MoveToFolder()");
}
catch (Exception ex)
{
// Ignore error while settings the flag
workLog.WriteVerbose($"Error occurred while setting the follow-up flag. Reason: {ex.Message}", "Notify.cs > MoveToFolder()");
}
}
folderView.PropertySet = new PropertySet(BasePropertySet.IdOnly);
folderView.PropertySet.Add(FolderSchema.DisplayName);
folderView.Traversal = FolderTraversal.Deep;
FindFoldersResults findFolderResults = _service.FindFolders(WellKnownFolderName.Root, folderView);
if (findFolderResults == default(FindFoldersResults))
{
return false;
}
try
{
FolderId folderId = findFolderResults.Cast<Folder>().FirstOrDefault(Folder => Folder.DisplayName == folderName).Id;
mail.Move(folderId);
return true;
}
catch
{
return false;
}
}
catch (Exception)
{
return false;
}
}
You need to set the PidLidFlagRequest extended property https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/pidlidflagrequest-canonical-property with the text you want to show eg
ExtendedPropertyDefinition followUpTextFlag = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "ChangeDetails", MapiPropertyType.String);
ExtendedPropertyDefinition PidLidFlagRequest = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Common, 0x8530, MapiPropertyType.String);
ExtendedPropertyDefinition PidLidFlagString = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Common, 0x85C0, MapiPropertyType.Integer);
Flag flag = new Flag();
flag.FlagStatus = ItemFlagStatus.Flagged;
flag.StartDate = DateTime.Now;
flag.DueDate = DateTime.Now.AddHours(1);
mail.Flag = flag;
mail.SetExtendedProperty(PidLidFlagRequest, "PidFlag Blah Blah");
mail.SetExtendedProperty(PidLidFlagString, 0);
mail.SetExtendedProperty(followUpTextFlag, "Blah Balh");
mail.Update(ConflictResolutionMode.AutoResolve);
Related
I am trying to find the unique id of a folder in Outlook.
For some reason I keep getting an error with the AutoDiscoverUrl method, but I have no idea why. I have tried all the possible solutions on StackOverflow.
I am trying to run it in a commandline program using C#, and have commented/documented the code. I have used others' example on how to do this, but it doesn't work.
static void Main(string[] args)
{
// Set server binding
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.UseDefaultCredentials = true;
// Set Credentials
service.Credentials = new WebCredentials("xxxx", "xxxxx", "xxxx");
service.UseDefaultCredentials = true;
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
// Set the URL
service.AutodiscoverUrl("xxxx", Callback);
// Error Tracing
service.TraceEnabled = true;
// Redirect callback
// Set View
FolderView view = new FolderView(100);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly);
view.PropertySet.Add(FolderSchema.DisplayName);
view.Traversal = FolderTraversal.Deep;
FindFoldersResults findFolderResults = service.FindFolders(WellKnownFolderName.Root, view);
// Find specific folder
foreach (Folder f in findFolderResults)
{
// Show FolderId of the folder "test"
if (f.DisplayName == "test")
{
Console.WriteLine(f.Id);
}
}
}
private static bool Callback(string redirectionUrl)
{
bool result = false;
var redirectionUri = new Uri(redirectionUrl);
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
You could find the unique id of the folder using the below code:
ExchangeService Service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
Service.UseDefaultCredentials = false;
Service.Credentials = new WebCredentials("yourUserID", "yourPassword");
Mailbox ProdSupportMailbox = new Mailbox("ProdSupport#company.com");
Service.AutodiscoverUrl("ProdSupport#company.com");
FolderView view = new FolderView(100);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly);
view.PropertySet.Add(FolderSchema.DisplayName);
view.Traversal = FolderTraversal.Deep;
FindFoldersResults findFolderResults = server.FindFolders(WellKnownFolderName.Root, view);
// find specific folder
foreach(Folder f in findFolderResults)
{
// show folderId of the folder "Test"
if (f.DisplayName == "Test")
{
Console.WriteLine(f.Id);
}
}
For more information, please refer to these links:
Exchange Web Service FolderId for a not well known folder name
Microsoft Exchange Web Services reading my local Outlook folders INSTEAD of another address
Please i use a service windows to acced to my email. i have this exception when i try to bind my folder
Here is my code
private static readonly ExchangeVersion _ExchangeServerVersion = ExchangeVersion.Exchange2010;
private static readonly IWebProxy _ExchangeWebProxy;
public static EmailClient CreateClient(string account, string password, string domain, string mailtowatch=null ,TimeSpan? timeout = null )
{
if (!timeout.HasValue)
{
timeout = new TimeSpan(0, 5, 0);
}
EmailClient result = new EmailClient();
result.Mailbox = mailtowatch;
ExchangeService client = new ExchangeService(_ExchangeServerVersion);
client.UseDefaultCredentials = true;
if (!string.IsNullOrEmpty(password))
{
client.Credentials = new WebCredentials(account, password, domain);
}
try
{
client.AutodiscoverUrl(mailtowatch);
}
catch (Exception)
{
client.Url = new Uri("https://office.natixis.com/EWS/Exchange.asmx");
}
client.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, mailtowatch);
client.WebProxy = WebRequest.DefaultWebProxy;
client.WebProxy = _ExchangeWebProxy;
result.Service = client;
client.Timeout = (int)(timeout.Value.TotalMilliseconds);
return result;
}
*******************************
and i call it here
*******************************
Folder folder = null;
FolderId id = null;
if (criteria.FolderName != null)
{
log.Debug(string.Format("Getting folder {0}", criteria.FolderName));
id = GetFolderId(criteria.FolderName);
}
log.Debug("Start binding folder");
if (criteria.Password == null)
{
var folderTemp = new FolderId(WellKnownFolderName.Inbox, criteria.EmailToWatch); //Or the folder you want to search in
folder = Folder.Bind(Service, folderTemp);
}
else
{
//client.UseDefaultCredentials = true;
folder = Folder.Bind(Service, id ?? new FolderId(WellKnownFolderName.Inbox, new Mailbox( criteria.EmailToWatch) ));
}
but on bindid the folder i have an exception ErrorNonExistentMailbox.
Even when i use UseDefaultCredentials = true is not working
I am using Outlook2013 which has a number of mailboxes from both exchange and pop servers.(Rob#mydomain.com[default exchange], rob#somethingdifferent.com[POP], support#mydomain.com[exchange])
I am trying to use Outlook automation to send an email using the support#mydomain.com account.
The problem I am having is the below code creates a mail item in the support outbox but the from field is rob#mydomain.com not support#mydomain.com. This stops it from being sent.
I would like to change the from address to support#mydomain.com. I thought that by setting the Sendusingaccount property did this.
Any help is greatly appreciated.
public static string Send_Email_Outlook(string _recipient, string _message, string _subject, string _cc, string _bcc, string accountname)
{
try
{
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
// Get the NameSpace and Logon information.
Microsoft.Office.Interop.Outlook.NameSpace oNS = oApp.GetNamespace("mapi");
// Log on by using a dialog box to choose the profile.
oNS.Logon(Missing.Value, Missing.Value, true, true);
// Create a new mail item.
Microsoft.Office.Interop.Outlook.MailItem oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
// Set the subject.
oMsg.Subject = _subject;
// Set HTMLBody.
oMsg.HTMLBody = _message;
oMsg.To = _recipient;
oMsg.CC = _cc;
oMsg.BCC = _bcc;
#region Send via another account
if (accountname.Trim().Length != 0)
{
Microsoft.Office.Interop.Outlook.Accounts accounts = oMsg.Session.Accounts;
for (int i = 1; i <= accounts.Count; i++)
{
string accountfound = accounts[i].DisplayName.ToLower();
if (accountname.ToLower() == accountfound)
{
oMsg.SendUsingAccount = accounts[i]; // Send using support account
Microsoft.Office.Interop.Outlook.Recipient recipient = oMsg.Session.CreateRecipient(accountfound);
oMsg.Sender = recipient.AddressEntry;
break;
}
}
}
#endregion
// Send.
(oMsg as Microsoft.Office.Interop.Outlook._MailItem).Send();
// Log off.
oNS.Logoff();
// Clean up.
//oRecip = null;
//oRecips = null;
oMsg = null;
oNS = null;
oApp = null;
}
// Return Error Message
catch (Exception e)
{
return e.Message;
}
// Default return value.
return "";
}
Yes, you can use the SendUsingAccount property to set up the correct account you need to send items from.
public static Outlook.Account GetAccountForEmailAddress(Outlook.Application application, string smtpAddress)
{
// Loop over the Accounts collection of the current Outlook session.
Outlook.Accounts accounts = application.Session.Accounts;
foreach (Outlook.Account account in accounts)
{
// When the e-mail address matches, return the account.
if (account.SmtpAddress == smtpAddress)
{
return account;
}
}
throw new System.Exception(string.Format("No Account with SmtpAddress: {0} exists!", smtpAddress));
}
public static string Send_Email_Outlook(string _recipient, string _message, string _subject, string _cc, string _bcc, string accountname)
{
try
{
Microsoft.Office.Interop.Outlook.Application oApp = new Microsoft.Office.Interop.Outlook.Application();
// Get the NameSpace and Logon information.
Microsoft.Office.Interop.Outlook.NameSpace oNS = oApp.GetNamespace("mapi");
// Log on by using a dialog box to choose the profile.
oNS.Logon(Missing.Value, Missing.Value, true, true);
// Create a new mail item.
Microsoft.Office.Interop.Outlook.MailItem oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oApp.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
// Set the subject.
oMsg.Subject = _subject;
// Set HTMLBody.
oMsg.HTMLBody = _message;
oMsg.To = _recipient;
oMsg.CC = _cc;
oMsg.BCC = _bcc;
#region Send via another account
// Retrieve the account that has the specific SMTP address.
Outlook.Account account = GetAccountForEmailAddress(oApp , "support#mydomain.com");
// Use this account to send the e-mail.
oMsg.SendUsingAccount = account;
// Send.
(oMsg as Microsoft.Office.Interop.Outlook._MailItem).Send();
// Log off.
oNS.Logoff();
// Clean up.
//oRecip = null;
//oRecips = null;
oMsg = null;
oNS = null;
oApp = null;
}
// Return Error Message
catch (Exception e)
{
return e.Message;
}
// Default return value.
return "";
}
Hello I am using the following code to get the custom header from EWS.
But unfortunately it's not returning the header. I looked into the outlook for the headers using Mapi tool, where I can see the headers.
Any suggestions please.
service = ExchangeServiceHelpers.GetBinding();
// Bind the Inbox folder to the service object
var inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
var searchFilter = ExchangeServiceHelpers.PopulateSearchFilters();
var view = new ItemView(int.MaxValue); // Search operation should return maximum number of elements.
// Defines a property set that contains the schematized Internet message headers.
var headerProperty = new ExtendedPropertyDefinition(
DefaultExtendedPropertySet.InternetHeaders,
"x-worksitefolderemailid",
MapiPropertyType.String);
var columns = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.InternetMessageId, headerProperty);
view.PropertySet = columns;
// Fire the query for the unread items
var findResults = inbox.FindItems(searchFilter, view);
// Loop through the search results.
foreach (EmailMessage message in findResults)
{
try
{
message.Load(
new PropertySet(new PropertyDefinitionBase[] { ItemSchema.MimeContent, ItemSchema.Subject}));
string mailAddress = GetFolderId(message, headerProperty); // Get internet header
if (string.IsNullOrEmpty(mailAddress))
{
Logger.Info(
string.Format("Email '{0}' doesn't have folder id address. Marking as Read Item.",
message.Subject));
ExchangeServiceHelpers.MarkMessageAsRead(service, message.Id); // Marking the email item as Read prevents the item to be returned in further search results.
continue;
}
}
catch (Exception e)
{
Logger.Error(e);
}
}
private static string GetFolderId(EmailMessage message, ExtendedPropertyDefinition headerProperty)
{
try
{
if (message.ExtendedProperties == null || message.ExtendedProperties.Count == 0)
{
Logger.Info(
string.Format("Email '{0}' doesn't have any extended properties. Marking as Read Item.",
message.Subject));
return string.Empty;
}
//message.InternetMessageHeaders
foreach (ExtendedProperty property in message.ExtendedProperties)
{
if (property.PropertyDefinition == headerProperty)
{
return property.Value.ToString();
}
}
}
catch (Exception ex)
{
Logger.Error(ex);
}
return string.Empty;
}
Naresh,
The inbox.FindItems() call won't return the internet headers. You'll need to update message.Load() to use a property set that includes headerProperty.
With regards,
With reference to my previous question, I already wrote my program and it is working awesome when I am using VS2008 for run it.
I have two more questions:
1. I want to check with you guys when I run my program all the mail are appearing in VS output as xml file, but I never used to print them in output. is it usual for all or I need to add something to remove it. I feel it takes long time from my PC to show mails in output.
2. my second question is that when I want to use only exe file stand alone(run program via exe file not with VS ) I am receiving below error and program is hanging & close.
"MailReader has encountered a problem and needs to close. We are
sorry for the inconvenience."
As I mentioned above this program is working fine in VS.
I copy part of my code that read mails and split them for your reference.
public void ReadMail()
{
ServicePointManager.ServerCertificateValidationCallback += delegate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) { return true; };
try
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.TraceEnabled = true;
service.Credentials = new WebCredentials(_username, _password); //Modify this
service.Url = new Uri(_exchange); //Modify this
service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri(_exchange);
service.TraceEnabled = true;
service.Credentials = new WebCredentials(_username, _password); //Modify this
service.Url = new Uri(_exchange);
//SearchFilter to get unreaded messages only.
SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
ItemView itemview = new ItemView(Int16.MaxValue);
//DateTime searchdate = new DateTime(2012, 7, 6); //Year, month, day
SearchFilter greaterthanfilter = new SearchFilter.IsGreaterThan(ItemSchema.DateTimeSent, Convert.ToDateTime(startDate));
SearchFilter lessthanfilter = new SearchFilter.IsLessThan(ItemSchema.DateTimeSent,Convert.ToDateTime(finishDate));
SearchFilter[] f = { greaterthanfilter, lessthanfilter };
SearchFilter filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, f);
//Folder folder = Folder.Bind(this.m_Service, WellKnownFolderName.MsgFolderRoot); //Or the folder you want to search in
//FindItemsResults<Item> results = folder.FindItems(filter, new ItemView(1000));
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.SentItems,filter, itemview);
Action action = () => fr.setText(findResults.Items.Count + "mails need to analysis from "+startDate +" to "+ finishDate);
fr.Invoke(action, null);
action = () => fr.setMaximumProgressBar(findResults.Items.Count);
fr.Invoke(action, null);
dmd = new List<DailyMailDetails>();
foreach (Item item in findResults.Items)
{
string messageDate = "Error in Date";
string messageSubj = "Error in Subject";
int index = 0;
try
{
PropertySet propertySet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.DateTimeSent, ItemSchema.Body, ItemSchema.Subject);
propertySet.RequestedBodyType = BodyType.Text;
EmailMessage message = EmailMessage.Bind(service, item.Id, propertySet);
string temp = startSign.ToUpper();
int start = message.Body.Text.ToUpper().IndexOf(temp) + temp.Length;
int end = message.Body.Text.ToUpper().IndexOf(finishSign.ToUpper());
int len = end - start;
string text = message.Body.Text.Substring(start, len);
index = findDmdIndex(message.DateTimeSent.ToShortDateString().ToString());
if (index == -1)
{
dmd.Add(new DailyMailDetails(message.DateTimeSent.ToShortDateString().ToString(), (List<PeopleSigniture>)Extensions.Clone<PeopleSigniture>(OrginallistPeopleSign)));
index = dmd.Count - 1;
}
bool signExist = false;
for (int i = 0; i < listPeopleSign.Count; i++)
if (text.ToUpper().Contains(dmd[index].peopleSign[i].Signiture.ToUpper()))
{
dmd[index].peopleSign[i].addResponse(message.DateTimeSent.ToString(), message.Subject.ToString());
signExist = true;
break;
}
messageDate = message.DateTimeSent.ToString();
messageSubj = message.Subject.ToString();
if (!signExist)
dmd[index].peopleSign[dmd[index].peopleSign.Count - 2].addResponse(message.DateTimeSent.ToString(), message.Subject.ToString());
}
catch (Exception ex)
{
dmd[index].peopleSign[dmd[index].peopleSign.Count - 1].addResponse(messageDate, messageSubj);
}
action = () => fr.increasePrograss();
fr.Invoke(action, null);
}
}
catch (Exception ex)
{
MessageBox.Show("Class: Mail Function:ReadMail" + ex.Message);
}
Action action2 = () => fr.setText(ToString(true),dmd);
fr.Invoke(action2, null);
}
For issue #1 - you are viewing the XML output likely because you have EWS tracing enabled. You need to set ExchangeService.TraceEnabled to false or comment it out entirely. (You also have many duplicate lines of code you need to cleanup.)
service.TraceEnabled = false;
For issue #2 - you need to determine the actual .NET exception. Without this - we cannot help you further. It could be crashing for countless reasons. Please provide a stack trace.