Link to home
Start Free TrialLog in
Avatar of Tom Knowlton
Tom KnowltonFlag for United States of America

asked on

Outlook automation in C#

I need a jumpstart getting Outlook automation working under C#:

Here are the requirements:

1)  I need to be able to navigate to a specific folder in Outlook
2)  Once I am in this folder, I need to programmatically loop through the e-mails in the folder.
3)  Inside the loop, I need to open each e-mail and read the message body into a C# string variable.
4)  Then I want to parse the string variable (containing the e-mail message body) and look for certain phrases.

#4 I can do, but 1 thru 3 I'd like some sample code.

Please only respond if you are pretty experienced in Outlook Automation and can provide working code in C# only.

My Sincere Thanks,


Tom
Avatar of RomanPetrenko
RomanPetrenko

Take a look here:

.NET Connector for Microsoft Outlook
http://www.codeproject.com/csharp/OutlookConnector.asp

Here you can find good list of How-To's about programming outlook in VS.NET
http://www.microeye.com/resources/res_tech_vsnet.htm

Later I'll give you sample for Outlook 2003(Only this version I have), But I think you can get  some information from links I posted above...
Avatar of Tom Knowlton

ASKER

I've been to both sites and they are not *quite* what I am looking for.

For example, they always seem to navigate to default folders that Outlook provides, using constants.

I need to know how to navigate to a folder that Outlook does not provide.....one that I created myself.
Roman:

I look forward to seeing your sample code.

Here is what I have so far, plus pseudocode describing what I want to accomplish:

private void ProcessFailures()
            {

                  Outlook.Application oApp;
                  Outlook._NameSpace oNameSpace;
                  Outlook.MAPIFolder oFolder;

                  oApp = new Outlook.ApplicationClass();
                  oNameSpace = oApp.GetNamespace("MAPI");
                  oNameSpace.Logon(null,null,true,true);

                  oFolder.MoveTo("FaxSuccess");  //navigate to custom folder??????

//loop through folder contents
//assign message body to a C# string variable
//parse string variable looking for certain tokens






                  //objOutlook.Explorers objOutlookExplorer = objOutlook.Explorers.Add(objOutlook.ActiveExplorer
                  
            }
Inside OnStart just before call to ProcessFailures
Inside ProcessFailures beginning of Try
Exception thrown inside function ProcessFailures.  Message:  Could not complete the operation because the service provider does not support it.::StackTrace:     at Outlook.NameSpaceClass.get_Folders()
   at WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress.ProcessFailures() in n:\clientdevelopment\neo\buyersfund\windowsservicetrackrequestprogress\servicetrackrequestprogress.cs:line 101
etrackrequestprogress\servicetrackrequestprogress.cs:line 99

How can it not be supported?
Here is the sample:

private Outlook.MAPIFolder GotoFolder(Outlook.MAPIFolder fldr, string FolderName)
{
if (fldr == null ) return null;
if (fldr.Name == FolderName) return fldr;
foreach( Outlook.MAPIFolder folder in fldr.Folders)
{
      if (folder.DefaultItemType != Outlook.OlItemType.olMailItem)
            continue;
      if (folder.Name == FolderName)
            return folder;
      Outlook.MAPIFolder f = GotoFolder(folder,FolderName);
      if (f != null)
            return f;
}
return null;
}

private void button1_Click(object sender, System.EventArgs e)
{
string FolderName = "Anekdots";//<=== Folder name you want to open.
oApp = new Outlook.ApplicationClass();
oNamespace = oApp.GetNamespace("MAPI");
oNamespace.Logon(null,null,true,true);
Outlook.MAPIFolder fldr = GotoFolder(oNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) ,FolderName);<-- if you have more than one account,get corresponding folder from oNamespace.Folders collection
if (fldr == null)
      MessageBox.Show("Folder Not Found");
if (fldr.DefaultItemType != Outlook.OlItemType.olMailItem )
      MessageBox.Show("Can't work with non-Mail Items");
foreach(Outlook.MailItem mitem in fldr.Items)
{
      string forParsing = mitem.Body; <<=== Here if you running this code from external application you'll get outlook warning "Another program tying to access ... bla-bla-bla... Allow access for 1(2)(5) minute(s)?". You will not recieve this message if you are running in Outlook addin or outlook custom form code.
      //<write here parsing code>
}
}
Roman:

