Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I have migrated an classic COM ArcMap Extension of mine to an add-in. I have tried the new persistence approach of add-in's but I cannot serialize my custom objects through the XMLSerializer. I get all sorts of COM errors. The custom objects all implement IPersistVariant. And I suspect this interface needs COM registration to work. What would be the most efficient way to get my objects to persist?

Or is there a way that I can still use the IPersistVariant interface on my custom objects to persist them in the mxd-document?

This is the class of the custom object I am trying to persist:

Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geometry

<ComClass(RigPolyline.ClassId, RigPolyline.InterfaceId, RigPolyline.EventsId)> _
Public Class RigPolyline

Implements IPersistVariant

#Region "COM GUIDs"
    Public Const ClassId As String = "1572F48A-63B5-4720-AF6A-0B898B549764"
    Public Const InterfaceId As String = "13AA4CE9-95A0-43A1-8A92-9BF66B45E226"
    Public Const EventsId As String = "2EE2B76B-DC06-4563-8441-9FA0BC584C20"
#End Region


Public m_Name As String = ""
Public m_Polyline As IPolyline
Public m_IsVisible As Boolean
Public m_PolylineID As Integer

Public Sub New()

End Sub

Public Property Name() As String
    Get
        Return m_Name
    End Get
    Set(ByVal Value As String)
        m_Name = Value
    End Set
End Property

Public Property Polyline() As IPolyline
    Get
        Polyline = m_Polyline
    End Get
    Set(ByVal Value As IPolyline)
        m_Polyline = Value
    End Set
End Property

Public Property Visible() As Boolean
    Get
        Return m_IsVisible
    End Get
    Set(ByVal Value As Boolean)
        m_IsVisible = Value
    End Set
End Property

Public Property PolylineID() As Integer
    Get
        Return m_PolylineID
    End Get
    Set(ByVal Value As Integer)
        m_PolylineID = Value
    End Set
End Property

Public Function CreateCopy() As RigPolyline
    Dim pPolylineCopy As RigPolyline = New RigPolyline
    Dim pPolyline As IPolyline = New Polyline
    Dim pClone As IClone

    pPolylineCopy.Name = m_Name
    pPolylineCopy.Visible = m_IsVisible
    pPolylineCopy.PolylineID = m_PolylineID

    pClone = m_Polyline
    pPolyline = pClone.Clone
    pPolylineCopy.Polyline = pPolyline

    Return pPolylineCopy

End Function


Public ReadOnly Property ID As ESRI.ArcGIS.esriSystem.UID Implements ESRI.ArcGIS.esriSystem.IPersistVariant.ID
    Get
        Try
            Dim extUID As New UIDClass()
            extUID.Value = Me.GetType().GUID.ToString("B")
            Return extUID

        Catch ex As Exception
            Dim pErr As New ShowError(ex)
            Return Nothing
        End Try
    End Get
End Property

Public Sub Load(ByVal Stream As ESRI.ArcGIS.esriSystem.IVariantStream) Implements ESRI.ArcGIS.esriSystem.IPersistVariant.Load
    Try
        m_Name = Stream.Read
        m_Polyline = Stream.Read
        m_IsVisible = Stream.Read
        m_PolylineID = Stream.Read
    Catch ex As Exception
        Dim pErr As New ShowError(ex)
    End Try
End Sub

Public Sub Save(ByVal Stream As ESRI.ArcGIS.esriSystem.IVariantStream) Implements ESRI.ArcGIS.esriSystem.IPersistVariant.Save
    Try
        Stream.Write(m_Name)
        Stream.Write(m_Polyline)
        Stream.Write(m_IsVisible)
        Stream.Write(m_PolylineID)

    Catch ex As Exception
        Dim pErr As New ShowError(ex)
    End Try
End Sub
End Class

The class that inherits from Extension holds the Load en Save methods:

Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.ADF.Serialization
Imports ESRI.ArcGIS.Geometry

Public Class APPExtension
Inherits ESRI.ArcGIS.Desktop.AddIns.Extension

Private Const PersistdataVersion As String = "4.0"
Private mData As MyPersistentData

<Serializable()> _
Private Structure MyPersistentData

    Public persistanceVersion As String
    Public RigPolyline As Byte()

End Structure

Public Sub New()
End Sub


Protected Overrides Sub OnLoad(ByVal inStrm As System.IO.Stream)

    mData.persistanceVersion = ""
    mData.RigPolyline = Nothing
    PersistenceHelper.Load(Of MyPersistentData)(inStrm, mData)

    If mData.persistanceVersion = PersistdataVersion Then
        Dim mbs As IMemoryBlobStream = New MemoryBlobStream
        Dim mbsv As IMemoryBlobStreamVariant = mbs
        mbsv.ImportFromVariant(mData.RigPolyline)

        Dim pStream As IPersistStream = New PropertySet
        pStream.Load(mbs)

        Dim pPropSet As IPropertySet = pStream
        Dim line As RigPolyline = pPropSet.GetProperty("RigPolyline")
    End If

