Fge
Fge

Reputation: 2961

Serializationexception even with [field:NonSerialized]

I have an IPC-Server running with some really simply interface which is working quite ok for now. The interface itself is really simple and only consists of 4 methods. (RemoteCall Method is SingleTone):

[Serializable]
public class NetworkHook : MarshalByRefObject
{
    public void onIsInstalled(int pid, MessageDirection direction)
    {
    }

    public void onDataRecieved(int pid, MessageDirection direction, byte[][] data)
    {
    }

    public void onException(int pid, MessageDirection direction, Exception exception)
    {
    }

    public void onPing(int pid, MessageDirection direction)
    {
    }
}

Troubles begun when I started to add events to this class (so any part of the server can subscribe to incoming messages, the remote calling method is SingleTone to keep the eventlisteners). As the NetworkHook is serializable I started to add [field: NonSerialzied] tags to my events. That should be fine as I don't need the clients to be aware of the event-listeners (or don't them want to be at all) so losing them is ok:

[field: NonSerialized]
public event EventHandler<InstalledEventArgs> Test;

Anyway I still got this error and tried another approach with a custom eventhandler this time:

public delegate void HookInstalledEventHandler(object sender, HookInstalledEventArgs args);

[field: NonSerialized]
public event HookInstalledEventHandler Test;

And again - i still get the exception when adding listeners to the event. So i tried to override the serialization method of the NetworkHook-Class:

public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{ 

}

As I don't have any variables I need to save this is pretty simple. But again - I get an exception. So now I run out of ideas what I could do prevent this exception, or what do I do wrong here (making the events static is a choice I really don't want to do)?

The exception I get:

System.Runtime.Serialization.SerializationException wurde nicht behandelt.
  Message="Der Typ \"SimpleHookGUI.CommandLineReporter\" in Assembly \"SimpleHookGUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\" ist nicht als serialisierbar gekennzeichnet."
  Source="mscorlib"
  StackTrace:
    Server stack trace: 
       bei System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
       bei System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
       bei System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
       bei System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
       bei System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
       bei System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
       bei System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
       bei System.Runtime.Remoting.Channels.BinaryClientFormatterSink.SerializeMessage(IMessage msg, ITransportHeaders& headers, Stream& stream)
       bei System.Runtime.Remoting.Channels.BinaryClientFormatterSink.SyncProcessMessage(IMessage msg)
    Exception rethrown at [0]: 
       bei System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       bei System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       bei HookManager.NetworkObserver.NetworkHook.add_Test(HookInstalledEventHandler value)
       bei SimpleHookGUI.Program.Main() in xxx\Program.cs:Zeile 24.
       bei System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       bei System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

I tried this for the first answer:

[field: NonSerialized]
private event EventHandler<HookInstalledEventArgs> test;

public event EventHandler<HookInstalledEventArgs> Test
{
    add { this.test = (EventHandler<HookInstalledEventArgs>) Delegate.Combine(this.test, value); }
    remove { this.test = (EventHandler<HookInstalledEventArgs>) Delegate.Remove(this.test, value); }
} 

Upvotes: 3

Views: 1206

Answers (2)

Fge
Fge

Reputation: 2961

After i tried every way to tell the serialization not to serialize my events but they kept on getting serialized I tried it the other way round - give my class NetworkHook what it wants, a serializable class.
So I created a new Test class called NetworkListener which is serializable but doesn't serialize the events:

[Serializable]
public class NetworkListener
{
    private static EventHandler<HookInstalledEventArgs> hookInstalled;

    public event EventHandler<HookInstalledEventArgs> HookInstalled
    {
        add { hookInstalled = (EventHandler<HookInstalledEventArgs>) Delegate.Combine(hookInstalled, value); }
        remove { hookInstalled = (EventHandler<HookInstalledEventArgs>) Delegate.Remove(hookInstalled, value); }
    }

    public void onHookInstalled(Object sender, HookInstalledEventArgs args)
    {
        EventHandler<HookInstalledEventArgs> handler = hookInstalled;
        if (handler != null)
        {
            handler(this, args);
        }
    }
}

This class can subscribe to my NetworkHook : MarshalByRefObject without throwing an exception. I think it really is only a dirty workaround with that static event in there which doesn't bahave static to the outside (beside it's just another class to pass events). But I can't make it private only and flag it with [field: NonSerialized()] as it simply looses all callbacks then. So at least I wanted to get rid of that extra class and include that event-workaround in my original class:

public class NetworkHook : MarshalByRefObject
{
    private static EventHandler<HookInstalledEventArgs> hookInstalled;

    public event EventHandler<HookInstalledEventArgs> HookInstalled
    {
        add { hookInstalled = (EventHandler<HookInstalledEventArgs>) Delegate.Combine(hookInstalled, value); }
        remove { hookInstalled = (EventHandler<HookInstalledEventArgs>) Delegate.Remove(hookInstalled, value); }
    }
    // ....
}

And by surprise - I do get my exception again. I some kind a have the feeling the MarshalByRefObject completly bypasses my serialization-commands ... somehow. Well - this is a really bad solution I think and I'll have to rewrite some other classes to fit in this - but for the first time in hours - it works at all. I anyone has a better solution I'd love to give it another try :) As long I'll stick with my original class calling some "event proxy" which does delegate the real events.

Upvotes: 2

leppie
leppie

Reputation: 117250

event is just sugar for more complex construct.

You need to decompose it for this to work.

It is equivalent to:

private EventHandler eventField;

public event EventHandler SomeEvent
{
  add { eventfield = Delegate.Combine(eventfield, value); }
  remove { eventfield = Delegate.Remove(eventfield, value); }
}

For serialization to work, you need to apply the NonSerialized attribute to the eventField field.

Upvotes: 1

Related Questions