Getting a bunch of errors with your sample code.  Can you please help me debug?

http://www.knowltonfamily.com/a.gif


Here are the errors:

N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(126): foreach statement cannot operate on variables of type 'Outlook.Items' because 'Outlook.Items' does not contain a definition for 'GetEnumerator', or it is inaccessible
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(93): foreach statement cannot operate on variables of type 'Outlook.Folders' because 'Outlook.Folders' does not contain a definition for 'GetEnumerator', or it is inaccessible
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(110): The name 'oApp' does not exist in the class or namespace 'WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress'
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(111): The name 'oNamespace' does not exist in the class or namespace 'WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress'
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(112): The type or namespace name 'oNamespace' could not be found (are you missing a using directive or an assembly reference?)
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(114): The type or namespace name 'oNamespace' could not be found (are you missing a using directive or an assembly reference?)
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(117): The type or namespace name 'MessageBox' could not be found (are you missing a using directive or an assembly reference?)
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(122): The type or namespace name 'MessageBox' could not be found (are you missing a using directive or an assembly reference?)
Sorry, the screenshot is:

http://www.knowltonfamily.com/b.gif
UPDATE:

Roman, I have taken the errors down to just 2 errors:


http://www.knowltonfamily.com/c.gif


Some problem with the foreach statement:

N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(93): foreach statement cannot operate on variables of type 'Outlook.Folders' because 'Outlook.Folders' does not contain a definition for 'GetEnumerator', or it is inaccessible
N:\ClientDevelopment\NEO\BuyersFund\WindowsServiceTrackRequestProgress\ServiceTrackRequestProgress.cs(130): foreach statement cannot operate on variables of type 'Outlook.Items' because 'Outlook.Items' does not contain a definition for 'GetEnumerator', or it is inaccessible




Here is my updated source code for the Windows Service:


using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;

namespace WindowsServiceTrackRequestProgress
{
      public class ServiceTrackRequestProgress : System.ServiceProcess.ServiceBase
      {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;
            private System.Timers.Timer timerCheckFaxStatus;

            public BuyersFund.GatherMessages.GatherMessages gmMessage = new BuyersFund.GatherMessages.GatherMessages();

            public ServiceTrackRequestProgress()
            {
                  // This call is required by the Windows.Forms Component Designer.
                  InitializeComponent();

                  // TODO: Add any initialization after the InitComponent call
            }

            // The main entry point for the process
            static void Main()
            {
                  System.ServiceProcess.ServiceBase[] ServicesToRun;
      
                  // More than one user Service may run within the same process. To add
                  // another service to this process, change the following line to
                  // create a second service object. For example,
                  //
                  //   ServicesToRun = New System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
                  //
                  ServicesToRun = new System.ServiceProcess.ServiceBase[] { new ServiceTrackRequestProgress() };

                  System.ServiceProcess.ServiceBase.Run(ServicesToRun);
            }

            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.timerCheckFaxStatus = new System.Timers.Timer();
                  ((System.ComponentModel.ISupportInitialize)(this.timerCheckFaxStatus)).BeginInit();
                  //
                  // timerCheckFaxStatus
                  //
                  this.timerCheckFaxStatus.Interval = 5000;
                  this.timerCheckFaxStatus.Elapsed += new System.Timers.ElapsedEventHandler(this.timerCheckFaxStatus_Elapsed);
                  //
                  // ServiceTrackRequestProgress
                  //
                  this.ServiceName = "Service1";
                  ((System.ComponentModel.ISupportInitialize)(this.timerCheckFaxStatus)).EndInit();

            }

            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if (components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }

            /// <summary>
            /// Set things in motion so your service can do its work.
            /// </summary>
            protected override void OnStart(string[] args)
            {
                  // TODO: Add code here to start your service.
                  this.timerCheckFaxStatus.Enabled=true;
            }

            private Outlook.MAPIFolder GotoFolder(Outlook.MAPIFolder fldr, string FolderName)
            {
                  if (fldr == null ) return null;
                  if (fldr.Name == FolderName) return fldr;
                  foreach(Outlook.MAPIFolder folder in fldr.Folders)
                  {
                        if (folder.DefaultItemType != Outlook.OlItemType.olMailItem)
                              continue;
                        if (folder.Name == FolderName)
                              return folder;
                        Outlook.MAPIFolder f = GotoFolder(folder,FolderName);
                        if (f != null)
                              return f;
                  }
                  return null;
            }


