Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

BinaryFormatter.Deserialize not finding a type of a Plugin


:P
On this page:

I'm working on a plug in for LiveWriter that does screen captures with SnagIt and embeds the content directly into the post. Works great, but now I'm to the point that I want to set up some configuration options and serialize them. I use a few simplified serialization functions in my utility library that make serialization a one line method call and usually this works great.

Here's what the Serialization code to the registry looks like:

/// <summary>
/// Saves the current settings of this object to the registry
/// </summary>
/// <returns></returns>
public bool SaveSettings()
{
    SnagItAutomation Snag = this;
 
    byte[] Buffer = null;
 
    if (!wwUtils.SerializeObject(Snag,out Buffer))
        return false;
 
    RegistryKey SubKey = Registry.CurrentUser.OpenSubKey("Software\\Windows Live Writer\\SnagItScreenCapture");
    if (SubKey == null)
        SubKey = Registry.CurrentUser.CreateSubKey("Software\\Windows Live Writer\\SnagItScreenCapture");
    if (SubKey == null)
        return false;
 
    SubKey.SetValue("ConfigData", Buffer, RegistryValueKind.Binary);
 
    return true;
}
 
/// <summary>
/// Factory method that creates teh SnagItAutomation object by trying to read last capture settings
/// from the registry.
/// </summary>
/// <returns></returns>
public static SnagItAutomation Create()
{
    byte[] Buffer = null;
 
    RegistryKey SubKey = Registry.CurrentUser.OpenSubKey("Software\\Windows Live Writer\\SnagItScreenCapture");
    if (SubKey != null)
        Buffer = SubKey.GetValue("ConfigData",null,RegistryValueOptions.None) as byte[];
 
    if (Buffer == null)
        return new SnagItAutomation();
 
    SnagItAutomation SnagIt = wwUtils.DeSerializeObject(Buffer, typeof(SnagItAutomation)) as SnagItAutomation; 
    if (SnagIt == null)
        return new SnagItAutomation();
 
    return SnagIt;
}

Unforutnately the deserialization code doesn't work at all, due to the fact that the plugin is loading the assemblies out of a non-known path. In other words the deserialization code is failing with:

---------------------------
SnagIt Capture Exception
---------------------------
Failed to capture image:

Unable to find assembly 'SnagitScreenCapture, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
---------------------------
OK
---------------------------

This even though the type is actually loaded in the current AppDomain. The issue is that the assembly lives in a /plugin directory of which the application knows nothing. So the type load fails. I had run into something similar in Help Builder a while back with loading dependent assemblies that were referenced but not signed. The AppDomain supports a AssemblyResolve event that is fired whenever an assembly needs to load. In this code it's possible to explicitly load and return an assembly. In my case the solution is simple and I can simply return the executing assembly:

/// <summary>
/// Factory method that creates teh SnagItAutomation object by trying to read last capture settings
/// from the registry.
/// </summary>
/// <returns></returns>
public static SnagItAutomation Create()
{
    byte[] Buffer = null;
 
 
    RegistryKey SubKey = Registry.CurrentUser.OpenSubKey("Software\\Windows Live Writer\\SnagItScreenCapture");
    if (SubKey != null)
        Buffer = SubKey.GetValue("ConfigData",null,RegistryValueOptions.None) as byte[];
 
    if (Buffer == null)
        return new SnagItAutomation();
 
    // *** Force Assembly resolving code to fire so we can load the assembly
    AppDomain.CurrentDomain.AssemblyResolve +=
        new ResolveEventHandler(CurrentDomain_AssemblyResolve);
 
    SnagItAutomation SnagIt = wwUtils.DeSerializeObject(Buffer,typeof(SnagItAutomation)) as SnagItAutomation;
 
    // *** Unhook the event handler for the rest of the application
    AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);
 
    if (SnagIt == null)
        return new SnagItAutomation();
 
    return SnagIt;
}
 
/// <summary>
/// Handle custom loading of the current assembly if the assmebly won't
/// resolve with the name.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    try
    {
        Assembly assembly = System.Reflection.Assembly.Load(args.Name);
        if (assembly != null)
            return assembly;
    }
    catch { ;}
 
    return Assembly.GetExecutingAssembly();            
}

It works, but it took a while to remember how to do this <s>...

 

You might ask WTF I am writing to the registry for. With Vista's security and the inability to write files into a reasonable location I might as well write into a known store in the registry as opposed to creating a directory and file in Documents which I think is even more intrusive. Ideally I'd write a configuration into the installation directory (the .config file for example), but by default this spot is not writable and scattering this stuff all over the place is just sucky. So the registry it is and simply dumping out a single binary blob is a quick way to get it there.

Before you ask - I'll post this plug-in once get a few more little details worked out...

Posted in Live Writer  

The Voices of Reason


 

Evgeny Egorov
April 16, 2008

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

It works! Thanks!

Troy
May 09, 2008

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

Excellent. This is exactly what I was looking for!

Alec
August 15, 2008

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

Yay, helped me as well.

Piyush
August 14, 2009

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

Thanks for the solution. it really works.

Josh
December 15, 2009

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

Props for the much nicer solution than what most people have out there, and the explanation. This was the biggest WTF of the day for me, but I guess it makes sense.

Rodrigo Caldeira
December 16, 2009

# re: BinaryFormatter.Deserialize not finding a type of a Plugin

Thanks for the post! That's exactly I was looking for.

I'm creating an Windows Control that will be embedded in an website and manipulate images. So, to save the images I'm serializing a class that contains each image opened an its attributes.

Thanks a lot and congratulations!

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024