locked
SerializationException even when the class is marked as Serializable RRS feed

  • Question

  • Hello..

     

    I have a Windows Forms application that it is using remoting to communicate with a Windows Service. That service uses a third party assembly called SmartThreadPool.

     

    When I run the application, this error is launched when I call a proxy method:

     

    "El tipo 'Amib.Threading.SmartThreadPool' del ensamblado 'SmartThreadPool, Version=1.0.2235.39078, Culture=neutral, PublicKeyToken=null' no está marcado como serializable."

     

    Telling that Amib.Threading.SmartThreadPool isn't marked as serializable. I opened that class source code and I added [Serializable( )] to the SmartThreadPool class. I also tried without the brackets, using only [Serializable]. In either case, the same serializable error occured.

     

    How can I correct it?

     

    This is the code that launches de exception:

     

    Code Snippet

    if (manager == null)

        manager = _remoto.GetExecutionManager();

     

    The _remoto variable is declared and instantiated this way:

     

    Code Snippet

    private OPProxy.MonitorRemote _remoto;

     

    _clientChannel = new TcpClientChannel();

    ChannelServices.RegisterChannel(_clientChannel, true);

    _remoto = (OPProxy.MonitorRemote)Activator.GetObject(typeof(OPProxy.MonitorRemote), String.Format("tcp://localhost:{0}/Monitor", _cfg.RemotePort));

     

    And Monitor Remote clas is implemented this way:

     

    Code Snippet

    public class MonitorRemote : MarshalByRefObject

    {

        private static ProcessHelper.ExecutionManager _execution;

        public static ProcessHelper.ExecutionManager Execution

        {

            get { return MonitorRemote._execution; }

            set { MonitorRemote._execution = value; }

        }

        public ProcessHelper.ExecutionManager GetExecutionManager()

        {

            return MonitorRemote.Execution;

        }

    }

      

    Any help will be greatly appreciated.

     

    Thanks

    Jaime

    Friday, June 15, 2007 2:38 PM

