I am trying to send meeting invites from user to a contact person using my chatbot. My solution is working fine when I run outlook in the background but when I close it, it throws an exception:
System.Runtime.InteropServices.COMException
When I tried to deploy this code on my server it showed me the same exception.
The code works on my local machine when I keep outlook running in the background.
Microsoft.Office.Interop.Outlook.Application outlookApplication = new Microsoft.Office.Interop.Outlook.Application();
// outlookApplication.Startup += new Microsoft.Office.Interop.Outlook.ApplicationEvents_11_StartupEventHandler(outlookApp_Startup);
Microsoft.Office.Interop.Outlook.AppointmentItem appointmentItem = (Microsoft.Office.Interop.Outlook.AppointmentItem)outlookApplication.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olAppointmentItem);
appointmentItem.Subject = "Dina need further assistance";
appointmentItem.Body = "Please Help me ";
appointmentItem.Location = "Room #1";
appointmentItem.Start = DateTime.Now;
foreach (string email in Recipients)
{
appointmentItem.Recipients.Add(email);
}
//appointmentItem.Recipients.Add("karmit.dhawan#accenture.com");
appointmentItem.End = DateTime.Now.AddHours(1);
appointmentItem.ReminderSet = true;
appointmentItem.ReminderMinutesBeforeStart = 15;
appointmentItem.Importance = Microsoft.Office.Interop.Outlook.OlImportance.olImportanceNormal;
appointmentItem.BusyStatus = Microsoft.Office.Interop.Outlook.OlBusyStatus.olBusy;
appointmentItem.MeetingStatus = Microsoft.Office.Interop.Outlook.OlMeetingStatus.olMeeting;
appointmentItem.Recipients.ResolveAll();
appointmentItem.Display(true);
I'm currently writing a Windows Service to log in to a specific Exchange account, get any new emails, parse them, and save the email in the appropriate folder.
Everything is working perfectly, except saving the email.
Relevant code blocks (try / catch blocks and irrelevant stuff removed to keep it short):-
Set up the Service
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(emailAddress, password);
service.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback);
CheckForNewEmails(service);
Getting and checking new emails
private static void CheckForNewEmails(ExchangeService service)
{
int offset = 0;
int pageSize = 50;
bool more = true;
ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
view.PropertySet = PropertySet.IdOnly;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
while (more)
{
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items)
{
emails.Add((EmailMessage)item);
}
more = findResults.MoreAvailable;
if (more)
{
view.Offset += pageSize;
}
}
if (emails.Count > 0)
{
PropertySet properties = (BasePropertySet.FirstClassProperties);
service.LoadPropertiesForItems(emails, properties);
var mailItems = new List<DatabaseService.MailItem>();
var dbService = new DatabaseService();
var defaultUser = dbService.GetDefaultUser(defaultUserID);
foreach (var email in emails)
{
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
}
}
}
Finding who it should be linked to
public void FindLinks(ExchangeService service, ref MailItem mailItem, User defaultUser)
{
string address = mailItem.Sender;
// get file hash
var tempPath = Path.GetTempPath();
var fileName = GetFilenameFromSubject(mailItem);
var fullName = Path.Combine(tempPath, fileName);
SaveAsEML(service, mailItem, fullName);
var sha = new SHA256Managed();
mailItem.Hash = Convert.ToBase64String(sha.ComputeHash(File.OpenRead(fullName)));
File.Delete(fullName);
using (var db = DatabaseHelpers.GetEntityModel())
{
// Do all the linking stuff
}
}
And finally, the problem area: Saving the file to disk (in this case to a temporary folder so I can get the file hash, to check it's not a duplicate (e.g. CC'd etc))
Looking through StackOverflow and various other sources, there seem to be two ways to do this:-
1) Using the MailItem.Load method
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
mailItem.mail.Load(new PropertySet(ItemSchema.MimeContent));
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Using the above code, the file is created and has the correct content.
However, attempting to access any of the email properties AFTER this point results in a Null Exception crash (big crash too, no Try/Catch or UnhandledException trap will pick it up, just kills the Service)
In the above code, this line crashes the entire service:-
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID ,
email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
email.Sender,
email.Subject,
mailItem.Hash,
mailItem.LinkString
));
Specifically, referencing email.DateTimeSent
I added in some diagnostic code directly before this line:-
if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }
The first line logs NOT null, so email still exists.
However, the second line instantly crashes with a null exception error, without logging anything.
If I comment out the line SaveAsEML(service, mailItem, fullName); from FindLinks then everything works perfectly (except of course the file isn't saved).
2) Binding a Property Set
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Doing it this way, nothing crashes, it goes through every email just fine, and can reference email.DateTimeSent and all the other properties.
Unfortunately, it creates zero-length files, no content.
Been banging my head against the wall for hours now over this (took me an hour of adding diagnostics everywhere just to track down the crash happening when referencing properties), and it's undoubtedly something trivial I've overlooked, so if some kind soul could point out my stupidity I'd be most grateful!
Edit:
I was able to work around the above problem easily enough by simply saving the value of the properties before using them:-
foreach (var email in emails)
{
var dateTimeSent = email.DateTimeSent;
var sender = email.Sender;
var subject = email.Subject;
var mailItem = new DatabaseService.MailItem();
mailItem.mail = email;
mailItem.MessageID = email.InternetMessageId;
mailItem.Sender = email.Sender.Address;
dbService.FindLinks(service, ref mailItem, defaultUser);
mailItems.Add(mailItem);
LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
mailItem.MessageID,
dateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
sender,
subject,
mailItem.Hash ?? "No Hash",
mailItem.LinkString ?? "No Linkstring"
));
}
This allowed me to use the MailItem.Load method which saved the file, and thus do what I was after in this specific case.
However there are other things this Service will need to do, and I really don't want to have to save a copy of every single property I'll need to access.
The reason this would fail
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
}
}
Is that you have used Bind to Load the message with the MimeContent in the email variable and then you haven't used that. mailItem.mail won't be linked at all to email variable created as part of this operation (even though they are the same object on the server). Locally these are just two separate variables. EWS is client/server so you make a request and the Managed API will return a local object that represents the result of the operation. But that object is disconnected hence when you do the bind above it just generates another client object to represent the result of that operation. eg so the above should have been
private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
{
PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
fileStream.Write(email.MimeContent.Content, 0, email .MimeContent.Content.Length);
}
}
I'm using the EWS Managed API in C# to download a bunch of messages from my company's exchange server. Loading the messages themselves takes a long time considering that service.FindItems() only fetches limited information about the messages, but it's not a huge deal. The serious problem I'm facing is how long it takes to load attachments.
The program is supposed to display an email and its image attachment side-by-side. When loading a new email, it can take well over a minute for the attachment to load. I initially fetched the attachments for each message when the message was loaded, but I thought it would be better to try to load them all at once into a List<EmailMessage> so the program wouldn't have to fetch the attachments when loading individual messages.
Here's the code I used to do that:
fetchView.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
fetchView.Traversal = FolderTraversal.Deep;
//create itemView for actual message query since we finally found the damn folder
ItemView iView = new ItemView(int.MaxValue);
FolderId sharedInboxFolder = new FolderId(WellKnownFolderName.Root, sharedMailbox);
FolderId targetFolder = new FolderId(WellKnownFolderName.Root, sharedMailbox);
FindFoldersResults inboxFolders = service.FindFolders(sharedInboxFolder, fetchView);
bool folderFound = false;
//look through the folders in the inbox to find the user-specified one by name
foreach(Folder f in inboxFolders)
{
if (f.DisplayName == Properties.Settings.Default.InboxFolder)
{
targetFolder = f.Id;
folderFound = true;
}
}
// Set itemview properties for FindItems() operation
fullProperties.Add(ItemSchema.Body);
fullProperties.Add(ItemSchema.Attachments);
fullProperties.Add(ItemSchema.DateTimeReceived);
fullProperties.Add(ItemSchema.Subject);
if (!folderFound)
{
MessageBox.Show("Folder not found!");
} else {
SearchFilter greaterthanfilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, searchDate);
SearchFilter lessthanfilter = new SearchFilter.IsLessThan(ItemSchema.DateTimeReceived, searchDate.AddDays(1));
SearchFilter dayFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, greaterthanfilter, lessthanfilter);
FindItemsResults<Item> fetchedMessages = service.FindItems(targetFolder, dayFilter, iView);
foreach (Item i in fetchedMessages.Items)
{
EmailMessage msg = EmailMessage.Bind(service, i.Id, fullProperties);
emails.Add(msg);
}
}
}
I then save all the attachments to disk with
for (int i = 0; i < message.Attachments.Count; i++)
{
if (message.Attachments[i] is FileAttachment)
{
FileAttachment att = message.Attachments[i] as FileAttachment;
att.Load();
using (FileStream attStream = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Program\images\" + i.ToString(), FileMode.Create, FileAccess.ReadWrite))
{
att.Load(attStream);
attStream.Close();
attStream.Dispose();
}
}
else
{
MessageBox.Show("Not FileAttachment!");
}
}
Then, to load an image and it's attachment, I do
imgViewer.Source = new BitmapImage(new Uri(/some/path/to/image/))
I suspect the hangup is during the save attachments phase. I found this post that indicated TraceFlags should be disabled, so I did service.TraceFlags = TraceFlags.None but that didn't seem to help at all. I'm contemplating just downloading all the attachments up front, or figuring out some kind of caching mechanism where I download the attachments of message[n+1...x] in the background while the user works on message[n], but this has limited usefulness, because the program should also let the user select an image and load it relatively instantly (i.e. much less than a minute).
Any suggestions are much appreciated.
Your loading the attachments twice for no apparent reason.
Remove the att.Load(); from your if (message.Attachments[i] is FileAttachment) statement.
Maybe have a look at implementing paging and in turn consider reducing your ItemView pageSize to batches of 1000 or less, instead of MaxValue
Also make sure you are only returning what you need in your PropertySet.
BasePropertySet.FirstClassProperties Consider changing this to just return what you need.
Side Note:
A good way to identify slow performing code is to use the .Net Stopwatch class.
Stopwatch sw = new Stopwatch();
sw.Start();
// code
sw.Stop();
StopWatchLog stopWatchLog = new StopWatchLog();
stopWatchLog.CreateXMLTextFile("MethodName()" + " took" + sw.Elapsed.Seconds + " seconds.");
I currently want to download all email messages (regardless in which folder they're in) to my SQL Server database.
Now while I know how to search for email messages or subscribe to streaming notifications, I've yet to learn on how to synchronize all messages from EWS to my database.
var emailMessages = GetItems<MSEmailMessage>(WellKnownFolderName.MsgFolderRoot);
foreach (var emailMessage in emailMessages)
{
Debug.WriteLine(emailMessage.Subject);
}
private IList<T> GetItems<T>(WellKnownFolderName wellKnownFolderName) where T : Item
{
IList<T> result = new List<T>();
Folder folder = Folder.Bind(_exchangeService, wellKnownFolderName);
if (folder.TotalCount > 0)
{
ItemView view = new ItemView(folder.TotalCount);
FindItemsResults<Item> items = _exchangeService.FindItems(wellKnownFolderName, view);
foreach (var resultItem in items.OfType<T>())
{
result.Add(resultItem);
}
}
return result;
}
This returns 0 email messages (it even threw an exception before checking for the folder.TotalCount before initializing a new ItemView...).
While checking for WellKnownFolderName.Inbox returns the email messages from the inbox, it does not allow me to query for sub folders to synchronize the entirety of the messages.
What am I missing?
You can build up a list of folders to search for mail in. Then iterate through each folder and get all the emails in that folder.
In the code snippet below we can create a folderSearchFilter with FolderTraversal set to Deep which will scan all sub folders of the target folder. We can then apply this filter to the two main well-known folders Inbox and SentItems
Once you have a list of folders to index, then you can use your own code to retrieve all the mails from that folder.
var view = new FolderView(int.MaxValue)
{
PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) { FolderSchema.DisplayName }
};
SearchFilter foldersearchFilter = new SearchFilter.IsGreaterThan(FolderSchema.TotalCount, 0);
view.Traversal = FolderTraversal.Deep;
List<Folder> searchFolders;
try
{
searchFolders = new List<Folder>
{
Folder.Bind(ExchangeService, WellKnownFolderName.Inbox),
Folder.Bind(ExchangeService, WellKnownFolderName.SentItems)
};
}
catch (ServiceResponseException e) {}
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.Inbox, foldersearchFilter, view).Folders);
searchFolders.AddRange(ExchangeService.FindFolders(WellKnownFolderName.SentItems, foldersearchFilter, view).Folders);
var results = new List<Item>();
foreach (var searchFolder in searchFolders)
{
//Get all emails in this folder
}
Right, the root folder likely has 0 messages in it. When you do FindItems in a folder, the results don't bubble up from subfolders. You need to iterate over every folder if you want to get to their messages.
I have an account on Gmail with 12,000 mail messages in inbox mailbox folder.
My problem is that I want to receive only top 100 mail messages, I tried a lot but I failed.
Here's the code
ImapClient go_client = new ImapClient();
CompletionResponse go_response = null;
Mailbox go_Folders_As_Tree = null;
go_client.Host = "Host";
go_client.Port = "port no";
go_client.Username = "user id";
go_client.Password = "pasword";
go_client.SSLInteractionType = EInteractionType.SSLPort;
go_response = go_client.Login();
go_Folders_As_Tree = go_client.GetMailboxTree();
Mailbox inbox = Mailbox.Find(go_Folders_As_Tree, "INBOX");
MessageCollection tmp = go_client.GetAllMessageHeaders(inbox);
foreach (ImapMessage msg in tmp)
{
ImapMessage actual_message = go_client.GetMessageText(msg.UID, go_Folders_As_Tree.Children[1]);
}
This way I have to wait for all my 12,000 mail messages.
int maxCount = 200;
for (int i = 1; i <= maxCount; i++) {
// Your Code here...
}
This looks like a good time to use LINQ's Take extension method:
MessageCollection tmp = go_client.GetAllMessageHeaders(inbox);
foreach (ImapMessage msg in tmp.Take(100))
{
ImapMessage actual_message = go_client.GetMessageText(msg.UID, go_Folders_As_Tree.Children[1]);
}
Note that this still downloads all 12,000 headers, but only the content of the first 100 (if this is a problem, give us a link to your ImapClient documentation so we might be able to find a better approach). Also note that if the headers are unordered, you'll get an unpredictable set of emails, and you might want to do an OrderBy first. (e.g. tmp.OrderByDescending(x=>x.Date).Take(100))