Outlook 2007 ignores programmatically-set SaveSentMessageFolder property

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • infomage27
    New Member
    • Dec 2009
    • 2

    Outlook 2007 ignores programmatically-set SaveSentMessageFolder property

    I have tried scripting this with rules and VBA, now trying with C# but still failing. (code is exploratory, so please excuse the terribleness)

    maybe someone here will show me the error of my code...
    • sending an email by hand - everything works as expected (rules, VBA, etc are all applied).
    • sending an email from code (C#, external VBA, etc) has the messages saving in Sent Items regardless of any other settings or configurations (up to and including the scenario where the "save sent items" option is disabled in outlook preferences)
    • releasing the COM object before setting a new value doesn't help.
    • as you can see from the printouts below (last step4), the property is set correctly, but all emails sent by this code are actually saved in "\\Mailbox - Username\Sent Items" (default), not "\\Personal Folders\Sent Mail - Local" (where they SHOULD be getting saved)
    • one caveat - the dev machine only has Outlook 2003 (office 11) interop files, and the machine with outlook has Outlook 2007 (office 12) - this setup unfortunately cannot be easily modified, and alternatives to develop/test on are unavailable, but I don't *think* the mismatch should be causing this particular problem...


    Code:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Outlook = Microsoft.Office.Interop.Outlook;
    
    namespace Emailer
    {
        class Emailer
        {
            static void Main(string[] args)
            {
                // folderName is stored in the app.config, value is set to "Sent Mail - Local"
                string folderName = System.Configuration.ConfigurationSettings.AppSettings["folderName"].ToString();
    
                Console.WriteLine(folderName);
                 Outlook.Application oApp = null;
                 Outlook.NameSpace oNameSpace = null;
                 Outlook.MAPIFolder oOutboxFolder = null;
                 Console.WriteLine("step1");
                oApp = new Outlook.Application();
                oNameSpace= oApp.GetNamespace("MAPI");
                oNameSpace.Logon(null,null,true,true);
                Console.WriteLine("step2");
                foreach (Outlook.MAPIFolder parent in oNameSpace.Folders)
                { // go through each of the parent folders - Mailbox, Personal Folders, etc
                    foreach (Outlook.MAPIFolder f in parent.Folders)
                    { // go through the folders which contain 
                        Console.WriteLine("step2: checking " + f.Name);
                        if (f.Name.Contains(folderName))
                            oOutboxFolder = f;
                    }
                }
                if (oOutboxFolder == null)
                {
                    Console.WriteLine("error: no folder found");
                    return;
                }
                Console.WriteLine("step3:" + oOutboxFolder.Name);
                //oOutboxFolder = oNameSpace.Folders[folderName]; //oNameSpace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderOutbox);
    
                Object o = oApp.CreateItem(Outlook.OlItemType.olMailItem);
    
                
                Outlook.MailItem mi = (Outlook.MailItem)o;
                Console.WriteLine("step4 setting outbox");
    
                Outlook.MAPIFolder tempFolder = mi.SaveSentMessageFolder;
                System.Runtime.InteropServices.Marshal.ReleaseComObject(tempFolder);
                Console.WriteLine("step4 releasing com");
    
                mi.SaveSentMessageFolder = oOutboxFolder;
                Console.WriteLine("step4 set to " + mi.SaveSentMessageFolder.FullFolderPath); // prints correct values!
                mi.To = "validemail@validdomain.com";
                mi.Body = "Some Message";
                mi.Subject = "Some Title";
                Console.WriteLine("step5");
                mi.Save(); 
                Console.WriteLine("step6");
                mi.Send(); 
            }
        }
    }


    output (slightly clipped)

    Sent Mail - Local
    step1
    step2
    step2: checking Deleted Items
    step2: checking Inbox
    ...
    step2: checking Sent Mail - Local
    step3:Sent Mail - Local
    step4 setting outbox
    step4 releasing com
    step4 set to \\Personal Folders\Sent Mail - Local
    step5
    step6
    Last edited by tlhintoq; Dec 29 '09, 05:48 PM. Reason: [CODE] ...Your code goes between code tags [/CODE]
  • tlhintoq
    Recognized Expert Specialist
    • Mar 2008
    • 3532

    #2
    TIP: When you are writing your question, there is a button on the tool bar that wraps the [code] tags around your copy/pasted code. It helps a bunch. Its the button with a '#' on it. More on tags. They're cool. Check'em out.

    Comment

    • infomage27
      New Member
      • Dec 2009
      • 2

      #3
      aha!
      so apparently the SaveSentMessage Folder property doesn't actually do anything, which was the source of my problems.


      but adding a handler on the sent mail folder does the trick - functioning (and ever-so-slightly cleaned up) code included below.


      note that "mi" must remain in scope, otherwise it will get garbage collected and the following will become invalidated (and your handler will stop working eventually)
      mi.SaveSentMess ageFolder.Items .ItemAdd += new Outlook.ItemsEv ents_ItemAddEve ntHandler(Items _ItemAdd);

      full code:

      Code:
      using System;
      using System.Collections.Generic;
      using System.Text;
      using Outlook = Microsoft.Office.Interop.Outlook;
      
      namespace Emailer
      {
      
          class Emailer
          {
              Outlook.Application oApp = null;
              Outlook.NameSpace oNameSpace = null;
              Outlook.MAPIFolder oOutboxFolder = null;
      
      // handler - moves the item to a new folder
              private void Items_ItemAdd(object item)
              {
                  Outlook.MailItem mailItem = (Outlook.MailItem)item;
                  Console.WriteLine("captured event! moving mail for "  +mailItem.To + " to "  + oOutboxFolder.FullFolderPath);
                  mailItem.Move(oOutboxFolder);
                  mailItem.Save();
              }
      
      // main
              private void runme()
              {
                   string folderName = System.Configuration.ConfigurationSettings.AppSettings["folderName"].ToString();
      
                   Console.WriteLine(folderName);
                  oApp = new Outlook.Application();
                  oNameSpace= oApp.GetNamespace("MAPI");
                  oNameSpace.Logon(null,null,true,true);
      
      // find the folder by name - remove/replace this code if you use it in your own project!
                  foreach (Outlook.MAPIFolder parent in oNameSpace.Folders)
                  {
                      foreach (Outlook.MAPIFolder f in parent.Folders)
                      {
                          if (f.Name.Contains(folderName))
                              oOutboxFolder = f;
                      }
                  }
      
                  if (oOutboxFolder == null)
                  {
                      Console.WriteLine("error: no folder found, verify that config file setting (key=folderName) matches the outlook folder name (case sensitive). Press any key.");
                      Console.ReadKey();
                      return;
                  }
      
                  Console.WriteLine("Folder: " + oOutboxFolder.FullFolderPath);
      
      // can probably skip this step completely, but it works
                  Object o = oApp.CreateItem(Outlook.OlItemType.olMailItem);
                  Outlook.MailItem mi = (Outlook.MailItem)o;
      
                  Console.WriteLine("Launching handler...");
                  mi.SaveSentMessageFolder.Items.ItemAdd += new Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
      
      
      // handler will stay active as long as the app keeps running, so pause it.
                  Console.WriteLine("Capturing messages... [Enter] or [CTRL]+C to quit");
                  Console.ReadLine();
              }
      
      
              static void Main(string[] args)
              {
                  Emailer e = new Emailer();
                  e.runme();
              }
          }
      }

      Comment

      • Micho2008
        New Member
        • Jan 2010
        • 5

        #4
        I think you should not call the Save method, because this method saves the mail item in the current folder or in the default folder, which might suppress calling the SaveSentMessage Folder. (If you want to save the email to your disk, use the SaveAs method instead of Save.)

        I have created a function called SendEMail which can be used to send an email through Office Outlook 2007 (and I think it works with prior versions too). The function is reusable; just copy the code below into your code.


        Code:
            Public Function SendEmail( _
            ByVal _SenderEmail As String, _
            ByVal _ReceiverEmail As String, _
            ByVal _Subject As String, _
            ByVal _Body As String, _
            ByVal _Files() As String, _
            ByVal _BCC As String, _
            Optional ByVal _SaveInOlFld As Outlook.MAPIFolder = Nothing, _
            Optional ByVal _SaveToDisk As String = Nothing) _
            As Outlook.MailItem
        
                Dim o As New Outlook.Application
                Dim nms As Outlook.NameSpace = o.GetNamespace("MAPI")
                Dim vMail As Outlook.MailItem = _
                                     o.CreateItem(Outlook.OlItemType.olMailItem)
        
                Try
                    ' Specify the sender address:
                    If _SenderEmail <> "" Then
                        vMail.SendUsingAccount = GetAccount(_SenderEmail)
                    Else
                        ' Use the default account.  In this case, you may or may not
                        ' use the following statement:
                        ' vMail.SendUsingAccount = GetAccount(nms.CurrentUser.Address)
                    End If
        
                    ' Specify the receiver, subject, body, attachment, and BCC:
                    vMail.To = _ReceiverEmail
                    vMail.Subject = _Subject
                    vMail.Body = _Body
                    If _Files IsNot Nothing Then vMail = AttachFiles(vMail, _Files)
                    vMail.BCC = _BCC
        
                    ' Specify the folder in which the email would be saved after being sent:
                    If _SaveInOlFld IsNot Nothing Then _
                        vMail.SaveSentMessageFolder = _SaveInOlFld
        
                    ' Save the email to disk:
                   If _SaveToDisk IsNot Nothing Then _
                        .SaveAs(_SaveToDisk, Outlook.OlSaveAsType.olRTF)
                    
                    ' Send the email:
                    vMail.Send()
                    
                    Return vMail
                Catch ex As Exception
                    MsgBox(ex.Message)
                   Return Nothing
                End Try
            End Function
        
        
        
            Public Function AttachFiles(ByVal vMail As Outlook.MailItem, ByVal vFiles() As String) _
            As Outlook.MailItem
        
                Try
                    For j As Integer = 0 To vFiles.GetUpperBound(0)
                        vMail.Attachments.Add(vFiles(j))
                    Next
                    Return vMail
                Catch ex As Exception
                    Return vMail
                End Try
            End Function
        
        
        
            Public Function GetAccount(ByVal _Email As String) As Outlook.Account
        
                Dim o As New Outlook.Application
                Dim vAccs As Outlook.Accounts = o.Session.Accounts
                Dim vAcc As Outlook.Account
        
                Try
                    For Each vAcc In vAccs
                        If vAcc.SmtpAddress = _Email Then
                            Return vAcc
                        End If
                    Next
                    Return Nothing
                Catch ex As Exception
                    Return Nothing
                End Try
            End Function

        Comment

        • Micho2008
          New Member
          • Jan 2010
          • 5

          #5
          Here's how you can call the SendEmail method:


          Dim vMail As Outlook.MailIte m = SendEmail("mich o@gmail.com", _
          "joe@hotmail.co m", "hi", "hi friend.", Nothing, "", _
          vOutlookFolder, "C:\Email.r tf")

          If vMail IsNot Nothing Then MsgBox("Email is successfully sent")

          NB: To skip an optional argument, just place a comma. To skip the last optional argument just skip it without doing anything.
          To skip a non-optional string argument, set it to Nothing (or null in C#).

          The vOutlookFolder argument denotes the folder in Outlook into which the sent email shoudl be saved. vOutlookFolder must be of type Outlook.MapiFol der.

          I hope this helps although my reply is more than one month later.

          Comment

          Working...