            private void ProcessFailures()
            {
                  string FolderName = "Anekdots";//<=== Folder name you want to open.

                  Outlook.Application oApp;
                  Outlook.NameSpace oNamespace;

                  oApp = new Outlook.ApplicationClass();
                  oNamespace = oApp.GetNamespace("MAPI");
                  oNamespace.Logon(null,null,true,true);
                  //if you have more than one account,get corresponding folder from oNamespace.Folders collection
                  Outlook.MAPIFolder fldr = GotoFolder(oNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) ,FolderName);
                  if (fldr == null)
                  {
                        gmMessage.AddMessage("IT","Folder Not Found");
                  }

                  if (fldr.DefaultItemType != Outlook.OlItemType.olMailItem )
                  {                        
                        gmMessage.AddMessage("IT","Can't work with non-Mail Items");
                  }


                  foreach(Outlook.MailItem mitem in fldr.Items)
                  {
                        //Here if you running this code from external application you'll get outlook warning "Another program tying to access ... bla-bla-bla... Allow access for 1(2)(5) minute(s)?". You will not recieve this message if you are running in Outlook addin or outlook custom form code.
                        string forParsing = mitem.Body;
                        //<write here parsing code>
                  }
            }

            private void ProcessSuccesses()
            {
            }

            /// <summary>
            /// Stop this service.
            /// </summary>
            protected override void OnStop()
            {
                  // TODO: Add code here to perform any tear-down necessary to stop your service.
            }

            private void timerCheckFaxStatus_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {                  
                  try
                  {
                        gmMessage.AddMessage("IT","Inside OnStart just before call to ProcessFailures");
                        ProcessFailures();
                        gmMessage.AddMessage("IT","Inside OnStart just after call to ProcessFailures");
                  }
                  catch(Exception ee)
                  {
                        gmMessage.AddMessage("IT","Exception thrown inside function OnStart.  Message:  " + ee.Message + "::StackTrace:  " + ee.StackTrace);
                        gmMessage.WriteMessagesOutToFile("IT","C:\\faxprogressFAILURE.txt");
                  }            
            }
      }
}

I looked at your answer.

It is looking like I am using the wrong code....based on the other posts you have made in my other questions.

Like I said in my other post, I am using Outlook 2000 and VS .NET 2002.
As I said in my first post I gave you sample for outlook 2003, but you can work with it
just change foreach statements:
for(Outlook.MAPIFolder folder = fldr.Folders.GetFirst();folder != null; folder = fldr.Folders.GetNext())
instead of
foreach(Outlook.MAPIFolder folder in fldr.Folders) in GotoFolders
and
for(Outlook.MailItem mitem = fldr.Items.GetFirst();mitem != null; mitem = fldr.Items.GetNext())
instead of
foreach(Outlook.MailItem mitem in fldr.Items)

that's all,

PS: Sorry, I can't check this code, I'm already at home. Try to check this code I'll be waiting for your answers.
Roman:

You code does not work under VS .NET 2002.....even after converting the foreach statements over to just for loops.
What errors you get?
Now I am getting:

Inside OnStart just before call to ProcessFailures
Exception thrown inside function OnStart.  Message:  Could not complete the operation because the service provider does not support it.::StackTrace:     at Outlook.NameSpaceClass.GetDefaultFolder(OlDefaultFolders FolderType)
   at WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress.ProcessFailures() in n:\clientdevelopment\neo\buyersfund\windowsservicetrackrequestprogress\servicetrackrequestprogress.cs:line 120
   at WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress.timerCheckFaxStatus_Elapsed(Object sender, ElapsedEventArgs e) in n:\clientdevelopment\neo\buyersfund\windowsservicetrackrequestprogress\servicetrackrequestprogress.cs:line 166
ckrequestprogress.cs:line 166


Here is line 120:

     Outlook.MAPIFolder fldr = GotoFolder(oNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox) ,FolderName);


Here is line 166:

      ProcessFailures();
