Problem altering/saving moved MailItems in Outlook VBA

I am writing some mail filters using VBA for Outlook 2003, and I have hit what I believe is a bug in Outlook, but the workaround is not apparent to me.

When email arrives, I am processing it using my code, launched from the built-in Outlook Rules as a script. This works fine. The problem arises when I choose to run my rules on, say, another folder.

I found that any items of mail that have been flagged as complete cannot be moved, and I encounter the following error message:

"an unsent message cannot be flagged complete"

This is bizarre, because this is incoming mail, in either the Inbox itself, or a sub-folder. So, there should be no cause for this error - especially as my code is not flagging the mail item - the mail item is already flagged as complete.

To reproduce this error message, simply try programmatically moving a received email message from one folder to another. You will encounter the error for any completed items.

I performed a Google search but could not find a workaround to this problem. One way to avoid the error is to unflag the mail item, but this is obviously not the desired final state... the code I am working on currently unflags the mail item, moves it, and then attempts to reflag the item again and save it. However, I am experiencing a lot of difficulty with this, because once the mail item has been moved, it seems to disappear from the clutches of my code.

Here is an example:



Function MoveEmailItemToFolder(objEmailMessage As MailItem, strFolder As String)
    '-------------------------------------------------------------------------------
    '   Description
    '-------------------------------------------------------------------------------
    '   This function moves the supplied email message to the specified folder.
   
    On Error GoTo ErrorHandler
   
    '-------------------------------------------------------------------------------
    '   Declarations
    '-------------------------------------------------------------------------------
    Dim objFolder As MAPIFolder         ' Destination folder
    Dim strEntryID As String            ' Email id
   
    '-------------------------------------------------------------------------------
    '   Initialisation
    '-------------------------------------------------------------------------------
    Set objFolder = Outlook.Application.GetNamespace("MAPI").GetFolderFromID(colFolders.Item(strFolder))
    strEntryID = objEmailMessage.EntryID
   
    '-------------------------------------------------------------------------------
    '   Code
    '-------------------------------------------------------------------------------
    If objFolder Is Nothing Then
        MsgBox ("The specified folder (below) is invalid and could not be referenced" _
            & Chr(13) & Chr(13) & strFolder)
    Else
        Debug.Print "   ...moving"
       
        If objEmailMessage.FlagStatus = olFlagComplete Then
            Debug.Print "   ...unflagging"
            objEmailMessage.FlagStatus = olNoFlag
            Debug.Print "   ...moving"
            objEmailMessage.Move objFolder
           
            Debug.Print "   ...finding"
            objEmailMessage = Outlook.Application.GetNamespace("MAPI").GetItemFromID(strEntryID)
            Debug.Print "   ...flagging"
            objEmailMessage.FlagStatus = olFlagComplete
            Debug.Print "   ...saving"
            objEmailMessage.Save
            Debug.Print "   ...done"
        Else
            Debug.Print "   ...flag is ok"
            objEmailMessage.Move objFolder
        End If
    End If
   
    '-------------------------------------------------------------------------------
    '   Free memory and exit
    '-------------------------------------------------------------------------------
Finish:
    Set objFolder = Nothing
    Exit Function
   
    '-------------------------------------------------------------------------------
    '   Error handling
    '-------------------------------------------------------------------------------
ErrorHandler:
    If Err.Description = "Can't move the items." Then
        Debug.Print "   ...failed to move"
    Else
        Call HandleError(Err.Number, Err.Description)
    End If
    Resume Finish
   
End Function

 


Please note that the debug code is there just to highlight where the error is occurring. I thought that I would be able to track the EntryID of the MailItem and use it to find the email again after the move, but it seems that the EntryID must change due to the move. I don't know of any other reliable way to find the email again.

So, if I cannot find the email after moving, then I am stuck... obviously the reflagging of the email has to be done after moving and not before, because that's how this whole problem started - not being able to move a flagged mail item.

