Deserialize - unable to find an Assembly .....
Hi,
I've got a really strange error. I'm using a fairly standard method to implement the IClonable interface, basically I serialize my object to a memory stream and then stream it back again.
This all works fine in the .Net runtime environment and if I use a test application. However, I need to run as a COM control inside of an HTML page (long story, but I am embedding it in the home page pane of Outlook).
Anyway everything works fine except this routine. It fails when trying to deserialize with "Assembly not found". I added some debug code to load the assembly dynamically and that all works fine. It can't be a version issue as I am serializing and deserializing the same object in the same method.
I've traced where the framework is loading the assembly and it looks pretty simple:
internalstaticAssemblyLoadAssemblyFromStringNoThrow(string assemblyName) {try {returnFormatterServices.LoadAssemblyFromString(assemblyName); }catch (Exception) { }returnnull; }Any clues would be greatly appreciated. I really don't want to have to add my assembly to the GAC if I can help it.
Regards,
LJ
Code:
publicobject Clone(){
BinaryFormatter bFormatter =newBinaryFormatter();MemoryStream stream =newMemoryStream();bFormatter.Serialize(stream,
this);stream.Seek(0,
SeekOrigin.Begin);//Debugging code --
Type ThisType =this.GetType();System.Windows.Forms.
MessageBox.Show("Assembly = " + ThisType.Assembly.FullName.ToString());Assembly ass =Assembly.Load(ThisType.Assembly.FullName.ToString());System.Windows.Forms.
MessageBox.Show(ass.ToString());//Debugging code --
BusinessObjectBase
NewObject = (BusinessObjectBase)bFormatter.Deserialize(stream);return NewObject;}
Error:
************** Exception Text **************
System.Runtime.Serialization.SerializationException: Unable to find assembly 'Testing.CMSSearch, Version=1.0.2498.28872, Culture=neutral, PublicKeyToken=null'.
at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at Testing.CMSSearch.Base_Classes.BusinessObjectBase.Clone() in C:\Data\DEVELOP\CMSSearch\Control\Base Classes\BusinessObjectBase.cs:line 176
at Testing.CMSSearch.Presentation.SearchControl.lstViews_SelectedValueChanged(Object sender, EventArgs e) in C:\Data\DEVELOP\CMSSearch\Control\Presentation\SearchControl.cs:line 126
at System.Windows.Forms.ListControl.OnSelectedValueChanged(EventArgs e)
at System.Windows.Forms.ListBox.OnSelectedValueChanged(EventArgs e)
at System.Windows.Forms.ListBox.OnSelectedIndexChanged(EventArgs e)
at System.Windows.Forms.ListBox.WmReflectCommand(Message& m)
at System.Windows.Forms.ListBox.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
Right, I think you can serialize and desrialize from same application but you can't serialize in one and deserialize in second! Right?
For this 2 work you need to have a same assembly (Version must also be same). You need to create a seperate DLL to Serialize and Desrialize object and in any application use the same dll to work fine. Since Serialization process puts some type of water mark in serialized object itself so when desializating it checks for the same Assembly with the same version which serialized this object. If it is the same then it desrializes it successfully else raise the exception you are getting.
I hope you understand the concept and know what to do now!
Best Regards,
Rizwan
You could also serialise as xml which won't embed the assembly version.
Your assembly *really* ought to be in the GAC. But it might work if you stick it into the Internet Explorer folder. "c:\program files\internet explorer" last time I looked. It might work too if you put it in, shudder, c:\windows\system32 or any other folder that's on your system path.
Thanks for the help, apologies for not seeing your replies earlier but my post got moved and I thought it had been deleted. Only found it again 'cause I'm having the same problem in another bit of code now ....
I understand the versioning issue but I am serializing and deserializing the same object (in memory) so there definately won't be a version issue.
What I don't understand is that the code works fine when run standalone, so it must be something to do with the way I am running (as a COM control in an HTML page). What's even stranger is the fact that it quite happily manages to serialize the object, stick it in memory and only fails when deserializing.
Presumably the deserialize is somehow running in a different context or appdomain which cannot find my assembly - despite the fact I am calling deserialize from the assembly that contains the class I want to deserialize my object into. I don't understand why deserialize would not try to look in/use the currently loaded assembly ? Is it just because assembly load is in a different (framework) assembly ? I can happily load it by calling Assembly.Load on the line before I do the deserialize.
Thanks again,
LJ
I understand the versioning issue but I am serializing and deserializing the same object (in memory) so there definately won't be a version issue.
Me and nobugs did not talk about the version of the object you want to serialize deseialize but we have mentioned that Serlizing Code Deserializing code should be in same assembly and it should be used on both sides to get results without exception.
Just give it a try and you'll see all will be fine!
Best Regards,
RizwanSharp,
Thanks again for taking the time to reply.
The serializing and deserializing is in the same assembly - in fact it's in the same method. I'm using it to clone an object and the code is inside the class of the object I want to clone as part of the ICloneable interface.
I'm not sure what you mean by "it should be used on both sides". I am not using remoting or web services so there is only one side, one assembly and indeed one method.
The method serializes this object to memory, deserializes it to a new object and then returns it.
The whole method (without any debug code) looks like this:
public object Clone()
{
BinaryFormatter bFormatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
bFormatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
BusinessObjectBase NewObject = (BusinessObjectBase)bFormatter.Deserialize(stream);
return NewObject;
}
That is all there is to it ! It fails on this line:
BusinessObjectBase NewObject = (BusinessObjectBase)bFormatter.Deserialize(stream);
It only fails if my assembly is running as a COM control hosted in a browser (or Outlook). I should point out that this assembly is a windows control library. It works absolutely fine if I run it within a test form.
As I said if I try and load the assembly myself in the above method then it works fine. However it seems that the Assembly.Load in the framework used by deserialize is not finding it. As I said there is only one assembly - the one it is failing to load is the same assembly which contains the class and the Clone method.
It's almost like the framework has lost context or something.
Apologies if I am missing the point you're trying to make.
Thanks,
LJ
Ahh John,
I thought you are using 2 assemblies; one for serializing and second for deserializing (should create a problem). but your scenerio is OK and should work. Unfortunately I have not tried it on Web page may be there is some problem in. I would recomend trying what Nobugs said. Just give it a try and see if it works!
Applogies for the confusions!
Best Regards & Best of Luck,
Rizwan
RizwanSharp,
Thanks for your help, you may be correct & nobugs' suggestion may solve my problem - unfortunately it's not a workaround I can use in the Live environment. I just wish I knew what the difference was between me doing Assembly.Load successfully and the framework doing it and failing.
LJ.
Still getting nowhere with this....
Does anyone know any way of supplying the assembly to use for deserialization the BinaryFormatter, or telling it where to look ?
Have you tried moving the assembly into the c:\windows\system32 folder? If that works, you could have your setup program add the folder that contains the assembly to the search path. That's about as ugly as using the system32 folder (not quite). You still haven't given us a good reason why you can't put it in the GAC... Thanks again nobugz.
I don't really want to put it in the GAC because it's not a shared assembly, and I won't be putting it in System32 as that would obviously be a bad thing.
I really just want to understand why it's going wrong and then I can make a decision on the best workaround.
Why is the load behaviour different in the framework when I run as a COM control to what it is if I just run in a winform, and why can I load the damn assembly myself but the binary formatter can't ? Why does the assembly search process differ ?
Is it because I am running inside the browser process (or Outlook) the reason that the framework can't find my assembly as it's not in the directory of the loaded process (although it is in memory) ?
Is it something to do with the way BinaryFormatter works, is it crossing threads or appdomains or something ?
If I can work out the reason then it will help me to avoid similar problems later on, and improve my understanding ....
LJ
You still haven't answered the question: does it work when you move the assembly into c:\windows\system32? We need to know the answer to diagnose the problem.
Hi nobugz,
It makes no difference if I copy it to System32 or add my bin folder to the path environment variable. However it does work if I copy the contents of my bin folder to the Office folder, - bear in mind that this is a user control dll wrapped up as an ActiveX control running in the "home page" (browser window) inside Outlook. So it runs inside of the Outlook process.
It also works if I add all of the assemblies to the GAC.
It makes perfect sense that it will work if I move it to somewhere that the .Net runtime can find it. What I don't understand is why it cannot find the assembly given that it is already loaded into memory.
According to msdn, .the runtime "Checks whether the assembly name has been bound to before and, if so, uses the previously loaded assembly. If a previous request to load the assembly failed, the request is failed immediately without attempting to load the assembly."
Thanks again,
LJ.
Hi John,
I was running into the same problem already twice. As far I could find out is the cause of this that binary formatter loads types dynamically form the assemblies in the application directory and does not check loaded assemblies before. Instead it tries the find the assemblies which information’s are saved in the serialized file in the startup directory (or better AppDomain.CurrentDomain.BaseDirectory). If you create an instance by COM it is set to the to the directory where the COM executable is located and of cause your .Net assemblies are not there. If you check the description of the BinaryFormatter.Binder property and the description SerializationBinder – class you will find answers. You can work around if you replace the binder by one the gets types by loaded assemblies:
public class Binder : SerializationBinder {
public override Type BindToType(string assemblyName, string typeName) {
Type tyType = null;
string sShortAssemblyName = assemblyName.Split(',')[0];
Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly ayAssembly in ayAssemblies) {
if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
tyType = ayAssembly.GetType(typeName);
break;
}
}
return tyType;
}
}
}
Before you call Deserialize just set the binder – property of your BinaryFormatter instance = new Binder(). It is working as long the objects you want to deserialize are defined in the same assembly where you deserialize from, if not then same porblem will be caused though creation of instances of the deserialized types inside the binary formatter. I could not find a solution for this.