MauricioUY

Hi,

I want to improve an outlook addin which is using mapi fields (a lot) and this is generating abnormal behaviors in outlook, so now I'm in doubt to quit all use of mapi fields fo User Properties but I need some recomendatios or comments about using "mapi fields vs user properties" (or viceversa).

Thanks,

Mauricio.




Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Sue Mosher - Outlook MVP

.NET languages have no support for accessing MAPI properties in versions prior to Outlook 2007. Therefore, the UserProperties collection would be the supported approach.

What kind of behaviors are you seeing as abnormal





Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

Somethings like warnings when closing a mail saying: "The message has changed, do yo want to save...."

or if you save a message in drafts and later open it and make some changes, when yo want save it, a warning appear saying that a copy will be made in origin folder, so you have a copy in inbox too, later if yo close outlook , when you open it again and open the message in drafts folder the last changes does not appear, but in the copy in inbox the changes are.......

Behaviors like this happen in PCs which have my outlook addin installed, I assume that work with mapi fields are the problem because if I ommit set mapi fields this behaviors does not happen.

Thanks for your help,

Mauricio






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Sue Mosher - Outlook MVP

When you work with MAPI properties, you need to make sure you release items promptly so that you don't get conflicts between an item still held in memory and the item the user sees in the UI. It may even be necessary to force GC.Collect.



Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

ok, I post one method of many which I'm using:

Public Shared Sub CampoOculto_Agregar(ByRef Item As Outlook.MailItem, ByVal Campo As String, ByVal valor As String)
Dim lmsg As MAPI.Message
Dim lsTest As String
If IsDate(Left(Item.BillingInformation, 10)) Then
SetCampoOm(Item.BillingInformation, Campo, valor)
Else
Dim larValor As Array = Array.CreateInstance(GetType(String), 1)
larValor(0) = valor
lmsg = MSession.GetMessage(Item.EntryID, Item.Parent.StoreId)
Try
lsTest = lmsg.Fields(Campo).Name

If TypeName(lmsg.Fields(Campo).Value) = "Object()" Then
lmsg.Fields(Campo).Value = larValor
ElseIf TypeName(lmsg.Fields(Campo).Value) = "String" Then
lmsg.Fields(Campo).Delete()
lmsg.Fields.Add(Campo, vbArray + vbString, larValor)
Else
MsgBox("error de tipo al actualizar campo: " & Campo)
End If
Catch ex As System.Exception
Try
'lFields = lmsg.Fields()
'lFields.Add(campo,vbArray + vbstring
lmsg.Fields.Add(Campo, vbArray + vbString, larValor)

Catch ex1 As System.Exception
MsgBox(ex1.Message)
End Try
End Try
lmsg.Update(True, True)
lmsg = Nothing
End If

What is wrong in this code (talking about work with MAPI fields, of course)

I really appreciate your help, thanks.

Mauricio.






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Ken Slovak

For one thing, using CDO 1.21 in .NET code is not supported and can cause all sorts of problems. For another you should be releasing the object Imsg by possibly calling Marshal.ReleaseCOMObject on it after setting it to Nothing and then possibly calling GC.Collect().



Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

Hi.

