We just launched a new task to retrieve emails from exchange server for different accounts using asp.net web application. I don't have such experience in past, but after searching online I found a code snippet which could communicate with outlook and fetch emails from there. However, there is an exception occurs every time I test the code which is :
"Unable to cast COM object of type
'System.__ComObject' to interface type
'Microsoft.Office.Interop.Outlook.PostItem'.
This operation failed because the
QueryInterface call on the COM
component for the interface with IID
'{00063024-0000-0000-C000-000000000046}'
failed due to the following error: No
such interface supported (Exception
from HRESULT: 0x80004002
(E_NOINTERFACE)). "
Does anyone know the reason?
By the way, any suggestion helping on fetching emails from exchange server directly is highly appreciated!
My code:
Microsoft.Office.Interop.Outlook.Application app = null;
Microsoft.Office.Interop.Outlook._NameSpace ns = null;
Microsoft.Office.Interop.Outlook.PostItem item = null;
Microsoft.Office.Interop.Outlook.MAPIFolder inboxFolder = null;
Microsoft.Office.Interop.Outlook.MAPIFolder subFolder = null;
try
{
app = new Microsoft.Office.Interop.Outlook.Application();
ns = app.GetNamespace("MAPI");
ns.Logon("user", "password", false, false);
inboxFolder = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Folder Name:{0},EntryId:{1}", inboxFolder.Name, inboxFolder.EntryID);
sb.AppendFormat(" Num Items:{0}", inboxFolder.Items.Count.ToString());
Response.Write(sb);
for (int i = 1; i <= inboxFolder.Items.Count; i++)
{
item = (Microsoft.Office.Interop.Outlook.PostItem)inboxFolder.Items[i];//this is the exception happened line
Console.WriteLine("Item: {0}", i.ToString());
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Sent: {0} {1}" item.SentOn.ToLongDateString(), item.SentOn.ToLongTimeString());
Console.WriteLine("Categories: {0}", item.Categories);
Console.WriteLine("Body: {0}", item.Body);
Console.WriteLine("HTMLBody: {0}", item.HTMLBody);
}
}
catch (Exception)
{
throw;
}
finally
{
ns = null;
app = null;
inboxFolder = null;
}
Here goes:
With these lines...
Microsoft.Office.Interop.Outlook.MAPIFolder inboxFolder = null;
inboxFolder = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
...you're creating a reference to the user's Inbox. So far so good.
But then here...
for (int i = 1; i <= inboxFolder.Items.Count; i++)
{
item = (Microsoft.Office.Interop.Outlook.PostItem)inboxFolder.Items[i];
//other stuff...
}
...you're saying, "for every Item in the Inbox, assume that the Item is a PostItem."
According to MSDN, a PostItem:
Represents a post in a public folder
that others may browse.
The user's inbox isn't going to be full of post items. It's going to contain MailItem objects representing the user's emails. That being the case, that line of code should probably be
item = (Microsoft.Office.Interop.Outlook.MailItem)inboxFolder.Items[i];
Caveat: I don't know enough about Outlook's API to understand whether it's possible for there to be Outlook Item Objects other than MailItem objects in the Inbox, but I'd wager not. For reference, the full list of Outlook Item Objects is here.
Use as shown below:
// not PostItem
item = (Microsoft.Office.Interop.Outlook.MailItem)inboxFolder.Items[i];
Related
I need to write a console application that essentially will:
Access a group/shared mailbox's inbox.
Select all emails older than n minutes.
Move the selected emails to another folder "AgedEmails".
So far the program I wrote works well connecting to my email account, even not passing credentials.
The challenge is to access the shared emailbox not mine. The program will run in a server on a frequency set in Windows task scheduler. I have read many postings where the problem is the same I have, but could not find a solution that works.
I have tried the nameSpace.Logon method but it always connects to my employee's email account.
These are the many ways I tried to login to the shared email account (none work):
outlookNameSpace.Logon("mailboxname#company.com", "", true, true);
outlookNameSpace.Logon("mailboxname#company.com", "theRealPassword");,
and this is how I try to get a handle into the inbox:
inboxFolder = outlookNameSpace.Folders["mailboxname#company.com"].Folders["Inbox"];
I am looking for some one to put me in the right direction to achieve the goal. This application will run unattended.
Thanks in advance for your support. Here is the source code:
static async Task MoveEmailsAsync()
{
StreamWriter logFile = new StreamWriter(path, append: true);
Application outLookApplication = null;
NameSpace outlookNameSpace = null;
MAPIFolder inboxFolder = null;
MAPIFolder sourceFolder = null;
MAPIFolder testFolder = null;
Items mailItems = null;
string sSourceFolder = ConfigurationManager.AppSettings["SourceFolder"];
string destinationFolder = ConfigurationManager.AppSettings["DestinationFolder"];
try
{
int minutesAged = Convert.ToInt16(ConfigurationManager.AppSettings["MinutesAged"]);
DateTime age = DateTime.Now;
outLookApplication = new Application();
outlookNameSpace = outLookApplication.GetNamespace("MAPI");
inboxFolder = outlookNameSpace.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
sourceFolder = inboxFolder.Folders[sSourceFolder.ToString()];
testFolder = inboxFolder.Folders[destinationFolder.ToString()];
mailItems = sourceFolder.Items;
string from = null;
string subject = null;
int counter = mailItems.Count;
int i = 0;
for (int k = counter; k >= 1; k--)
{
try
{
i++;
if (true) //this condition will be removed
{
from = null;
subject = null;
Microsoft.Office.Interop.Outlook.MailItem mailitem = (Microsoft.Office.Interop.Outlook.MailItem)mailItems[k];
TimeSpan ts = DateTime.Now - mailitem.ReceivedTime;
if (ts.TotalMinutes > minutesAged)
{
if (!((mailitem.SenderEmailAddress == null) || (mailitem.SenderEmailAddress == "")))
{
from = mailitem.SenderEmailAddress.ToString();
}
if (!((mailitem.Subject == null) || (mailitem.Subject == "")))
{
subject = mailitem.Subject;
}
await logFile.WriteLineAsync(i + ". From: " + from + " - Subject: " + subject);
mailitem.Move(testFolder);
}
}
}
catch (Exception e)
{
await logFile.WriteLineAsync("Exception caught: " + e.ToString());
}
}
await logFile.WriteLineAsync(DateTime.Now.ToString() + " - End of Job.");
}
catch (Exception e)
{
await logFile.WriteLineAsync("Exception caught: " + e.ToString());
}
logFile.Flush();
logFile.Close();
}
}
Firstly, Namespace.Logon takes the name of an existing profile (as shown in Control Panel | Mail | Show Profiles), not an address of a mailbox.
Secondly, to open a folder from another user's mailbox, replace the GetDefaultFolder call with Namespace.CreateRecipient / Namespace.GetSharedDefaultFolder
Answers to your questions don't make any sense due to the following statement:
The program will run in a server on a frequency set in Windows task scheduler.
That is the key statement in your post because MS states the following for such scenarios:
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution.
Read more about that in the Considerations for server-side Automation of Office article.
If you deal with Exchange accounts you may consider using EWS instead, see Explore the EWS Managed API, EWS, and web services in Exchange for more information. In case of Office365 consider using the Graph API, see Use the Microsoft Graph API.
i am working on a code that get's a list and checks if the items is in emails subjects, i am trying to go through all the emails to compare to the list but the program crashes after 190 emails or so (there are 289 emails in the folder)
I searched online for solutions for this problem and nothing worked for me
oApp = new Microsoft.Office.Interop.Outlook.Application();
// Get the MAPI namespace.
oNS = oApp.GetNamespace("mapi");
// Log on by using the default profile or existing session (no dialog box).
oNS.Logon(Missing.Value, Missing.Value, false, true);
// Alternate logon method that uses a specific profile name.
// TODO: If you use this logon method, specify the correct profile name
// and comment the previous Logon line.
//oNS.Logon("profilename",Missing.Value,false,true);
//Get the Inbox folder.
oInbox = oNS.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
//Get the Items collection in the Inbox folder.
oItems = oInbox.Items;
MessageBox.Show(oItems.Count+"");
oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oItems.GetLast();
public void SearchEmail()
{
if (counter<oItems.Count)
{
try
{
string s;
if (oMsg != null)
{
s = oMsg.Subject;
for (int i = 0; i < allTickets.Count; i++)
{
label2.Text = oMsg.Subject;
string ticketID = allTickets[i].ToString();
if (s.Contains(ticketID) && oMsg.UnRead)
{
unreadTickets.Add(ticketID);
}
}
}
oMsg = (Microsoft.Office.Interop.Outlook.MailItem)oItems.GetPrevious();
counter++;
button2.Text = counter + "";
}
//Error handler.
catch (Exception x)
{
//MessageBox.Show(x.ToString());
}
}
}
the catch exception gives me this:
unable to cast com object of type 'system.__comobject' to interface type 'microsoft.office.interop.outlook
and afterward i am getting this error:
nullreferenceexception object reference not set to an instance of an object
Keep in mind that Inbox can contain items other than MailItem, such as ReportItem or MeetingItem. That would explain the cast error. You need to treat items as a generic object and read the Class property (exposed by all OOM objects) using reflection - if it is 43 (olMailItem), you can cast it to MailItem. Or you can use the "as" operator and check for null.
Also keep in mind that the Items collection is not sorted in any particular order unless you first call Items.Sort and specify a sort order; otheriwise what you get from Items.GetLast is undetermined.
When I Use OutlookSpy to get the EntryIDs from MailItems in a particular folder and supply them to the following code:
Outlook.Application myApp = new Outlook.ApplicationClass();
Outlook.NameSpace mapiNameSpace = myApp.GetNamespace("MAPI");
try
{
object obj = mapiNameSpace.GetItemFromID(sEntryID);
if (obj is Outlook.MailItem)
{
var getItem = (Outlook.MailItem)mapiNameSpace.GetItemFromID(sEntryID);
getItem.Display();
}
}
catch (Exception Ex)
{
Global.Common.LogError("Error accessing MailItem", Ex, "EntryID " + sEntryID + " not found in " + sFolder, "Warning");
}
I get unknown messaging errors for some EntryID values and successful display of the messages in Outlook with others. Can anyone suggest what attributes the MailItems might have which will affect whether I can display them successfully using GetItemFromID or any other method of displaying all messages by EntryID reliably?
Was the message store where the message resides touched in the active Outlook session? The way MAPI providers work, when a provider is loaded by MAPI, it registers the set of entry id guids (bytes 5-20 in the entry id) that it will handle. If the particular PST store has not been touched in the current session, MAPI does not know anything about its entry ids.
You can either access all the stores in the current session first (to make sure MAPI knows about their entry ids) or use the store entry id (second parameter, optional) when calling GetItemFromId - this way Outlook will open the store first, then ask the store to open the item. You can also call Namespace.AddStore / AddStoreEx to load the given PST file if it is not already in the current profile.
You might also want to log the exception details (Ex.Message) in your exception handler.
Thanks to all the respondents - this explains why sometimes particular messages would open and sometimes they would not. By getting the StoreId using the following code:
Outlook.Application myApp = new Outlook.ApplicationClass();
Outlook.NameSpace mapiNameSpace = myApp.GetNamespace("MAPI");
Object oStoreID = Common.GetFolder(myApp, sFolder).StoreID;
try
{
object obj = mapiNameSpace.GetItemFromID(sEntryID,oStoreID);
if (obj is Outlook.MailItem)
{
Outlook.MailItem getItem = (Outlook.MailItem)mapiNameSpace.GetItemFromID(sEntryID,oStoreID);
getItem.Display();
}
}
Where
public static Outlook.Folder GetFolder(Outlook.Application App, string folderPath)
{
Outlook.Folder folder;
string backslash = #"\";
try
{
if (folderPath.StartsWith(#"\\"))
{
folderPath = folderPath.Remove(0, 2);
}
String[] folders =
folderPath.Split(backslash.ToCharArray());
folder =
App.Session.Folders[folders[0]]
as Outlook.Folder;
if (folder != null)
{
for (int i = 1; i <= folders.GetUpperBound(0); i++)
{
Outlook.Folders subFolders = folder.Folders;
folder = subFolders[folders[i]]
as Outlook.Folder;
if (folder == null)
{
return null;
}
}
}
return folder;
}
catch { return null; }
}
All MailItems now display in Outlook.
I'll just throw this in here for posterity-- Outlook 2002 requires that the entry ID supplied to GetItemFromID use upper-case hex characters.
I have an Outlook add-in that is working successfully for about 100 users. It interacts with our application and creates/updates Tasks and Appointment items. However, one client cannot get it to work for anyone who is not a network administrator. Through some logging, I can see that it works fine until it gets to the part where it should save the Task, and then it throws the infamous "The operation failed." error. I have tried in vain to get more error details by investigating inner exceptions and such, but "The operation failed." is all I can seem to get out of it.
To make it simpler to debug, I eliminated our application from the picture and wrote them a test add-in that just creates 9 tasks (hard-coded to "Task 1", "Task 2", etc.) and it fails in the same place with the same error. I have asked them to write a macro to see if a macro is able to create tasks for these users. I am awaiting their reply on that.
Anyway, I have been on MSDN for over a month trying to get help on the issue and nobody has been able to help. I am hoping someone here could shed some light. If you can provide any insight on what might be happening or suggestions to help me track down the problem, it would be greatly appreciated!
This is the function I am using to create the sample tasks in the test add-in:
private void CreateTasks()
{
int i = 1;
Outlook.TaskItem t = null;
Outlook.UserProperties ups = null;
Outlook.UserProperty up = null;
string name = string.Empty;
while (i < 10)
{
name = string.Format("Task {0}", i.ToString());
t = Application.CreateItem(Outlook.OlItemType.olTaskItem);
t.Subject = name;
t.Status = Outlook.OlTaskStatus.olTaskNotStarted;
t.Body = string.Format("Task {0} description", i.ToString());
t.Categories = "Test Task";
t.Importance = Outlook.OlImportance.olImportanceNormal;
t.ActualWork = 0;
t.TotalWork = 5 * 60;
//mimic dates that might come in main add-in
DateTime st = Convert.ToDateTime("12/10/2013");
st = st.AddDays(i);
t.StartDate = st;
DateTime end = Convert.ToDateTime("01/02/2014");
end = end.AddDays(i);
string EP = end.ToShortDateString() + " 5:00:00 PM";
end = Convert.ToDateTime(EP);
t.DueDate = end;
//mimic how we keep track of our items in the main add-in
ups = t.UserProperties;
up = ups.Find("ID");
if (up == null)
{
up = ups.Add("ID", Outlook.OlUserPropertyType.olText);
}
up.Value = string.Format("ID {0}", i.ToString());
//This is where the "The Operation Failed." error occurs on every single task.
try
{
((Outlook._TaskItem)t).Save();
}
catch (Exception ex)
{
//logs message to file
}
//Release objects
if (up != null) Marshal.ReleaseComObject(up);
if (t != null) Marshal.ReleaseComObject(t);
i++;
}
GC.Collect();
}
According to MSDN:
Saves the Microsoft Outlook item to the current folder or,
if this is a new item, to the Outlook default folder for the item type.
Does the "non administrator user" have access to this folder?
I'd hedge a bet it is something to do with Access permissions. This would be the first place to start.
Is this in an Exchange mailbox? I've seen this error before when connection to the server becomes spotty. If it is, see if the error occurs if you switch from cached mode to online mode. I don't think it's a code issue.
I am writing a console application that searches through the current users outlook inbox. The program performs certain actions on each email based on the subject line and then moves the email to 1 of 3 subfolders. So far it has been running without throwing any errors. The problem is that it will skip over some of the emails in the inbox and I can't seem to find any logic behind why it does this. Sometimes it will skip the first email received, sometimes the last, other times it will leave 2 and move the rest. I've inserted a breakpoint in my code and stepped through each line and the skipped emails never show up. It doesn't seem to matter whether the emails are marked read or unread either. If I run the program multiple times then eventually the skipped emails are processed. What could be causing this?
Microsoft.Office.Interop.Outlook.Application application = null;
if (Process.GetProcessesByName("OUTLOOK").Count() > 0)
{
application = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
}
else
{
//outlook not open, do nothing
}
Microsoft.Office.Interop.Outlook.Items items = null;
Microsoft.Office.Interop.Outlook.NameSpace ns = null;
Microsoft.Office.Interop.Outlook.MAPIFolder inbox = null;
Microsoft.Office.Interop.Outlook.Application();
ns = application.Session;
inbox = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
items = inbox.Items;
try
{
foreach (Object receivedMail in items)
{
if (receivedMail is MailItem)
{
MailItem mail = receivedMail as MailItem;
string subject = mail.Subject.ToString().ToUpper();
switch (subject)
{
case "SUBJECT1":
DoStuff1(mail);
mail.Move(inbox.Folders["folder1"]);
break;
case "SUBJECT2":
DoStuff2(mail);
mail.Move(inbox.Folders["folder2"]);
break;
default:
mail.Move(inbox.Folders["folder3"]);
break;
}
}
}
Console.WriteLine("Complete");
Console.ReadLine();
}
catch (System.Runtime.InteropServices.COMException ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
If anyone happens to stumble across this I discovered a solution. I just added each email to a list first before performing any actions on them. I'm not sure why this step was necessary, maybe something to do with not being able to properly enumerate active inbox mail items.
List<MailItem> ReceivedEmail = new List<MailItem>();
foreach (Outlook.MailItem mail in emailFolder.Items)
{
ReceivedEmail.Add(mail);
}
foreach (MailItem mail in ReceivedEmail)
{
//do stuff
}