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...
Other Posts you might also like