Outlook email search crashes - c#

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.

Related

Getting header information using c#

I am trying to create an outlook add-in in C# which collects header information from new emails in the inbox. Googling it says to use the following code to get the header information for the email.
mailitem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001F/")
However when I use this the error Object reference not set to an instance of an object. Click the schema address also says the resource is no longer there is there another way of getting this or do I need to use a different language?
For reference I have added the below.
private void Quarantine()
{
Outlook.MAPIFolder inBox = (Outlook.MAPIFolder)this.Application.
ActiveExplorer().Session.GetDefaultFolder
(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Items items = (Outlook.Items)inBox.Items;
Outlook.MailItem MailItem = null;
items.Restrict("[UnRead] = true");
var destFolder = inBox.Folders["test"];
string StrRegex = #"(Final Score - [-][0-9] | Final Score - [2][0 - 1] | Final Score - [0 - 1][0-9])";
Regex Reg = new Regex(StrRegex);
foreach (object email in items)
{
MailItem = email as Outlook.MailItem;
String Header= MailItem.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001F/");
if (!(Reg.IsMatch(Header)))
{
MailItem.Move(destFolder);
}
}
}
}
as operator will return null is the object is not a MailItem. Your code never checks for that. You can have objects other than MailItem in the Inbox folder, such as ReportItem, MeetingItem, etc.

Fast searching an Outlook Address list

I want to search an huge address list from outlook for an AddressEntry by the email address. Searching by name is not problem, as you can write:
Microsoft.Office.Interop.Outlook.AddressEntry = AddressEntries[Name];
But I want to find the Entry by its email. This code is working but extremely slow:
public static string GetUserDataByEmailAddress(string EmailAddress)
{
Microsoft.Office.Interop.Outlook.Application OLApp = null;
bool OutlookWasRunning = false;
string UserName = string.Empty;
if (System.Diagnostics.Process.GetProcessesByName("OUTLOOK").Count() > 0)
{
OLApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Outlook.Application") as Microsoft.Office.Interop.Outlook.Application;
OutlookWasRunning = true;
}
else
{
OLApp = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace nameSpace = OLApp.GetNamespace("MAPI");
nameSpace.Logon("", "", System.Reflection.Missing.Value, System.Reflection.Missing.Value);
nameSpace = null;
OutlookWasRunning = false;
}
Microsoft.Office.Interop.Outlook.AddressLists GALs = null;
GALs = OLApp.Session.AddressLists;
if (GALs == null) { throw new System.Exception("ERROR: Unable to get address book collection from MS Outlook!"); }
Microsoft.Office.Interop.Outlook.AddressList gal = null;
gal = GALs["Globale Adressliste"];
if (gal == null) { throw new System.Exception("ERROR: Unable to get address book 'Global Address List' from MS Outlook!"); }
foreach (Microsoft.Office.Interop.Outlook.AddressEntry ent in gal.AddressEntries)
{
if(ent.Address == EmailAddress) { UserName = ent.Name; }
}
if (!OutlookWasRunning) { OLApp.Quit(); }
return UserName;
}
Ok, but this way works very slow. Now, I tried to use the AddressEntries as an IEnumeratable to do this:
var output = from a in gal.AddressEntries.AsQueryable() where (a as Microsoft.Office.Interop.Outlook.AddressEntry).Address == EmailAddress select a;
Doing that, I get the error:
Severity Code Description Project File Line Suppression State Error CS1936 Could not find an implementation of the query pattern for source type 'IQueryable'. 'Where' not found.
Does anybody know an fast way to search for the correct AddressEntry?
Regards,
Jan
Do not loop through all items in a container - some containers can contain tens of thousands of entries or more.
You can use Namespace.CreateRecipient / Recipient.Resolve in OOM - that will resolve a name (or address) against all containers. This is equivalent to typing a name in the To edit box in Outlook and hitting Ctrl+K.
If you want to resolve against a particular container (e.g. "All Users" GAL container), you would need to use Extended MAPI (C++ or Delphi only). You can use Redemption (I am its author - any language) - it exposes RDOSession.AddressBook.ResolveName / ResolveNameEx and RDOAddresList.ResolveName / ResolveNameEx .
Keep in mind that resolving against a particular container (PR_ANR resolution in MAPI) might not work if using an SMTP address (GAL limitation).

Microsoft Office Interop Outlook w/ C# - Why does Items.GetNext() return null?

