Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.Runtime.Serialization.SerializationException: Unable to find assembly MyAssembly

So I've found a bunch of threads on this topic but I don't think I've found one that applies yet.

Basically my .exe loads a .dll (MyAssembly) file which does the serialization and loading. Obviously it serializes quite fine.

But when I go to deserialize the file within the MyAssembly.dll file it explodes with the error in the title of this post.

Anyone have any ideas? I don't understand how it can't find the assembly that is calling the code!

My code:

// deserialize


 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     var Obj = bin.Deserialize(target);
     if (Obj != null)
     {
         ObjectToStore = (ObjectTypeInMyAssembly)Obj;
     }
 }

// serialize
 using (var target = new System.IO.FileStream(Path, System.IO.FileMode.OpenOrCreate))
 {
     var bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
     bin.Serialize(target, ObjectToStore);
 }
like image 290
Geesu Avatar asked Feb 20 '13 22:02

Geesu


3 Answers

Is the DLL in the same folder like the EXE?
I see you serialize/deserialize a object that lives in the DLL ("MyAssembly"). When deserialize, the formatter determines the name of the type from serialized data, and tries to find this type in the assembly at the main executable folder, that is- EXE folder.
Solution- move the DLL to EXE folder. There is a way to cause the formatter to search in another assembly, capture the event AppDomain.AssemblyResolve and return your DLL. See MSDN.

like image 62
RoadBump Avatar answered Nov 18 '22 21:11

RoadBump


Well, I used a trick that worked!

sealed class CustomizedBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type returntype = null;
        string sharedAssemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
        assemblyName = Assembly.GetExecutingAssembly().FullName;
        typeName = typeName.Replace(sharedAssemblyName, assemblyName);
        returntype =
                Type.GetType(String.Format("{0}, {1}",
                typeName, assemblyName));

        return returntype;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        base.BindToName(serializedType, out assemblyName, out typeName);
        assemblyName = "SharedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
    }
}

use the binder for binary formatters, like this:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();
like image 41
Sean Ed-Man Avatar answered Nov 18 '22 20:11

Sean Ed-Man


I'd like to build on Sean Ed-Man's answer, which is good, but doesn't work in my case.

If you can instantiate the class but the BinaryFormatter can't resolve it, this may work for you.

In my case, the calling assembly (PluginAssembly for this example) is being run as a plugin from the executable, in the form of a zip file. For some reason I can directly resolve the class (from NeededAssembly) when instantiating, but the BinaryFormatter can't resolve it. NeededAssembly is, of course, included as a reference to the PluginAssembly project, which is why I can instantiate. I don't know why the BinaryFormatter is different.

Regardless, this is what worked for me:

public class PluginAssembly
{
    // (class code here)

    private sealed class CustomizedBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type returntype = null;
            if (typeName.StartsWith("NeededAssembly.RemoteClient.MessagePayload"))
            {
                returntype = typeof(MessagePayload);
            }
            else if (typeName.StartsWith("NeededAssembly.RemoteClient.ResultsPayload"))
            {
                returntype = typeof(ResultsPayload);
            }
            else if (typeName.Equals("System.Collections.Generic.List`1[[NeededAssembly.ShortResult, NeededAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"))
            {
                returntype = typeof(List<ShortResult>);
            }
            else
            {
                returntype =
                        Type.GetType(String.Format("{0}, {1}",
                        typeName, assemblyName));
            }
            return returntype;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            base.BindToName(serializedType, out assemblyName, out typeName);
            if (serializedType.ToString().Contains("NeededAssembly"))
            {
                assemblyName = typeof(MessagePayload).Assembly.FullName;
            }
        }
    }
}

Of course, don't forget to use it:

BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new CustomizedBinder();

Basically I simply get a typeof for the needed class, which works.

like image 2
klugerama Avatar answered Nov 18 '22 19:11

klugerama