If anyone can offer any advice on this subject then I will be very grateful. I have found one similar posting on these forums, but the fix there was to save before moving, which obviously does not apply here.


Answer this question

Problem altering/saving moved MailItems in Outlook VBA

  • Mike Emerson

    Thanks a lot for this.  I have been banging my head against the wall over this one for a while.  Good to see good people doing good things...

  • Apolo H

    Hi,

    Please post this question in the Outlook newsgroup. The Outlook team will be able to help you there. The VSTO forum deals with VSTO managed code (VB and C#)programing for Word, Excel and Outlook.

    Here is a link to the Outlook VBA forum.
    http://msdn.microsoft.com/newsgroups/default.aspx dg=microsoft.public.office.developer.outlook.vba&lang=en&cr=US

    Thanks
    Paul Stubbs
    Program Manager
    http://blogs.msdn.com/pstubbs/< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    This posting is provided "AS IS" with no warranties, and confers no rights.



  • pavel_roy

    Finally, here's an improved version of the function. It now checks if the destination folder is the same as the current folder. A little more sensible, I think.



    Function MoveEmailItemToFolder(objEmailMessage As MailItem, strFolder As String)
        '-------------------------------------------------------------------------------
        '   Description
        '-------------------------------------------------------------------------------
        '   This function moves the supplied email message to the specified folder.
       
        On Error GoTo ErrorHandler
       
        '-------------------------------------------------------------------------------
        '   Declarations
        '-------------------------------------------------------------------------------
        Dim objFolder As MAPIFolder         ' Destination folder
       
        '-------------------------------------------------------------------------------
        '   Initialisation
        '-------------------------------------------------------------------------------
        Set objFolder = Outlook.Application.GetNamespace("MAPI").GetFolderFromID(colFolders.Item(strFolder))
       
        '-------------------------------------------------------------------------------
        '   Code
        '-------------------------------------------------------------------------------
        If objEmailMessage.Parent.EntryID = colFolders.Item(strFolder) Then
            GoTo Finish
        End If
       
        If objFolder Is Nothing Then
            MsgBox ("The specified folder (below) is invalid and could not be referenced" _
                & Chr(13) & Chr(13) & strFolder)
        Else
            Debug.Print "   ...moving"
            If objEmailMessage.FlagStatus = olFlagComplete Then
                objEmailMessage.FlagStatus = olNoFlag
                Set objEmailMessage = objEmailMessage.Move(objFolder)
                Set objEmailMessage = Outlook.Application.GetNamespace("MAPI").GetItemFromID(objEmailMessage.EntryID)
                objEmailMessage.FlagStatus = olFlagComplete
                objEmailMessage.Save
            Else
                Set objEmailMessage = objEmailMessage.Move(objFolder)
            End If
        End If
       
        '-------------------------------------------------------------------------------
        '   Free memory and exit
        '-------------------------------------------------------------------------------
    Finish:
        Set objFolder = Nothing
        Exit Function
       
        '-------------------------------------------------------------------------------
        '   Error handling
        '-------------------------------------------------------------------------------
    ErrorHandler:
        Call HandleError(Err.Number, Err.Description)
        Resume Finish
       
    End Function

     


    Enjoy.

  • RLRTech

    Hi Krycek,

    Technically this forum is for Visual Studio Tools for Office which is managed code development, there are people on this forum from MS and MVP's and other technical people like myself, who have expieriences and as such may have helped but there are more specific areas that may and are more suitable for Outlook VBA not Outlook VSTO. This forum is for Managed Code but we may have assisted if we had chance, it is important that we focus on the correct product groups to make sure it is targetted correctly.

    Ironically I have had expierience with this and could have highlighted some of these areas but only just saw the original post today Sad - Outlook has a lot of quirks and is very annoying at times in its inability to be consistent.

    Regards

  • themcfet

    Hi Paul

    Thanks for the link; I have now posted there as well.

    However, I am a little confused - you appear to be telling me that I posted in the wrong place, but I'm sure I did not. The issue that I posted about is to do with programming Outlook with VB/VBA and that is surely appropriate It doesn't matter that this instance of the code happens to be using VBA - you will get exactly the same issue when using VB.

    So, I am still looking for an answer to this issue...

    By way of update, I discovered that MailItem.Move actually returns a new instance of the MailItem object, which enables me to reference it after the move. So that's ok. However, when I try to flag the moved item as complete, I get the error message that started it all - about being unable to flag unsent items.

    So my method is now:

    - unflag
    - save
    - move
    - reflag
    - save

    ...except it fails on the reflag step. So, no matter if I try to simply move the completed item, or unflag, move, and reflag it, I am back to the same error.

    Can someone please shed some light on this for me

  • Nightlinerdev

    Sorry if there's an etiquette violation for replying to a thread this old...

    I would like to thank krycek for this gem, and also point out to others that the trick of creating an object and then reassigning it based on its EntryID works well for MailItem.Copy as well as move. When you copy a sent-mail item, for example, the new item has the current date as the received date and has the sent property = false. There may be other properties that are changed. If you immediately reassign it as above, these properties will have their original values and you can proceed with a better copy than you had originally.


  • Kmendonsa

    Ok, I finally found out the problem, no thanks to this forum or the newsgroup, either. I have been quite surprised at that.

    Anyway, here's the solution for anyone else who might have this issue.

    When the MailItem is moved, it is changed in more ways than one. It is important to reassign the object from the result of MailItem.Move, in order to preserve a means of referencing the object. This is because the EntryID changes. However, the newly moved object for some reason has the MailItem.Sent property set to false, now.

    Because this property is read-only, we initially appear to be stumped. We have a means of accessing the newly moved object, but we cannot re-flag it as complete, because it is unsent... and we cannot mark it as sent, because that property is read-only.

    However, I found that quite bizarrely, the status of MailItem.Sent does not seem to be persistant. I checked the status before and after moving, and it was always True before and False after. This led me to try a slightly different approach.

    By using the result of MailItem.Send to access the moved MailItem, we can then access the EntryID property. And here's the crux of the solution - if we then get the object again, from GetItemFromID(), then the MailItem.Sent property will be True once more!

    So, the process is:

    - unflag
    - move
    - find
    - flag
    - save

    Here is the function, working, with some debug code in to make it clearer what is happening:


    Function MoveEmailItemToFolder(objEmailMessage As MailItem, strFolder As String)
        '-------------------------------------------------------------------------------
        '   Description
        '-------------------------------------------------------------------------------
        '   This function moves the supplied email message to the specified folder.
       
        On Error GoTo ErrorHandler
       
        '-------------------------------------------------------------------------------
        '   Declarations
        '-------------------------------------------------------------------------------
        Dim objFolder As MAPIFolder         ' Destination folder
       
        '-------------------------------------------------------------------------------
        '   Initialisation
        '-------------------------------------------------------------------------------
        Set objFolder = Outlook.Application.GetNamespace("MAPI").GetFolderFromID(colFolders.Item(strFolder))
       
        '-------------------------------------------------------------------------------
        '   Code
        '-------------------------------------------------------------------------------
        If objFolder Is Nothing Then
            MsgBox ("The specified folder (below) is invalid and could not be referenced" _
                & Chr(13) & Chr(13) & strFolder)
        Else
            Debug.Print "   ...moving"
           
            If objEmailMessage.FlagStatus = olFlagComplete Then
                If objEmailMessage.Sent Then
                    Debug.Print "   ...SENT"
                Else
                    Debug.Print "   ...UNSENT"
                End If
                Debug.Print "   ...unflagging"
                objEmailMessage.FlagStatus = olNoFlag
                Debug.Print "   ...moving"
                Set objEmailMessage = objEmailMessage.Move(objFolder)
               
                If objEmailMessage.Sent Then
                    Debug.Print "   ...SENT"
                Else
                    Debug.Print "   ...UNSENT"
                End If
               
                Debug.Print "   ...finding"
                Set objEmailMessage = Outlook.Application.GetNamespace("MAPI").GetItemFromID(objEmailMessage.EntryID)
               
                If objEmailMessage.Sent Then
                    Debug.Print "   ...SENT"
                Else
                    Debug.Print "   ...UNSENT"
                End If
               
                Debug.Print "   ...flagging"
                'objEmailMessage.Sent = True
                objEmailMessage.FlagStatus = olFlagComplete
                Debug.Print "   ...saving"
                objEmailMessage.Save
                Debug.Print "   ...done"
            Else
                Debug.Print "   ...flag is ok"
                Set objEmailMessage = objEmailMessage.Move(objFolder)
            End If
        End If
       
        '-------------------------------------------------------------------------------
        '   Free memory and exit
        '-------------------------------------------------------------------------------
    Finish:
        Set objFolder = Nothing
        Exit Function
       
        '-------------------------------------------------------------------------------
        '   Error handling
        '-------------------------------------------------------------------------------
    ErrorHandler:
        If Err.Description = "Can't move the items." Then
            Debug.Print "   ...failed to move"
        Else
            Call HandleError(Err.Number, Err.Description)
        End If
        Resume Finish
       
    End Function

     


    ...and here's the proper, final function, working, without the debug code:


    Function MoveEmailItemToFolder(objEmailMessage As MailItem, strFolder As String)
        '-------------------------------------------------------------------------------
        '   Description
        '-------------------------------------------------------------------------------
        '   This function moves the supplied email message to the specified folder.
       
        On Error GoTo ErrorHandler
       
        '-------------------------------------------------------------------------------
        '   Declarations
        '-------------------------------------------------------------------------------
        Dim objFolder As MAPIFolder         ' Destination folder
       
        '-------------------------------------------------------------------------------
        '   Initialisation
        '-------------------------------------------------------------------------------
        Set objFolder = Outlook.Application.GetNamespace("MAPI").GetFolderFromID(colFolders.Item(strFolder))
       
        '-------------------------------------------------------------------------------
        '   Code
        '-------------------------------------------------------------------------------
        If objFolder Is Nothing Then
            MsgBox ("The specified folder (below) is invalid and could not be referenced" _
                & Chr(13) & Chr(13) & strFolder)
        Else
            Debug.Print "   ...moving"
            If objEmailMessage.FlagStatus = olFlagComplete Then
                objEmailMessage.FlagStatus = olNoFlag
                Set objEmailMessage = objEmailMessage.Move(objFolder)
                Set objEmailMessage = Outlook.Application.GetNamespace("MAPI").GetItemFromID(objEmailMessage.EntryID)
                objEmailMessage.FlagStatus = olFlagComplete
                objEmailMessage.Save
            Else
                Set objEmailMessage = objEmailMessage.Move(objFolder)
            End If
        End If
       
        '-------------------------------------------------------------------------------
        '   Free memory and exit
        '-------------------------------------------------------------------------------
    Finish:
        Set objFolder = Nothing
        Exit Function
       
        '-------------------------------------------------------------------------------
        '   Error handling
        '-------------------------------------------------------------------------------
    ErrorHandler:
        If Err.Description = "Can't move the items." Then
            Debug.Print "   ...failed to move"
        Else
            Call HandleError(Err.Number, Err.Description)
        End If
        Resume Finish
       
    End Function

     


    A point to note: the colFolders variable is a global collection that stores the MAPI EntryIDs for the folders I am using. Anyone wishing to use this function needs to implement their own method for determining the folder (plenty of ways to do that).

    I'm very annoyed that Outlook has this bug but at least I have managed to find a solution. I hope this helps someone else so that they won't waste as much time on this as I have.

  • hjt

    Also wanted to say thanks...this has been driving me crazy for the last week or so. This, however, works like a charm.

  • Problem altering/saving moved MailItems in Outlook VBA