I'm trying to fetch attachments from a particular folder in Outlook 2010 in a C# Windows Forms app. I have a class called MailInbox that contains the Outlook namespace, inbox, and message objects.
Here is the code for that class and its methods:
public class mailInbox
{
//declare needed variables for outlook office interop
public Outlook.Application oApp = new Outlook.Application();
//public Outlook.MAPIFolder oInbox;
public Outlook.MAPIFolder idFolder;
public Outlook.NameSpace oNS;
public Outlook.Items oItems;
public Outlook.MailItem oMsg;
public Outlook.MAPIFolder subFolder;
public string subFolderName;
public mailInbox()
{
//read the subfoldername from a text file
using (StreamReader subFolderNameRead = new StreamReader(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\SuperVerify\config\subfoldername.txt"))
{
subFolderName = subFolderNameRead.ReadLine().Trim();
}
oNS = oApp.GetNamespace("mapi");
//oInbox = oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
idFolder = oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Folders[subFolderName]; // use the subFolderName string, read from the config text file, to get the folder from outlook
oItems = idFolder.Items;
//oMsg = (Outlook.MailItem)oItems.GetNext();
}
public void FetchEmail() // fetches the next email in the inbox and saves the attachments to the superverify folder
{
oNS.Logon(Missing.Value, Missing.Value, false, true); //login using default profile. This might need to be changed.
oMsg = (Outlook.MailItem) oItems.GetNext(); //fetch the next mail item in the inbox
if(oMsg.Attachments.Count > 0) // make sure message contains attachments **This is where the error occurs**
{
for (int i =0; i< oMsg.Attachments.Count; i++)
{
oMsg.Attachments[i].SaveAsFile(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\SuperVerify\images\" + oMsg.Attachments[i].FileName); //save each attachment to the specified folder
}
} else //if no attachments, display error
{
MessageBox.Show("The inbox folder has no messages.", "TB ID Verification Tool", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
public void changeSubFolderName(string newSubFolderName)
{
subFolderName = newSubFolderName;
}
}
When I click the button that invokes the FetchEmail method, I get the error
System.NullReferenceException: 'Object reference not set to an instance of an object.'
oMsg was null. I thought that
public Outlook.MailItem oMsg;
instantiated the object and that
oMsg = (Outlook.MailItem) oItems.GetNext();
assigned it an Outlook MailItem, but it seems I don't understand what's going on here.
From the documentation which curiously references VB.NET's Nothing instead of null,
It returns Nothing if no next object exists, for example, if already positioned at the end of the collection.To ensure correct operation of the GetFirst, GetLast, GetNext, and GetPrevious methods in a large collection, call GetFirst before calling GetNext on that collection, and call GetLast before calling GetPrevious. To ensure that you are always making the calls on the same collection, create an explicit variable that refers to that collection before entering the loop.
So GetNext will return null if you're at the end of the folder. You'd have to check it for null before attempting to use it.
You can also use oItems.Count to determine that there are no items when you initially access the folder's items. But if Count is one or more you'd still have to check for null, because you're dealing with a collection that can change. Hypothetically someone could remove items from the folder while you're reading it, so you can't get the count up front and then rely on it.

I get MAPI_E_UNKNOWN_ENTRYID when do GetMessageFromID() in thread through Redemption

I used Redemption (http://dimastr.com/redemption/home.htm) for develop my Outlook AddIn. When I try GetMessageFromId in STA-thread(!) at first time all good, but in next times i get MAPI_E_UNKNOWN_ENTRYID.
RDOSession rdoSession = null;.
rdoSession = new RDOSession();
if (rdoSession != null)
{
if (!rdoSession.LoggedOn)
rdoSession.Logon(Consts.ProfileName);
if (rdoSession.LoggedOn)
{
for (int c = 1; c <= rdoStoresCnt; c++)
{
/* other code */
RDOMail mail = null;
try
{
mail = rdoSession.GetMessageFromID(entryID);
/* other code */
}
catch (Exception ex)
{
if (mail != null) Marshal.ReleaseComObject(mail); mail = null;
}
finally
{
if (mail != null) Marshal.ReleaseComObject(mail); mail = null;
}
}
}
}
What am I doing wrong?
MAPI_E_UNKNOWN_ENTRYID means the current MAPI session (created by calling RDOSession.Logon) does not know which MAPI provider is supposed to handle the specified entry id because (most likely) the provider has not been loaded yet in that session and did nto have a chance to register its set of entry ids with the MAPI system in the session.
You can try to specify the store entry id when calling GetMessageFromId (Redemption will open the specified store first and call IMsStore::OpenEntry instead of IMAPISession::OpenEntry), but the real solution is avoid creating a brand new MAPI session at all - since your code is inside Outlook, there is already the MAPI session used by Outlook: simply set the RDOSession.MAPIOBJECT property to Namespace.MAPIOBJECT from Outlook. Do not call RDOSession.Logoff in that case.

Error when opening some Outlook MailItems with GetItemFromID using EntryID from Outlook Spy

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.

Categories

Resources