Now I tried following your suggests but it did not solve the problem, the abnormal behavior continue :(

--------------------------------------------------------------

System.Runtime.InteropServices.Marshal.ReleaseComObject(lmsg)
lmsg = Nothing
GC.Collect()

---------------------------------------------------------------

So, if CDO 1.21 with .NET and Office2003 does not work (or generate abnormal behaviors) :

how could I do to persist metadata associated to a mail object (user properties are not an option because I don't have a mapi form associated with all mails)

thanks a lot for your support,

Mauricio.






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Sue Mosher - Outlook MVP

I think you need to set an object to Nothing before calling ReleaseComObject.

The availability of the UserProperties collection is not dependent on a custom form being associated with a message. That said, using UserProperties on messages can create problems for forwarded messages. Lower level third-party libraries like Outlook Redemption and MAPI33 can provide access to features that normally are available only with Extended MAPI. Another approach would be to use VB6 to create your own COM component using CDO 1.21 that your .NET application can call when it needs that functionality.





Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

if I set to nothing the object before calling ReleaseComObject, this method throw an error because object is nothing..., so I assume that set to nothing goes after ReleaseComObject.

I can't to use UserProperties without a custom form because I need persist the values on mail item on forward and reply. When user close Outlook and open it again I need the value on mail still there too.

So, now I will try to do something with redemption or mapi33 or else make my own vb6 dll.

thanks a lot again,

Mauricio.

PD: later I will post my tests result and maybe new questions ;)






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Ken Slovak

Does it work any better if you do this

iMsg = Nothing

GC.Collect()

GC.WaitForPendingFinalizers





Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

Hi!

Now I tried to do this:

-------------------------------------------------------------------------

.NET code:

    Public Sub SetCampoMAPI_Mejorado(ByRef Item As Outlook.MailItem, ByVal Campo As String, ByVal valor As String)
        Dim lwrapMAPI As Object, lsEntryID As String, lsStoreID As String
        Try
            lsEntryID = Item.EntryID
            lsStoreID = Item.Parent.StoreID
            lwrapMAPI = CreateObject("prjWorkMAPI.clsWorkMAPI")  'I allready try with an interop of this DLL
            lwrapMAPI.SetCampoMAPI(lsEntryID, lsStoreID, Campo, valor)
            lwrapMAPI.Dispose()
            System.Runtime.InteropServices.Marshal.ReleaseComObject(lwrapMAPI)
            lwrapMAPI = Nothing
        Catch ex As System.Exception
            MsgBox(ex.Message & vbCrLf & ex.Source & vbCrLf & ex.StackTrace, MsgBoxStyle.Critical, "Atencion")
            If Not lwrapMAPI Is Nothing Then
                lwrapMAPI.Dispose()
                System.Runtime.InteropServices.Marshal.ReleaseComObject(lwrapMAPI)
                lwrapMAPI = Nothing
            End If
        End Try
        GC.Collect()
        GC.WaitForPendingFinalizers()
    End Sub

------------------------------------------------------------------------

as you see, I made a VB6's dll which open a mapi session and modify or create a mapi field in a message. In this dll I'm doing early binding with CDO and set to nothing all objects.

------------------------------------------------------------------------

VB6 dll code:

Option Explicit

Private mSessionObj As MAPI.Session

Public Sub SetCampoMAPI(ByVal xsEntryID As String, ByVal xsStoreID As String, _
                        ByVal Campo As String, ByVal valor As String)
    Dim lmsg As MAPI.Message
    Dim loFields As MAPI.Fields, loField As MAPI.Field, lbExiste As Boolean
    Dim lsTest As String
    On Error GoTo ErrHand
   
    Set lmsg = MSession.GetMessage(xsEntryID, xsStoreID)
    Set loFields = lmsg.Fields
    If loFields.Count > 0 Then
        For Each loField In loFields
            If UCase(Trim("" & loField.Name)) = UCase(Trim("" & Campo)) Then
                loField.Value = valor
                lbExiste = True
                Exit For
            End If
        Next
    End If
    If Not lbExiste Then
        loFields.Add Campo, vbString, valor
    End If
   
    lmsg.Update True, True

    Set loFields = Nothing
    Set lmsg = Nothing
   
Exit Sub
ErrHand:
    Err.Raise Err.Number, "SetCampoMAPI" & Err.Source, Err.Description
End Sub

Public Function MSession() As MAPI.Session
    If mSessionObj Is Nothing Then
        Set mSessionObj = New MAPI.Session
        mSessionObj.Logon "Outlook", "", False, False
        MsgBox "Cree la sesion MAPI:" & mSessionObj.CurrentUser().Name
    End If

    Set MSession = mSessionObj
End Function


Public Sub Dispose()
    If Not mSessionObj Is Nothing Then
        mSessionObj.Logoff
        Set mSessionObj = Nothing
    End If
End Sub

------------------------------------------------------------------------

But the unwanted behavior still here :(

when I want to save a message which was in draft folder, the warning message appear ("...this message has been changed by another user or in another window...."), only when I set mapi fields of course.

reproduce this behavior is very simple, did you try

thanks for your help,

Mauricio.






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Ken Slovak

No, I don't get that error in either VB6 or VBA code. Do you get it if the Message is saved elsewhere, other than Drafts

Do you have any open references to the Imsg item You're passing the EntryID and StoreID for Imsg, are you releasing any object references to that object before passing the ID strings





Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

Hi,

yes I get this warning in inbox folder too ("The item could not be saved because it has been changed by another user o in another window.Do you want to make a copy in the default folder for the item ").

but, did you trying do something like I'm doing (addin in .net 2003, calling a VB6 dll and using the MailItem variable in .net code which is in module level because I need events handling, to catch mail close for example)

I'm going to do some tests without event handling and without module level variables, later I'll tell you about this.

thanks for your patience,

Mauricio.






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

MauricioUY

well, I just trying to do the same that I'm doing with .net 2003 but now I try to do it with VB6, here is the code (in red is my customized code):

---------------------------------------------------------------------------------------------------------

Option Explicit

Public FormDisplayed As Boolean
Public VBInstance As VBIDE.VBE
Dim mcbMenuCommandBar As Office.CommandBarControl
Dim mfrmAddIn As New frmAddIn
Public WithEvents MenuHandler As CommandBarEvents 'command bar event handler

Public WithEvents OLKApplication As Outlook.Application
Public WithEvents ColInspectors As Inspectors
Private mSessionObj As MAPI.Session

Sub Hide()

On Error Resume Next

FormDisplayed = False
mfrmAddIn.Hide

End Sub

Sub Show()

On Error Resume Next

If mfrmAddIn Is Nothing Then
Set mfrmAddIn = New frmAddIn
End If

Set mfrmAddIn.VBInstance = VBInstance
Set mfrmAddIn.Connect = Me
FormDisplayed = True
mfrmAddIn.Show

End Sub

'------------------------------------------------------
'this method adds the Add-In to VB
'------------------------------------------------------
Private Sub AddinInstance_OnConnection(ByVal Application As Object, ByVal ConnectMode As AddInDesignerObjects.ext_ConnectMode, ByVal AddInInst As Object, custom() As Variant)
On Error GoTo error_handler

'save the vb instance
'Set VBInstance = Application
Set OLKApplication = Application
Set ColInspectors = OLKApplication.Inspectors

' 'this is a good place to set a breakpoint and
' 'test various addin objects, properties and methods
' Debug.Print VBInstance.FullName
'
' If ConnectMode = ext_cm_External Then
' 'Used by the wizard toolbar to start this wizard
' Me.Show
' Else
' Set mcbMenuCommandBar = AddToAddInCommandBar("My AddIn")
' 'sink the event
' Set Me.MenuHandler = VBInstance.Events.CommandBarEvents(mcbMenuCommandBar)
' End If
'
' If ConnectMode = ext_cm_AfterStartup Then
' If GetSetting(App.Title, "Settings", "DisplayOnConnect", "0") = "1" Then
' 'set this to display the form on connect
' Me.Show
' End If
' End If

Exit Sub

error_handler:

MsgBox Err.Description

End Sub

'------------------------------------------------------
'this method removes the Add-In from VB
'------------------------------------------------------
Private Sub AddinInstance_OnDisconnection(ByVal RemoveMode As AddInDesignerObjects.ext_DisconnectMode, custom() As Variant)
On Error Resume Next

'delete the command bar entry
mcbMenuCommandBar.Delete

'shut down the Add-In
If FormDisplayed Then
SaveSetting App.Title, "Settings", "DisplayOnConnect", "1"
FormDisplayed = False
Else
SaveSetting App.Title, "Settings", "DisplayOnConnect", "0"
End If

Unload mfrmAddIn
Set mfrmAddIn = Nothing

End Sub

Private Sub IDTExtensibility_OnStartupComplete(custom() As Variant)
If GetSetting(App.Title, "Settings", "DisplayOnConnect", "0") = "1" Then
'set this to display the form on connect
Me.Show
End If
End Sub

'this event fires when the menu is clicked in the IDE
Private Sub MenuHandler_Click(ByVal CommandBarControl As Object, handled As Boolean, CancelDefault As Boolean)
Me.Show
End Sub

Function AddToAddInCommandBar(sCaption As String) As Office.CommandBarControl
Dim cbMenuCommandBar As Office.CommandBarControl 'command bar object
Dim cbMenu As Object

On Error GoTo AddToAddInCommandBarErr

'see if we can find the Add-Ins menu
Set cbMenu = VBInstance.CommandBars("Add-Ins")
If cbMenu Is Nothing Then
'not available so we fail
Exit Function
End If

'add it to the command bar
Set cbMenuCommandBar = cbMenu.Controls.Add(1)
'set the caption
cbMenuCommandBar.Caption = sCaption

Set AddToAddInCommandBar = cbMenuCommandBar

Exit Function

AddToAddInCommandBarErr:

End Function


Private Sub ColInspectors_NewInspector(ByVal Inspector As Outlook.Inspector)
Dim lMailItem As Outlook.MailItem

Set lMailItem = Inspector.CurrentItem
If Not lMailItem Is Nothing Then
SetCampoMAPI lMailItem.EntryID, lMailItem.Parent.StoreID, "Prueba", "MPV"

End If
Set lMailItem = Nothing

End Sub


Public Sub SetCampoMAPI(ByVal xsEntryID As String, ByVal xsStoreID As String, _
ByVal Campo As String, ByVal valor As String)
Dim lmsg As MAPI.Message
Dim loFields As MAPI.Fields, loField As MAPI.Field, lbExiste As Boolean
Dim lsTest As String
On Error GoTo ErrHand

Set lmsg = MSession.GetMessage(xsEntryID, xsStoreID)
Set loFields = lmsg.Fields
If loFields.Count > 0 Then
For Each loField In loFields
If UCase(Trim("" & loField.Name)) = UCase(Trim("" & Campo)) Then
loField.Value = valor
lbExiste = True
Exit For
End If
Next
End If
If Not lbExiste Then
loFields.Add Campo, vbString, valor
End If

lmsg.Update True, True

Set loFields = Nothing
Set lmsg = Nothing

Dispose
Exit Sub
ErrHand:
Err.Raise Err.Number, "SetCampoMAPI" & Err.Source, Err.Description
End Sub

Public Function MSession() As MAPI.Session
If mSessionObj Is Nothing Then
Set mSessionObj = New MAPI.Session
mSessionObj.Logon "Outlook", "", False, False
MsgBox "Cree la sesion MAPI:" & mSessionObj.CurrentUser().Name
End If

Set MSession = mSessionObj
End Function

Public Sub Dispose()
If Not mSessionObj Is Nothing Then
mSessionObj.Logoff
Set mSessionObj = Nothing
End If
End Sub

---------------------------------------------------------------------------------------------------------

and the result is exactly the same that I have with .net addin......... when I modify a mapi field and after I want to save the mail, I get the warning message.

I don't realise if are something wrong it that code, I think that I release all objects but abnormal behavior is still here :(

So, after all of this tests I assume that this behavior will happen allways that you want to change or add mapi fields in a mail item.

could you post the code that you used to test this behavior and didn't get the error

thanks a lot,

Mauricio.






Re: Visual Studio Tools for Office Outlook: MAPI fields versus User Properties

Ken Slovak

No, I didn't exactly replicate your setup, I just tested using some Outlook VBA code with CDO 1.21.

I'd first suggest using a piggyback logon to CDO and not using a profile logon. That could be part of the problem. I would logon as follows: mSessionObj.Logon "", "", False, False. Since Outlook is already running that lets CDO share the same session as Outlook.

I'd also not do everything you're doing in NewInspector. The object reference you can get in NewInspector is a pretty weak reference and I only use it for CurrentItem.Class, .MessageClass. I wait until Inspector.Activate fires before I try to manipulate any properties such as the Fields collection to make sure the item in the Inspector is fully instantiated.

Using Update(True, True) from a call from NewInspector might be part of the problem along with the other things I mentioned.

See if changing those things helps.