Plugin Problem; Unable to find assembly 'xyz.dll'
I have a plugin base app with a directory structure like this:
..\AppDir\App.exe // App directory
..\AppDir\Plugins\plugin xyz.dll // plugin directory
The main app uses the following method to save form settings and user preferences.
static object GetSetting(string key) { ... }
GetSettings retrieves the byte[] from the database and deserializes the object. This works great for all forms and settings within the main app. Even the plugins are able to save to the database no problem. My problem is when a plugin tries to retrieve its saved custom typed object from the database I get an error 'Unable to find assembly 'Plugin xyz.dll''
When I do an AppDomain.CurrentDomain.GetAssemblies(), the specified 'Plugin xyz.dll' is in the list so I am not sure what to do.
If I copy 'Plugin xyz.dll' to the ..\AppDir sometimes it works, and sometimes I get a different error. Unable to case type 'x.y.z' to 'x.y.z'
The only work-around to this problem I have been able to think up is to return the byte[] to the plugin and force the plugin to do its own de-serialization.
Any suggestions would be greatly appreciated.
[1186 byte] By [
gbarcalo] at [2007-12-28]
I am still unable to deserialize an object of a type that is not referenced in my application. If I copy the plugin to my application directory I can then deserialize the object but I cant use it. When I try to cast the deserialized object to 'Namespace.Type' I get an error indicating that I cannot cast a type of 'Namespace.Type' to 'Namespace.Type'. (Because they are from two different files? The file in the /Plugins trying to do the casting, and the file in the AppDir which was used to do the deserialization?)
Also I am unable to deserialize the byte[] from within the non-reference dll. I imagin that the Deserializer's access to the unreferenced dll is the problem and not where the deserialization occurs.
Though when I check AppDomain.Current.GetAssemblies() it does contain a reference to my pluing (non-referenced dll).
Any tips would be appreciated.
Ok..... So I finally figured it out, and this is how its done.
So there are a few things that need to be addressed in order to deserialize an object referenced by a .dll that was loaded at runtime.
First, I found everything I needed to accomplish the task here:
http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder.aspx
But a more abbreviated version to address my particular problem is as follows:
** Please forgive me for spelling mistakes this is writted in Notepad (You should be able to get the gist)
1. The custom class to be deserialized must implement the System.Runtime.Serialization.ISerializable
2. You must create a System.Runtime.Serialization.SerializationBinder
3. As part of the ISerializable implimentation you must include a specific constructor that matches the method implimented by ISerializable.
4. Only the outer most class to be deserialized needs to have the following code (Other custom classes contained within the class that was serialized dont need any additional code. (At least in my case they didn't)Sample:
// Inner class needs no modification
internal class CustomClassInner
{
public string firstName;
public string lastName;
}
internal class CustomClass : ISerializable // Class referenced by a plugin dll loaded @ runtime
{
public CustomClass()
{
// do whatever you want here
}
internal CustomClass(SerializationInfo info, StreamingContext context)
{
// This constructor is required to use 'ISerializable'
// Can be internal or public but I went with internal
try
{
x = info.GetInt32("x");
y = info.GetInt32("y");
names = (CustomClassInner)info.GetValue("names", typeof(CustomClassInner));
}
catch (SerializationException)
{
// some or all of this object could not be deserialized
}
}
public int x;
public int y;
public CustomClassInner names;
// To deserialize
public static CustomClass Deserialize(byte[] serializedCustomClass, SerializationBinder)
{
using (MemoryStream ms = new MemoryStream())
{
ms.Read(serializedCustomClass, 0, serializedCustomClass.Length);
ms.Position = 0;
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new DeserializationBinder();
return bf.Deserialize(ms) as CustomClass;
}
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("x", x);
info.AddValue("y", y);
info.AddValue("names", names);
}
#endregion
internal sealed class DeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
// Make sure you have a line here for every custom class that is referenced by 'CustomClass'
if (typeName == typeof(CustomClass).ToString())
return typeof(CustomClass);
else if (typeName == typeof(CustomClassInner).ToString())
return typeof(CustomClassInner);
else
return Type.GetType(typeName);
}
}
}