End Sub

Protected Overrides Sub OnSave(ByVal outStrm As System.IO.Stream)

    mData = New MyPersistentData
    mData.persistanceVersion = "4.0"

    Dim line As RigPolyline = New RigPolyline
    line.Name = "My line"
    line.Polyline = New Polyline
    Dim propSet As IPropertySet = New PropertySet
    propSet.SetProperty("RigPolyline", line)

    Dim ps As IPersistStream = propSet
    Dim mbs As IMemoryBlobStream = New MemoryBlobStream
    ps.Save(mbs, 0)
    Dim bytes As Byte()
    Dim mbsv As IMemoryBlobStreamVariant = mbs
    mbsv.ExportToVariant(bytes)
    mData.RigPolyline = bytes

    PersistenceHelper.Save(Of MyPersistentData)(outStrm, mData)

End Sub


End Class
share|improve this question
    
Are you following the same pattern used in the LogExtension Sample? –  Kirk Kuykendall Jul 18 '12 at 13:08
    
Yes but the BinaryFormatter cannot serialize my custom objects that implement IPersistVariant. Or should that work according to you? –  JsP Jul 18 '12 at 14:49
    
Do your custom objects use the SerializableAttribute, as described here? –  Kirk Kuykendall Jul 18 '12 at 14:57
    
Also, are you using the PersistenceHelper? –  Kirk Kuykendall Jul 18 '12 at 15:10

1 Answer 1

Use microsoft's SerializableAttribute and Esri's PersistenceHelper.

I haven't tested this code. Esri's help page for add-in Extension OnLoad method seems to have mangled the example code. Here it is reformatted:

// Need to add reference to ESRI.ArcGIS.ADF.Local assembly. 
// using System; 
// using System.Collections.Generic; 
// using System.IO; 
// using ESRI.ArcGIS.ADF.Serialization; 
public class LogExtension : ESRI.ArcGIS.Desktop.AddIns.Extension 
{ 
    private MyPersistentData _data; 
    [Serializable()] 
    private struct MyPersistentData 
    { 
      public string Location; 
      public string UserName; 
      public ESRI.ArcGIS.Geometry.PointClass Point; 
    } 

    public LogExtension() { } 
    protected override void OnStartup() 
    { 
        ArcMap.Events.OpenDocument += new ESRI.ArcGIS.ArcMapUI.IDocumentEvents_OpenDocumentEventHandler(Events_OpenDocument); 
    } 
    protected override void OnShutdown() { _data.Point = null; } 
    void Events_OpenDocument() 
    { 
        string logText = _data.Location 
                         + " ( " + _data.Point.X + ", " + _data.Point.Y + " )" 
                         + "\n UserName: " + _data.UserName; 
            System.Windows.Forms.MessageBox.Show(logText); 
    } 
    protected override void OnSave(Stream outStrm) 
    { 
        //Get called when saving document. 
        _data = new MyPersistentData(); 
        _data.Location = "Home"; 
        _data.UserName = Environment.UserName; 
        _data.Point = new ESRI.ArcGIS.Geometry.PointClass(); 
        _data.Point.X = 100; _data.Point.Y = 200; 
        PersistenceHelper.Save<MyPersistentData>(outStrm, _data); 
    } 
    protected override void OnLoad(Stream inStrm) 
    { 
        //Get called when opening a document with persisted stream. 
        //Initialize the struct _data.Location = "";
        _data.Point = new ESRI.ArcGIS.Geometry.PointClass(); 
        PersistenceHelper.Load<MyPersistentData>(inStrm, ref _data); 
    } 
}
share|improve this answer
    
I'll try this, thank you Kirk. –  JsP Jul 18 '12 at 15:41
    
Yeah in 10.0, you cant implement an interface in an add-in. It'll compile but internally the cast to the interface never works. In 10.1 the restriction was removed but IPersist may still be a special case. –  SeaJunk Jul 18 '12 at 17:33
    
I use 10.0 and implement several interfaces in my add-in with success: ILayer, ILegendinfo, ITool, ICommand, IGeoDataset. –  JsP Jul 19 '12 at 11:25
    
I think what @SeaJunk is saying is you can't implement an interface on on add-in. Well, you can, but apparently arcgis doesn't QI for it. –  Kirk Kuykendall Jul 19 '12 at 13:59
    
Possibly yes. I now followed your advice Kirk and put in the PersistenceHelper and decorated my class with the SerializeAttribute. It works for most types, but not for members declared as interfaces (there they are again). Throughout my solution I of course use members declared as IPolyline, IPoint an so on. In the sample they use PointClass which works. This is a problem since a lot of methods in ArcObjects return objects through their interfaces. Any thoughts on that? –  JsP Jul 19 '12 at 14:28

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.