All replies

  • Couple of questions:

     

    1. Could you please attach the exception callstack?

    2. Can you also add the ExecutionManager definition?

     

    Thanks,

    Monday, June 18, 2007 4:52 PM
  • This is the call stack:


    Code Snippet

    Server stack trace:
       en System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
       en System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
       en System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
       en System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
       en System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
       en System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
       en System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
       en System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
       en System.Runtime.Remoting.Channels.BinaryServerFormatterSink.SerializeResponse(IServerResponseChannelSinkStack sinkStack, IMessage msg, ITransportHeaders& headers, Stream& stream)
       en System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)

    Exception rethrown at [0]:
       en System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       en System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       en OPProxy.MonitorRemote.GetExecutionManager()
       en OPMon.Monitor.PopulateProcesses() en D:\Development\WebVida\OrdenProceso\OPMon\Monitor.cs:línea 110
       en OPMon.Monitor.Monitor_Load(Object sender, EventArgs e) en D:\Development\WebVida\OrdenProceso\OPMon\Monitor.cs:línea 55
       en System.Windows.Forms.Form.OnLoad(EventArgs e)
       en System.Windows.Forms.Form.OnCreateControl()
       en System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
       en System.Windows.Forms.Control.CreateControl()
       en System.Windows.Forms.Control.WmShowWindow(Message& m)
       en System.Windows.Forms.Control.WndProc(Message& m)
       en System.Windows.Forms.ScrollableControl.WndProc(Message& m)
       en System.Windows.Forms.ContainerControl.WndProc(Message& m)
       en System.Windows.Forms.Form.WmShowWindow(Message& m)
       en System.Windows.Forms.Form.WndProc(Message& m)
       en System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       en System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       en System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       en System.Windows.Forms.UnsafeNativeMethods.SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam)
       en System.Windows.Forms.Form.SetVisibleCore(Boolean value)
       en System.Windows.Forms.Control.set_Visible(Boolean value)
       en System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       en System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       en System.Windows.Forms.Application.Run(Form mainForm)
       en OPMon.Program.Main() en D:\Development\WebVida\OrdenProceso\OPMon\Program.cs:línea 17
       en System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
       en System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       en Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       en System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       en System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       en System.Threading.ThreadHelper.ThreadStart()

     

     

    And this is the ExecutionManager definition:

     

    Code Snippet

    namespace ProcessHelper
    {
        [Serializable(),
         System.ComponentModel.Description("Administrador de las sesiones para ejecutar")]
        public class ExecutionManager : IDisposable
        {
            #region . Campos .

            static private EventLog _evt;

            private List<Session> _sesiones;
            private int _maxSystem; // Máximo de procesos por sistema. Constraint: _maxSystem >= _maxEngine
            private SmartThreadPool _threadPool;
            static private int _maxEngine; // Máximo de procesos por Motor
            static private Dictionary<int, IWorkItemResult> _threads;
            static private Dictionary<DatabaseEngine, int> _cntEngine;

            #endregion

            #region . Propiedades .

            public List<Session> Sesiones
            {
                get { return _sesiones; }
                set { _sesiones = value; }
            }

            #endregion

            /// <summary>
            /// Construcción del Manager
            /// </summary>
            /// <param name="maxEngine">Cantidad máxima de ejecuciones en un mismo motor de datos</param>
            /// <param name="maxSystem">Cantidad máxima de procesos a ejecutar</param>
            public ExecutionManager(int maxEngine, int maxSystem)
            {
                if (maxEngine > maxSystem)
                    throw new Exception("El número máximo de procesos por motor debe ser menor o igual que el máximo de procesos en total.");

                InitializeEventLog();

                _sesiones = new List<Session>();
                _maxEngine = maxEngine;
                _maxSystem = maxSystem;

                InitializeExecution();
            }

            /// <summary>
            /// Construcción del manager
            /// </summary>
            /// <param name="nsess">Número de sesiones existentes en el sistema</param>
            /// <param name="maxEngine">Cantidad máxima de ejecuciones en un mismo motor de datos</param>
            /// <param name="maxSystem">Cantidad máxima de procesos a ejecutar</param>
            public ExecutionManager(int nsess, int maxEngine, int maxSystem)
            {
                if (maxEngine > maxSystem)
                    throw new Exception("El número máximo de procesos por motor debe ser menor o igual que el máximo de procesos en total.");

                InitializeEventLog();

                _sesiones = new List<Session>(nsess);
                _maxEngine = maxEngine;
                _maxSystem = maxSystem;

                InitializeExecution();
            }

            #region . Métodos .

            private void InitializeEventLog()
            {
                // Si el log de eventos no ha sido inicializado, lo hace en este momento
                if (_evt == null)
                {
                    _evt = new EventLog("Application");
                    _evt.Source = "ProcessHelper";
                }
            }

            /// <summary>
            /// Inserta una sesión dentro del sistema según la prioridad asignada
            /// </summary>
            /// <param name="proceso">Proceso a insertar</param>
            public void InsertSession(Session sesion)
            {
                // Si la ya existe en la cola de ejecución, no la vuelve a cargar
                int ndx = GetSessionIndex(sesion.Identity);
                if (ndx < 0)
                {
                    // Obtiene el índice donde insertar el proceso, dada su prioridad
                    ndx = GetSessionIndex(sesion.Priority);
                    lock (((System.Collections.ICollection)_sesiones).SyncRoot)
                    {
                        _sesiones.Insert(ndx, sesion);
                    }
                }
            }

            /// <summary>
            /// Ejecuta las sesiones pendientes
            /// </summary>
            /// <param name="threads">TRUE para ejecutar los procesos en threads apartes. Si no se hace en threads, la ejecuciión es secuencial, una sesión por vez.</param>
            public void ExecutePending(bool threads)
            {
                // Se bloquea la colección _sesiones para que no se vaya a modificar mientras
                // se encuenta gatillando los threads. De ese modo, no se produce la excepción indicando
                // que la colección fue modificada.
                lock (((System.Collections.ICollection)_sesiones).SyncRoot)
                {
                    // Elimina de la lista las sesiones que no tienen procesos
                    // Comienza desde el final para que no interfiera en la cuenta de elementos dentro de la colección
                    for (int i = _sesiones.Count - 1; i >= 0; i--)
                        if (_sesiones[i].Procesos == null || _sesiones[i].Procesos.Count == 0)
                            _sesiones.RemoveAt(i);

                    // Ejecuta los procesos pendientes en un thread aparte (dentro del thread pool).
                    // Si la ejecución fue exitosa, lo borra de la lista
                    foreach(Session sesion in _sesiones)
                    {
                        // threads indica si la ejecución es multithreaded
                        if (threads)
                        {
                            // Si ya existe la cantidad de procesos suficientes, no lo manda a ejecutar
                            // Sólo manda a ejecutar si existen pendientes dentro de la sesión y no exista ninguno en ejecución.
                            // Con esto se evita que dentro de la misma sesión se ejecuten procesos en "paralelo"
                            if (_threads.Count < _maxSystem && sesion.CanBeExecuted())
                            {
                                // Manda a ejecutar el grupo de procesos (correspondientes a una sesión) en un thread aparte
                                IWorkItemResult wir = _threadPool.QueueWorkItem(new WorkItemCallback(ExecutionManager.AsyncExecuteSession),
                                                      sesion,
                                                      new PostExecuteWorkItemCallback(ExecutionManager.SessionTerminated));
                                _threads[sesion.Identity] = wir;
                            }
                        }
                        else
                            ExecutionManager.SyncExecuteSession(sesion);
                    }
                }
            }

            private bool Exists(int ses_id)
            {
                foreach (Session sesion in _sesiones)
                    if (sesion.Identity == ses_id) return true;

                return false;
            }

            private int GetSessionIndex(int ses_id)
            {
                int ndx = 0;
                foreach (Session sesion in _sesiones)
                {
                    if (sesion.Identity == ses_id) return ndx;
                    ndx++;
                }

                return -1;
            }

            private int GetSessionIndex(ExecutionPriority priority)
            {
                int ndx = 0;
                foreach (Session sesion in _sesiones)
                {
                    if (sesion.Priority < priority) return ndx;
                    ndx++;
                }

                return ndx;
            }

            private void InitializeExecution()
            {
                _threads = new Dictionary<int, IWorkItemResult>(_maxSystem);

                // Inicializa la SmartThreadPool
                _threadPool = new SmartThreadPool(10 * 1000 /* 10 segundos de tiempo Idle */, _maxSystem, 0);
                _cntEngine = new Dictionary<DatabaseEngine, int>(4);
                _cntEngine[DatabaseEngine.Oracle7] = 0;
                _cntEngine[DatabaseEngine.Oracle9] = 0;
                _cntEngine[DatabaseEngine.SQLServer] = 0;
                _cntEngine[DatabaseEngine.Unknown] = 0;
            }

            #endregion

            #region . Ejecución de Threads .

            static Object AsyncExecuteSession(Object state)
            {
                Session sesion = (Session)state;
                DatabaseEngine engine = sesion.PendingEngine;

                // Variable para indicar que se debe ejecutar la sesión.
                // Es necesaria para poder liberar el uso de cntEngine antes de realizar la ejecución y no producir
                // aplazamiento indefinido para los demás procesos que necesitan ser ejecutados en caso de que la
                // ejecución se de demore.
                bool execute = false;
                lock(_cntEngine)
                {
                    if (_cntEngine[engine] < _maxEngine)
                    {
                        _cntEngine[engine]++;
                        execute = true;
                    }
                }

            #if DEBUG
                _evt.WriteEntry("Trató de ejecutar: " + sesion.Identity.ToString() + ", motor : " + engine.ToString() + ", cuenta motor : " + _cntEngine[engine].ToString() + ", cuenta por motor : " + _maxEngine.ToString() + ", execute = " + execute.ToString(), EventLogEntryType.Information);
            #endif

                if (execute)
                {
                    try
                    {
            #if DEBUG || _RELEASE_EVENT
                _evt.WriteEntry("Ejecutando: " + sesion.Identity.ToString() + ", motor : " + engine.ToString() + ", cuenta motor : " + _cntEngine[engine].ToString() + ", cuenta total : " + _threads.Count.ToString() + ", execute = " + execute.ToString(), EventLogEntryType.Information);
            #endif
                        if( !sesion.Execute() )
                _evt.WriteEntry("Ejecución Asíncrona: La sesión no se completó exitosamente.", EventLogEntryType.Error);
                    }
                    catch(Exception ex)
                    {
               _evt.WriteEntry("Ejecución Asíncrona: " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message), EventLogEntryType.Error);
                    }
                }

                return sesion;
            }

            static void SyncExecuteSession(Object sesion)
            {
             try
             {
              ((Session)sesion).Execute();
             }
             catch(Exception ex)
             {
              _evt.WriteEntry("Ejecución Síncrona: " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message), EventLogEntryType.Error);
             }
            }

            static void SessionTerminated(IWorkItemResult wir)
            {
                if (wir.IsCompleted)
                {
                    // Elimina el proceso en ejecución (wir.State contiene la sesión)
                    Session sesion = (Session)wir.State;
              //_eventLog.WriteEntry("[1] Decrementó: " + session->Name + ",  cuenta total : " + _cntTotal.ToString(), System::Diagnostics::EventLogEntryType::Information);
    #if DEBUG || _RELEASE_EVENT
                    DatabaseEngine engine = DatabaseEngine.Unknown;
    #endif
                    if (sesion.LastExecuted != null)
                    {
                        lock(_cntEngine)
                            _cntEngine[sesion.LastExecuted.Engine]--;

    #if DEBUG || _RELEASE_EVENT
                        engine = sesion.LastExecuted.Engine;
    #endif
              }
                    _threads.Remove(sesion.Identity);

            #if DEBUG || _RELEASE_EVENT
              _evt.WriteEntry("Finalizado: " + sesion.Identity.ToString() + ", motor : " + engine.ToString() + ", cuenta : " + _cntEngine[engine].ToString()  + ", cuenta total : " + _threads.Count.ToString(), EventLogEntryType.Information);
            #endif

                    // Sólo si la ejecución fue exitosa elimina la sesión, para que los procesos que siguen pendientes,
                    // sean ejecutados inmediatamente al siguiente ciclo.
              if (sesion.Status == ExecutionStatus.Success)
              {
            #if DEBUG || _RELEASE_EVENT
               _evt.WriteEntry("Removiendo sesión: " + sesion.Identity.ToString(), EventLogEntryType.Information);
            #endif
               sesion.Dispose();
              }
                }
            }

            public void AbortThreads()
            {
                if (_threads != null)
                    foreach(KeyValuePair<int, IWorkItemResult> thread in _threads)
                        if (thread.Value != null && !thread.Value.IsCanceled) thread.Value.Cancel();
            }

            public void AbortThreads(Session sesion)
            {
                if (_threads != null && _threads.ContainsKey(sesion.Identity) && !_threads[sesion.Identity].IsCanceled)
                    _threads[sesion.Identity].Cancel();
            }

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                AbortThreads();

                lock (((System.Collections.ICollection)_sesiones).SyncRoot)
                {
                    if (_sesiones != null)
                    {
                        foreach (Session sesion in _sesiones)
                            sesion.Dispose();

                        _sesiones.Clear();
                        _sesiones = null;
                    }
                }
            }

            #endregion
        }
    }

     

     Thanks

    Jaime

    Monday, June 18, 2007 9:09 PM
  • Are you sure you are getting the exact same exception when adding the attribute to SmartThreadPool? Are you sure all the items in the _sessions variable are also serializable?
    Monday, June 18, 2007 11:45 PM
  • You have marked the SmartThreadPool class as serializable.  Are all of the types down the SmartThreadPool stack also marked as serializable?
    Monday, July 16, 2007 6:53 PM