and what mail server you connected to?
Exchange with an Outlook client.
1. try to install .Net ver 1.1 from here:
http://www.microsoft.com/downloads/details.aspx?FamilyId=262D25E3-F589-4842-8157-034D1E7CF3A3&displaylang=en
2. Try to check if you're logged in correctly.(May be open Outlook and after that start your program)
What results do you got?
Take a look here:
http://support.microsoft.com/?kbid=329554
It's exactly your problem, am I right? You have to install hotfix...
The error message is very close to what I have been getting in VS .NET.......but the cause of the error message seems specific to another problem???????
If you read carefully the solution page says:

RESOLUTION
A supported fix is now available from Microsoft, but it is only intended to correct the problem that is described in this article. Apply it only to computers that are experiencing this specific problem.


My question is....is the article describing my problem?
But you use API, that they use for reply... so they will not write "When you access Folders property..." they gives sample that every one can understand...
Okay.  :)
I think this is exactly your problem, try to install hofix.
BTW: They wrote such resolution for each Hotfix they posts, it's politics. And also they include the hotfix into future SP so from this time it's for everyone, strange ?! :)
Roman:  Good point.  If they are putting the fix in the SP then it is most likely "Okay" for me to apply the hotfix.

Tom
What is the easiest way to determine what SP my Outlook 2000 is using right now?
But read carefully this article, you should have SP3 installed befor you install hotfix. It's post-SP3.
I am looking for SP3  (I assume it includes   SP1   and SP2    as well?  )
run Office update:
http://office.microsoft.com/OfficeUpdate/default.aspx it will tell you what you need to install
Roman:

I just finished installing all of the pending Windows Crititcal Updates.

My code compiles and builds the .EXE just fine, but when I run the Windows Service I get an error file that says:

Inside OnStart just before call to ProcessFailures
Exception thrown inside function OnStart.  Message:  Could not complete the operation because the service provider does not support it.::StackTrace:     at Outlook.NameSpaceClass.GetDefaultFolder(OlDefaultFolders FolderType)
   at WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress.ProcessFailures() in n:\clientdevelopment\neo\buyersfund\windowsservicetrackrequestprogress\servicetrackrequestprogress.cs:line 121
   at WindowsServiceTrackRequestProgress.ServiceTrackRequestProgress.timerCheckFaxStatus_Elapsed(Object sender, ElapsedEventArgs e) in n:\clientdevelopment\neo\buyersfund\windowsservicetrackrequestprogress\servicetrackrequestprogress.cs:line 168
ckrequestprogress.cs:line 166



I think you have problem with Window Service security, What account do you use to run the service?
If you run this code not as service but as windows application - it works?
ASKER CERTIFIED SOLUTION
Avatar of RomanPetrenko
RomanPetrenko

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Roman:

Agreed.....does not look like Outlook automation would work in a Windows Service.

BUT

What about Exchange Event Sinks?
I think they should work, It's COM+, but I've never used them...
UPDATE:

Well, guess what?

I finally had a major breakthrough on this @!#$^% problem that has been driving me crazy for the last few weeks!!!!!!!


It is a very small change.

Apparently the method   MoveFirst(   ) does not work so well with Exchange.

This was the cause of my COMException:   "Interface not registered" error message this entire time.


The solution is to use    Move(1,0)     instead of MoveFirst(  ).


That's it.   Simple.

This answer came courtesy of Tom Rizzo, an author who was kind enough to correspond with me and help me figure-out finally what the problem was.:

Looking for a good book on programming Exchange, Outlook, ADSI and
SharePoint?  Check out http://www.microsoft.com/MSPress/books/5517.asp

My I also reommend:

.NET and COM: The Complete Interoperability Guide  ISBN:067232170X
http://www.amazon.ca/exec/obidos/ASIN/067232170X/componentsnot-20/701-9386310-4703553
More on the MoveFirst problem I described:

http://support.microsoft.com/default.aspx?scid=kb;en-us;273791


If you are getting into Event Sinks....I recommend the following tutorial:

http://www.codeproject.com/csharp/CsManagedEventSinksHooks.asp

Make sure and read the article questions and follow-ups at the bottom!

There are a few caveats.  E-mail me at my Profile e-mail address and we can talk about them.  :)