Results 1 to 18 of 18

Thread: [RESOLVED] [2005] Problem Serializing

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Resolved [RESOLVED] [2005] Problem Serializing

    EDIT: Found the solution to this problem. Solution begins in post #13 below.

    I have a serializable class Person
    The Person class used to be contained in a project along with a Form that I was using for testing.
    I was using the class with no problems while the Person class was a part of the same project as the form I was using for testing. Serializing and Deserializing an object of type Person was successful.

    Then I moved the Person class to a class library.
    The Class works fine from the test project except it won't serialize.

    I just have some buttons on the form that create a Person and then Serialize and Deserialize the Person.
    Now however, when I try to serialize a Person object I get an error message that the form used in the testing project isn't serializable; i.e. the form itself is not serializable.

    What might be the problem?

    EDIT: Found the solution to this problem. Solution begins in post #13 below.
    Last edited by FourBlades; Aug 20th, 2007 at 04:22 AM.

  2. #2

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Here is the error message:

    Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: [2005] Problem Serializing

    You have to apply the Serializable attribute to the class.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  4. #4
    I'm about to be a PowerPoster!
    Join Date
    Jan 2005
    Location
    Everywhere
    Posts
    13,647

    Re: [2005] Problem Serializing

    That sounds suss... you're serialising a Person object, but you get that error about the form?

    Show us the code that you use to serialise the Person object.

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: [2005] Problem Serializing

    Hmmm... should have read more carefully.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Ok, I've narrowed it down to the fact that I was trying to serialize a delegate and the associated event declaration.
    So while the error message I copied to post #2 seems a little strange, the source has been identified.

    I apparently need to learn how to properly serialize a delegate.

    So, I'll be reading on how to handle the problem.

    In the mean time, If anyone can point me in the right direction please let me know.
    Last edited by FourBlades; Aug 18th, 2007 at 07:19 AM.

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Here's the client class (Form) code:
    Anyone see a problem with it that would cause the following error?

    Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.


    Code:
    Imports System.Runtime.Serialization
    Imports System.Runtime.Serialization.Formatters.Binary
    Imports System.IO
    Imports DILU.Utilities
    
    Public Class Form1
    
    #Region "Serialize"
    
        Private Sub Serialize(ByVal fileName As String, ByVal person As Person)
    
            Dim fs As FileStream = New FileStream(fileName, FileMode.OpenOrCreate)
    
            Dim bf As New BinaryFormatter
            Try
                bf.Serialize(fs, person)
            Catch exception As SerializationException
                Throw
            Finally
                fs.Close()
            End Try
    
        End Sub
    
    #End Region
    
    #Region "Deserialize"
    
        Private Function Deserialize(ByVal fileName As String) As Person
    
            Dim fs As FileStream = New FileStream(fileName, FileMode.Open)
    
            Dim p As New Person
            Try
    
                Dim formatter As New BinaryFormatter
                p = DirectCast(formatter.Deserialize(fs), Person)
    
            Catch exception As Runtime.Serialization.SerializationException
                Throw
            Finally
                fs.Close()
            End Try
    
            Return p
    
        End Function
    
    #End Region
    
    #Region "Button One Click"
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
            Dim p As New Person("FirstName", "LastName")
    
            AddHandler p.LastNameChanged, AddressOf PersonsLastNameChanged
            AddHandler p.FirstNameChanged, AddressOf PersonsFirstNameChanged
    
            Me.TextBox1.Text = p.FirstName
            Me.TextBox2.Text = p.LastName
    
            Serialize("C:\TestBinaryFormatterSerialization.txt", p)
            Dim np As Person = Deserialize("C:\TestBinaryFormatterSerialization.txt")
    
            Me.TextBox3.Text = np.ToString
            Me.TextBox4.Text = np.LastName
    
        End Sub
    
    #End Region
    
    #Region "Event Handlers For Names Changed"
    
        Private Sub PersonsLastNameChanged(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            MessageBox.Show("Last Name Changed, New Last Name Is: " & e.NewLastName)
        End Sub
    
        Private Sub PersonsFirstNameChanged(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            MessageBox.Show("First Name Changed, New First Name Is: " & e.NewFirstName)
        End Sub
    
    #End Region
    
    End Class
    Here Is The Code From The Class Library:

    First Name Changed Event Args Class:

    Code:
    Namespace Utilities
    
        <Serializable()> _
        Public Class FirstNameChangedEventArgs
            Inherits EventArgs
    
            Sub New(ByVal oldFirstName As String, ByVal newFirstName As String, ByVal sendingPerson As Person)
                mOldFirstName = oldFirstName
                mNewFirstName = newFirstName
            End Sub
    
            Private mOldFirstName As String
            Public ReadOnly Property OldFirstName() As String
                Get
                    Return mOldFirstName
                End Get
            End Property
    
            Private mNewFirstName As String
            Public ReadOnly Property NewFirstName() As String
                Get
                    Return mNewFirstName
                End Get
            End Property
    
            Private mPerson As Person
            Public ReadOnly Property Person() As Person
                Get
                    Return mPerson
                End Get
            End Property
    
        End Class
    
    End Namespace
    Last Name Changed Event Args Class:

    Code:
    Namespace Utilities
    
        <Serializable()> _
        Public Class LastNameChangedEventArgs
            Inherits EventArgs
    
            Sub New(ByVal oldLastName As String, ByVal newLastName As String, ByVal sendingPerson As Person)
                mOldLastName = oldLastName
                mNewLastName = newLastName
            End Sub
    
            Private mOldLastName As String
            Public ReadOnly Property OldLastName() As String
                Get
                    Return mOldLastName
                End Get
            End Property
    
            Private mNewLastName As String
            Public ReadOnly Property NewLastName() As String
                Get
                    Return mNewLastName
                End Get
            End Property
    
            Private mPerson As Person
            Public ReadOnly Property Person() As Person
                Get
                    Return mPerson
                End Get
            End Property
    
        End Class
    
    End Namespace
    The Person Class:

    Code:
    Namespace Utilities
    
        <Serializable()> _
        Public Class Person
    
    #Region "Event And Delegate Declarations"
    
            <Serializable()> _
            Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            Public Event LastNameChanged As LastNameChangedEventHandler
    
            <Serializable()> _
            Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            Public Event FirstNameChanged As FirstNameChangedEventHandler
    
    #End Region
    
    #Region "Private and Public Fields Not Wrapped By Properties (NONE)"
    
            'None
    
    #End Region
    
    #Region "Constructors"
    
            Sub New()
                Me.New("Unknown", "Unknown")
            End Sub
    
            Sub New(ByVal firstName As String, ByVal lastName As String)
    
                If Not (String.IsNullOrEmpty(firstName)) Then
                    mFirstName = firstName
                Else
                    mFirstName = "Unknown"
                End If
    
                If Not (String.IsNullOrEmpty(lastName)) Then
                    mLastName = lastName
                Else
                    mLastName = "Unknown"
                End If
    
            End Sub
    
    #End Region
    
    #Region "Instance Public Properties and Their Wrapped Fields"
    
            Private mLastName As String
            Public Property LastName() As String
                Get
                    Return mLastName
                End Get
                Set(ByVal value As String)
                    If Not mLastName = value Then
                        Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
                        OnLastNameChanged(Args)
                        mLastName = value
                    End If
                End Set
            End Property
    
            Private mFirstName As String
            Public Property FirstName() As String
                Get
                    Return mFirstName
                End Get
                Set(ByVal value As String)
                    If Not mFirstName = value Then
                        Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
                        mFirstName = value
                    End If
                End Set
            End Property
    
    #End Region
    
    #Region "Methods"
    
            Public Overrides Function ToString() As String
                Return String.Concat(FirstName, " ", LastName)
            End Function
    
            Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
                RaiseEvent LastNameChanged(Me, e)
            End Sub
    
            Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
                RaiseEvent FirstNameChanged(Me, e)
            End Sub
    
    #End Region
    
    #Region "Static Public Methods and Properties (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Methods In Interfaces (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Private Helper Methods (NONE)"
    
            'NONE
    
    #End Region
    
        End Class
    
    End Namespace

  8. #8

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Ok, the reason that the message "Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable" occurs is because the Form is being included in the object graph of the Person class being serialized.
    This is because the event delegate points to the Form class which is not serializeable (Forms never are).

    However, when I try to mark the event with the NonSerialized attribute I get this message:

    "Attribute 'NonSerializedAttribute' cannot be applied to 'LastNameChanged' because the attribute is not valid on this declaration type"

    Here is what I tried:

    Code:
    #Region "Event And Delegate Declarations"
    
            <Serializable()> _
            Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            <NonSerialized()> _  'Error Here
            Public Event LastNameChanged As LastNameChangedEventHandler
    
            <Serializable()> _
            Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            Public Event FirstNameChanged As FirstNameChangedEventHandler
    
    #End Region
    So how do I correctly mark the declaration for the event so that it will not be serialized?
    Last edited by FourBlades; Aug 18th, 2007 at 09:42 AM.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    FourBlades, Have you come up with a solution yet?

    I read that the NonSerialized attribute can be used in C# but not in VB.

    Might I suggest that prior to Serializing you remove any handlers from the using Form that registers to recieve event notifications from the Person class? That way the delegates within the Person class will not reference the Form and the Form will therefore not be included in the serialization object graph.

    Just a suggestion.

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Thank you for the idea. However, I've already thought of that.

    I'm hoping someone else might come up with a more elegant solution that circumvents having to deregister all of the event handlers prior to serialization.

  11. #11

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Ok, well let me know if you come up with an acceptable solution.

  12. #12

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Sure thing.

  13. #13

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Solution To Problem:
    The problem was as follows:
    When you serialize an object (I'm using the BinaryFormatter) an object graph is created that includes any object pointed to by the primary object being serialized. If the primary object being serialized contains a delegate (as in maybe an event delegate) andalso another object has subscribed to receive an event notification from the primary object (the one being serialized) then the subscribing object is also included in the object graph. The runtime attempts to serialize the subscribing object. If the subscribing object is not serializable, an error occurs.

    The solution to this problem is to exclude the delegates from being serialized.
    To exclude the delegates from being serialized you must handle custom serialization.
    Custom serialization requires the class author to implement the ISerializable interface.
    Within the implementation of the ISerializable interface you must write code to exclude any class members of type Delegate or any type that derives from Delegate.

    The complete Person class that implements the ISerializable interface is shown below:

    Code:
    Imports System
    Imports System.Security.Permissions
    Imports System.Runtime.Serialization
    
    Namespace Utilities
    
        <Serializable()> _
        Public Class Person
            Implements ISerializable
    
    #Region "Event And Delegate Declarations"
    
            '<Serializable()> _
            Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            Public Event LastNameChanged As LastNameChangedEventHandler
    
            '<Serializable()> _
            Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            Public Event FirstNameChanged As FirstNameChangedEventHandler
    
    #End Region
    
    #Region "Private and Public Fields Not Wrapped By Properties (NONE)"
    
            'None
    
    #End Region
    
    #Region "Constructors"
    
            Sub New()
                Me.New("Unknown", "Unknown")
            End Sub
    
            Sub New(ByVal firstName As String, ByVal lastName As String)
    
                If (String.IsNullOrEmpty(firstName)) Then
                    Throw New ArgumentNullException("firstName")
                Else
                    mFirstName = firstName
                End If
    
                If (String.IsNullOrEmpty(lastName)) Then
                    Throw New ArgumentNullException("lastName")
                Else
                    mLastName = lastName
                End If
    
            End Sub
    
            'The CLR calls this special constructor when this (Me) object is deserialized
            'Should have Protected scope if other classes can be inherited from this class
            'Otherwise, if this class is sealed (cannot be inherited from) the scope can be Private
            Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
                SerializationHelpers.ISerializableContructorHelper(info, context, Me)
            End Sub
    
            <SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
            Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) _
              Implements ISerializable.GetObjectData
    
                SerializationHelpers.GetObjectDataHelper(info, context, Me)
    
            End Sub
    
    #End Region
    
    #Region "Instance Public Properties and Their Wrapped Fields"
    
            Private mLastName As String
            Public Property LastName() As String
                Get
                    Return mLastName
                End Get
                Set(ByVal value As String)
                    If Not mLastName = value Then
                        Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
                        Me.OnLastNameChanged(Args)
                        mLastName = value
                    End If
                End Set
            End Property
    
            Private mFirstName As String
            Public Property FirstName() As String
                Get
                    Return mFirstName
                End Get
                Set(ByVal value As String)
                    If Not mFirstName = value Then
                        Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
                        Me.OnFirstNameChanged(args)
                        mFirstName = value
                    End If
                End Set
            End Property
    
    #End Region
    
    #Region "Instance Public Methods"
    
            Public Overrides Function ToString() As String
                Return String.Concat(FirstName, " ", LastName)
            End Function
    
            Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
                RaiseEvent LastNameChanged(Me, e)
            End Sub
    
            Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
                RaiseEvent FirstNameChanged(Me, e)
            End Sub
    
    #End Region
    
    #Region "Static Public Methods and Properties (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Methods In Interfaces (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Private Helper Methods (NONE)"
    
            'NONE
    
    #End Region
    
        End Class
    
    End Namespace

  14. #14

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Opps clicked on Submit Reply instead of Preview Post...more in next post

  15. #15

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    You'll notice a Protected Sub New and a Protected Overridable Sub GetObjectData highlighted in bold text within the Person class listed two posts above.
    These are the two additional code blocks required by the ISerializable interface.

    You'll also notice that in those blocks reference is made to a helper class as found below:

    Credit for the code goes to the authors of Practical Guidelines And Best Practices For Microsoft Visual Basic and Visual C# Developers a book I would highly recommend to anyone.

    Code:
    Imports System.Runtime.Serialization
    Imports System.Reflection
    
    Namespace Utilities
    
        Public Class SerializationHelpers
    
            ''' <summary>
            ''' Helper routine for serializing objects.
            ''' (Place a call to this sub into the Serializable Classes Protected Overridable Sub GetObjectData)
            ''' i.e. place into: Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
            ''' </summary>
            ''' <param name="info"></param>
            ''' <param name="context"></param>
            ''' <param name="obj"></param>
            ''' <remarks></remarks>
            Public Shared Sub GetObjectDataHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
    
                'Get list of serializable members
                Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
    
                'Read the value of each member
                Dim values() As Object = FormatterServices.GetObjectData(obj, members)
    
                'Store in the SerializationInfo Object, using member name
                For i As Integer = 0 To members.Length - 1
                    info.AddValue(members(i).Name, values(i))
                Next
    
            End Sub
    
            ''' <summary>
            ''' Helper routine for deserializing objects.
            ''' Place a call to this function into the Serializable Constructor; 
            ''' i.e. place into:   Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext))
            ''' </summary>
            ''' <param name="info"></param>
            ''' <param name="context"></param>
            ''' <param name="obj"></param>
            ''' <remarks></remarks>
            Public Shared Sub ISerializableContructorHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
    
                'Get the list of serializable members for this object 
                'excluding members that are of type delegate (filtered)
                'Note that members()(below) contains all types of members from the class passed into this sub via obj (byval obj as Object)
                Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
    
                Dim values(members.Length - 1) As Object
    
                'Read the value for each member
                For i As Integer = 0 To members.Length - 1
    
                    'retrieve the members from members() that are of type field
                    'and then get all field's values
                    Dim member As MemberInfo = members(i)
                    If member.MemberType = MemberTypes.Field Then
                        Dim membertype As Type = CType(member, FieldInfo).FieldType
                        values(i) = info.GetValue(member.Name, membertype)
                    End If
                Next
    
                'assign all serializable members in one operation
                FormatterServices.PopulateObjectMembers(obj, members, values)
    
            End Sub
    
            ''' <summary>
            ''' Return an array of serializable MemberInfo for a given type.
            ''' (Same as FormatterServices.GetSerializableMember, but FILTERS OUT delegate fields.)
            ''' </summary>
            ''' <param name="ty"></param>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Private Shared Function GetFilteredSerializableMembers(ByVal ty As Type) As MemberInfo()
    
                Dim al As New ArrayList
    
                'Get the list of serializable members
                For Each mi As MemberInfo In FormatterServices.GetSerializableMembers(ty)
    
                    'Add the mi object to the arraylist if it is NOT:
                    '(a FieldInfo object) [that IS also (AndAlso)] (found in the inheritance hierarchy of the Delegate class)
                    If Not (TypeOf mi Is FieldInfo AndAlso GetType([Delegate]).IsAssignableFrom(DirectCast(mi, FieldInfo).FieldType)) Then
                        al.Add(mi)
                    End If
                Next
                Return DirectCast(al.ToArray(GetType(MemberInfo)), MemberInfo())
            End Function
    
        End Class
    
    End Namespace
    You'll have to read up on the ISerializable interface and the purpose of it's members because a complete explanation would be lengthy.
    Upon reading up on how these two code blocks work in general; i.e. the purpose for the special sub New and the GetObjectData method you'll quickly see the importance of these helper methods.

    A complete example including the using class, the simple Person class, and the Helper class follows In the next post (was not accepted to this post because of it's size):

  16. #16

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Using Class example:

    Code:
    Imports System.Runtime.Serialization
    Imports System.Runtime.Serialization.Formatters.Binary
    Imports System.IO
    Imports DILU.Utilities
    
    Public Class Form1
    
    #Region "Serialize"
    
        Private Sub Serialize(ByVal fileName As String, ByVal person As Person)
    
            Dim fs As FileStream = New FileStream(fileName, FileMode.OpenOrCreate)
    
            Dim bf As New BinaryFormatter
            Try
                bf.Serialize(fs, person)
            Catch exception As SerializationException
                MessageBox.Show("Failed to serialize. Reason: " & exception.Message)
                Throw
            Finally
                fs.Close()
            End Try
    
        End Sub
    
    #End Region
    
    #Region "Deserialize"
    
        Private Function Deserialize(ByVal fileName As String) As Person
    
            Dim fs As FileStream = New FileStream(fileName, FileMode.Open)
    
            Dim p As New Person
            Try
    
                Dim formatter As New BinaryFormatter
                p = DirectCast(formatter.Deserialize(fs), Person)
    
            Catch exception As Runtime.Serialization.SerializationException
                Console.WriteLine("Failed to deserialize. Reason: " & exception.Message)
                Throw
            Finally
                fs.Close()
            End Try
    
            Return p
    
        End Function
    
    #End Region
    
    #Region "Button One Click"
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
            Dim p As Person
            Try
                p = New Person(" ", " ")
            Catch ex As ArgumentNullException
                p = Nothing
                p = New Person
            End Try
    
            Me.TextBox1.Text = p.FirstName
            Me.TextBox2.Text = p.LastName
    
            'yes, I actually do have a need for these two events :)
            AddHandler p.LastNameChanged, AddressOf PersonsLastNameChanged
            AddHandler p.FirstNameChanged, AddressOf PersonsFirstNameChanged
    
            Serialize("C:\BinaryNoDelegateSerialization.txt", p)
            Dim np As Person = Deserialize("C:\BinaryNoDelegateSerialization.txt")
    
            Me.TextBox3.Text = np.FirstName
            Me.TextBox4.Text = np.LastName
    
            Application.DoEvents()
    
            Threading.Thread.Sleep(3000)
    
            p.LastName = "New Last Name"
            p.FirstName = "New First Name"
    
        End Sub
    
    #End Region
    
    #Region "Event Handlers For Names Changed"
    
        Private Sub PersonsLastNameChanged(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            Me.TextBox2.Text = e.NewLastName
        End Sub
    
        Private Sub PersonsFirstNameChanged(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            Me.TextBox1.Text = e.NewFirstName
        End Sub
    
    #End Region
    
    End Class
    Person Class:

    Code:
    Imports System
    Imports System.Security.Permissions
    Imports System.Runtime.Serialization
    
    Namespace Utilities
    
        <Serializable()> _
        Public Class Person
            Implements ISerializable
    
    #Region "Event And Delegate Declarations"
    
            '<Serializable()> _
            Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
            Public Event LastNameChanged As LastNameChangedEventHandler
    
            '<Serializable()> _
            Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
            Public Event FirstNameChanged As FirstNameChangedEventHandler
    
    #End Region
    
    #Region "Private and Public Fields Not Wrapped By Properties (NONE)"
    
            'None
    
    #End Region
    
    #Region "Constructors"
    
            Sub New()
                Me.New("Unknown", "Unknown")
            End Sub
    
            Sub New(ByVal firstName As String, ByVal lastName As String)
    
                If (String.IsNullOrEmpty(firstName)) Then
                    Throw New ArgumentNullException("firstName")
                Else
                    mFirstName = firstName
                End If
    
                If (String.IsNullOrEmpty(lastName)) Then
                    Throw New ArgumentNullException("lastName")
                Else
                    mLastName = lastName
                End If
    
            End Sub
    
            'The CLR calls this special constructor when this (Me) object is deserialized
            'Should have Protected scope if other classes can be inherited from this class
            'Otherwise, if this class is sealed (cannot be inherited from) the scope can be Private
            Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
                SerializationHelpers.ISerializableContructorHelper(info, context, Me)
            End Sub
    
            <SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
            Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) _
              Implements ISerializable.GetObjectData
    
                SerializationHelpers.GetObjectDataHelper(info, context, Me)
    
            End Sub
    
    #End Region
    
    #Region "Instance Public Properties and Their Wrapped Fields"
    
            Private mLastName As String
            Public Property LastName() As String
                Get
                    Return mLastName
                End Get
                Set(ByVal value As String)
                    If Not mLastName = value Then
                        Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
                        Me.OnLastNameChanged(Args)
                        mLastName = value
                    End If
                End Set
            End Property
    
            Private mFirstName As String
            Public Property FirstName() As String
                Get
                    Return mFirstName
                End Get
                Set(ByVal value As String)
                    If Not mFirstName = value Then
                        Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
                        Me.OnFirstNameChanged(args)
                        mFirstName = value
                    End If
                End Set
            End Property
    
    #End Region
    
    #Region "Instance Public Methods"
    
            Public Overrides Function ToString() As String
                Return String.Concat(FirstName, " ", LastName)
            End Function
    
            Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
                RaiseEvent LastNameChanged(Me, e)
            End Sub
    
            Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
                RaiseEvent FirstNameChanged(Me, e)
            End Sub
    
    #End Region
    
    #Region "Static Public Methods and Properties (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Methods In Interfaces (NONE)"
    
            'NONE
    
    #End Region
    
    #Region "Private Helper Methods (NONE)"
    
            'NONE
    
    #End Region
    
        End Class
    
    End Namespace
    First Name Changed Event Args Class:

    Code:
    Namespace Utilities
    
        <Serializable()> _
        Public Class FirstNameChangedEventArgs
            Inherits EventArgs
    
            Sub New(ByVal oldFirstName As String, ByVal newFirstName As String, ByVal sendingPerson As Person)
                mOldFirstName = oldFirstName
                mNewFirstName = newFirstName
            End Sub
    
            Private mOldFirstName As String
            Public ReadOnly Property OldFirstName() As String
                Get
                    Return mOldFirstName
                End Get
            End Property
    
            Private mNewFirstName As String
            Public ReadOnly Property NewFirstName() As String
                Get
                    Return mNewFirstName
                End Get
            End Property
    
            Private mPerson As Person
            Public ReadOnly Property Person() As Person
                Get
                    Return mPerson
                End Get
            End Property
    
        End Class
    
    End Namespace
    The last name changed event args class is similar to the above code

    The Serialization Helpers Class is posted again in the next post because it was not accepted here due to it's size.

  17. #17

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    The Serialization Helpers Class:

    Code:
    Imports System.Runtime.Serialization
    Imports System.Reflection
    
    Namespace Utilities
    
        Public Class SerializationHelpers
    
            ''' <summary>
            ''' Helper routine for serializing objects.
            ''' (Place a call to this sub into the Serializable Classes Protected Overridable Sub GetObjectData)
            ''' i.e. place into: Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
            ''' </summary>
            ''' <param name="info"></param>
            ''' <param name="context"></param>
            ''' <param name="obj"></param>
            ''' <remarks></remarks>
            Public Shared Sub GetObjectDataHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
    
                'Get list of serializable members
                Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
    
                'Read the value of each member
                Dim values() As Object = FormatterServices.GetObjectData(obj, members)
    
                'Store in the SerializationInfo Object, using member name
                For i As Integer = 0 To members.Length - 1
                    info.AddValue(members(i).Name, values(i))
                Next
    
            End Sub
    
            ''' <summary>
            ''' Helper routine for deserializing objects.
            ''' Place a call to this function into the Serializable Constructor; 
            ''' i.e. place into:   Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext))
            ''' </summary>
            ''' <param name="info"></param>
            ''' <param name="context"></param>
            ''' <param name="obj"></param>
            ''' <remarks></remarks>
            Public Shared Sub ISerializableContructorHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
    
                'Get the list of serializable members for this object 
                'excluding members that are of type delegate (filtered)
                'Note that members()(below) contains all types of members from the class passed into this sub via obj (byval obj as Object)
                Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
    
                Dim values(members.Length - 1) As Object
    
                'Read the value for each member
                For i As Integer = 0 To members.Length - 1
    
                    'retrieve the members from members() that are of type field
                    'and then get all field's values
                    Dim member As MemberInfo = members(i)
                    If member.MemberType = MemberTypes.Field Then
                        Dim membertype As Type = CType(member, FieldInfo).FieldType
                        values(i) = info.GetValue(member.Name, membertype)
                    End If
                Next
    
                'assign all serializable members in one operation
                FormatterServices.PopulateObjectMembers(obj, members, values)
    
            End Sub
    
            ''' <summary>
            ''' Return an array of serializable MemberInfo for a given type.
            ''' (Same as FormatterServices.GetSerializableMember, but FILTERS OUT delegate fields.)
            ''' </summary>
            ''' <param name="ty"></param>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Private Shared Function GetFilteredSerializableMembers(ByVal ty As Type) As MemberInfo()
    
                Dim al As New ArrayList
    
                'Get the list of serializable members
                For Each mi As MemberInfo In FormatterServices.GetSerializableMembers(ty)
    
                    'Add the mi object to the arraylist if it is NOT:
                    '(a FieldInfo object) [that IS also (AndAlso)] (found in the inheritance hierarchy of the Delegate class)
                    If Not (TypeOf mi Is FieldInfo AndAlso GetType([Delegate]).IsAssignableFrom(DirectCast(mi, FieldInfo).FieldType)) Then
                        al.Add(mi)
                    End If
                Next
                Return DirectCast(al.ToArray(GetType(MemberInfo)), MemberInfo())
            End Function
    
        End Class
    
    End Namespace

  18. #18

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2006
    Posts
    1,051

    Re: [2005] Problem Serializing

    Due to the low number of replys to my original question in this thread, I assume that either the explanation was too lengthy or that not many people use custom serialization.

    Therefore, I decided to post a complete example others may find useful in the future.

    If anyone finds any errors in this code please let me know and I'll make corrections here for future viewers to see.

    Thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width