Generic WPF Dispatcher

Howdy! In a medium to large WPF application, it is fairly common required to do some background stuff and update the UI asynchronously. You could use the BackgroundWorker class to do this, you could use the Dispatcher object to do this too. Most of the people know how to use the Dispatcher and for those who do not, this link will surely help http://msdn.microsoft.com/en-us/magazine/cc163328.aspx.

Rather than peppering the Dispatcher code all over the application, it would be nice to have it in a single place. This can be achieved using extension methods on the Dispatcher class. Below is some code which can be used as is to make your Dispatcher generic enough to do most of the stuff.

internal static class TDFDispatcher
{
internal static void Dispatch(this Dispatcher source,Nullable<DispatcherPriority> priority,Action invokedDelegate)
{
if (invokedDelegate != null)
{
if (!source.CheckAccess())
{
source.Invoke(priority.HasValue ? priority.Value : DispatcherPriority.Normal, invokedDelegate);
}
else
{
invokedDelegate();
}
}
}

internal static void Dispatch<T>(this Dispatcher source, Nullable<DispatcherPriority> priority, Action<T> invokedDelegate,T arg)
{
if (invokedDelegate != null)
{
if (!source.CheckAccess())
{
source.Invoke(priority.HasValue ? priority.Value : DispatcherPriority.Normal, invokedDelegate,arg);
}
else
{
invokedDelegate(arg);
}
}
}

internal static TResult Dispatch<T,TResult>(this Dispatcher source, Nullable<DispatcherPriority> priority, Func<T,TResult> invokedDelegate, T arg)
{
TResult result = default(TResult);
if (invokedDelegate != null)
{
if (!source.CheckAccess())
{
result = (TResult)source.Invoke(priority.HasValue ? priority.Value : DispatcherPriority.Normal, invokedDelegate, arg);
}
else
{
result = invokedDelegate(arg);
}
}
return result;
}


You can certainly come up with even more permutations and combinations to enhance this class.



Happy Programming!

Abhang Rane


Creating instance of a type outside the current AppDomain

Howdy Ho! So I am came across a conundrum lately in my project where I had to create instances of a type via reflection. The scenario is, there is a Sample.dll which has a type defined in it MyType. The Sample.dll refers several other 3rd party dll’s like SomeOther1.dll and SomeCrap.dll. I have an executing assembly MyExecutingAssembly.exe residing in folder MyExecutingAssembly. The Sample.dll and its referenced files are located somewhere else in a folder called Sample.

Like a normal stupid developer, I wrote the following code to create the instance in MyExecutingAssembly.exe.

Assembly assembly = Assembly.LoadFrom(C:\Sample\Sample.dll);
Type appType = assembly.GetType(MyType);
object objClassInstance = Activator.CreateInstance(appType);

Boom! Error message for the 3rd line was something like, “Could not find assembly SomeCrap.dll, The assembly used while compiling might be different than that used while loading from …”. Blistering Barnacles, thundering typhoons… Now some straight talk,

The type MyType, resides in the Sample.dll, this dll references 2 other dlls, when the runtime tries so, it searches for the files in the folder where the MyExecutingAssembly.exe resides. This is so because the AppDomain in which we are trying to create the instance has the base directory path of the MyExecutingAssembly. Phew! Hope you get the issue here. There are several ways to tackle this,



Method 1



Create an appdomain, set its base directory to the folder where the Sample.dll and its referenced files reside. But this is some serious implication. Since we are in a separate AppDomain, we are creating the instance in a completely impenetrable environment. This asks for .NET remoting to communicate with the instance. The code would look as below,



Assembly assembly = Assembly.LoadFrom(@"C:\Sample\Sample.dll");
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = @"C:\Sample\";
AppDomain appDomain = AppDomain.CreateDomain("AppHostDomain", null, setup);
object obj = appDomain.CreateInstanceFromAndUnwrap(@"C:\Sample\Sample.dll", "SampleProxy");

Observe that the type I am creating is not the actual MyType but a class called SampleProxy which inherits from MarshalByRefObject. I create instance of the MyType inside this class. Sigh! It seems you did not like this. More information on Suzanne’s blog here.

Method 2 



Recursively check references of all the assemblies and load all of them manually in the current AppDomain. This a bit lengthy process, but, well, it works. Here is the code,



Assembly assembly = Assembly.LoadFrom(@"C:\Sample\Sample.dll");
AssemblyName[] arr = assembly.GetReferencedAssemblies();
LoadAssembly(arr);


private void LoadAssembly(AssemblyName[] arr)
{
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
List<string> names = new List<string>();
foreach (Assembly assem in loadedAssemblies)
{
names.Add(assem.FullName);
}

foreach (AssemblyName aname in arr)
{
if (!names.Contains(aname.FullName))
{
try
{
Assembly loadedAssembly = Assembly.LoadFrom(@"C:\Sample\" + aname.Name + ".dll");
AssemblyName[] referencedAssemblies = loadedAssembly.GetReferencedAssemblies();
LoadAssembly(referencedAssemblies);
}
catch (Exception ex)
{
continue;
}
}
}
}

Tedious eh.

Method 3



Use the AppDomain’s AssemblyResolve event. Basically, whenever you are trying to load assemblies in an AppDomain, if a particular assembly is that the runtime is trying to load fails for some reason, this event is fired. We can tap into this event and load the right assembly instead. Here is the code in short,



Assembly assembly = Assembly.LoadFrom(@"C:\Sample\Sample.dll");
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
object obj = Activator.CreateInstanceFrom(@"C:\Sample\Sample.dll", "MyType");

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = @"C:\Sample\Sample.dll";
string test = path.Substring(0,path.LastIndexOf(@"\"));
string[] arr = args.Name.Split(',');
string assemblyName = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
string newPath = System.IO.Path.Combine(test, assemblyName);
return Assembly.LoadFrom(newPath);
}


Well, a little better I guess. More information on this here.

Happy Programming!

Abhang Rane