C# cast derived class to base class exception via reflection - c#

I have an app that creates classes dynamically using reflection. When deployed, I get an exception when casting a derived class to its base class. It only happens on 1 in 100 machines. All the classes are in the same assembly. Below are some code snippets and output from a logging message just prior to the casting exception. I'm at my wits end, any help greatly appreciated.
//Parent class
namespace Framework.DataModel
{
[Serializable]
public class DataTreeRequest : TreeNode, IDirtyListener, ISerializable
{
....
}
}
// Derived Class
namespace Framework.DataModel
{
[Serializable]
public class CADElementRequest : DataTreeRequest
{
public CADElementRequest(String name) : base(name){}
}
}
// Method that uses reflection to create class and then cast to its base class
namespace Framework.DataModel
{
[Serializable]
public class DataModelBuilder : CoreBuilder
{
...
protected DataTreeRequest CreateDataTreeRequest(String asmName, String inName, String inType, String inSourceName)
{
DataTreeRequest dtr = null;
Assembly asm = Assembly.LoadFrom(asmName);
if (asm == null)
{
throw new BaseException("Can't find assembly " + asmName);
}
Type requestType = asm.GetType(inType);
if (requestType == null)
{
throw new BaseException("Can't find class of type " + inType + " in assembly " + asmName);
}
// Call the constructor for the tree node that takes the xml node as an argument
Type[] constructorArgsTypes = new Type[1];
constructorArgsTypes[0] = typeof(String);
ConstructorInfo constructorInfo = requestType.GetConstructor(constructorArgsTypes);
if (constructorInfo == null)
{
throw new BaseException("Can't find constructor for type " + inType + " that takes a String param");
}
Object[] constructorArgs = new Object[1];
constructorArgs[0] = inName;
Object newObj = constructorInfo.Invoke(constructorArgs);
// Code fails on this line trying to cast derived class to base class on 1 in 100 machines
dtr = newObj as DataTreeRequest;
if (dtr == null)
{
throw new BaseException("Can't cast newObj to type DataTreeRequest. newObj = " + newObj + ", Type = " + newObj.GetType().ToString());
}
dtr.InSource = inSourceName;
return dtr;
}
}
}
Logging output on failed machine:
Message = Found assembly=Framework.DataModel, Version=1.0.5885.31486,
Culture=neutral, PublicKeyToken=null
Message = newObj
AssemblyQualifiedName=Framework.DataModel.CADElementRequest,
Framework.DataModel, Version=1.0.5885.31486, Culture=neutral,
PublicKeyToken=null, BaseType==Framework.DataModel.DataTreeRequest,
FullName==Framework.DataModel.CADElementRequest
BaseException: Can't cast newObj to type DataTreeRequest. newObj =
Name=Substations;InType=;InName=Substations;OutName=Substations;InSource=;OutSource=;,
Type = Framework.DataModel.CADElementRequest

try to replace
Assembly asm = Assembly.LoadFrom(asmName);
if (asm == null)
{
throw new BaseException("Can't find assembly " + asmName);
}
Type requestType = asm.GetType(inType);
with
Type requestType = Type.GetType(inType)
where inType is assembly qualified name
https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx
If you need load assembly that is not referenced by project consider using Assembly.Load method.
About disadvantages of using Assembly.LoadFrom read Remarks section in https://msdn.microsoft.com/EN-US/library/1009fa28(v=VS.110,d=hv.2).aspx

the red flag is it fails on specific machines (1 out of 100) - how many machines in total does the code fail on? This indicates that it might be the configuration of the machine rather than the code. It also makes it extremely difficult to replicate in order to help.
If it were me. I'd take a step back and simplify the situation. Write code that just does the failing task, even with other simpler classes, and then focus on a failing machine. Get as much information as possible and build up understanding.
It's likely to be referent pain. The situation your facing is only a symptom of the real problem. You're focused on the code but that's just a symptom. That's why it would be good to simplify.
Hope this helps, I'm new to stackoverflow and I know there are rules to follow for questions. I'd have made this a comment but I don't have the reputation for that.

Related

Newtonsoft Json.NET InvalidCastException

This is more of a tech support question, but the newtonsoft.com website says stackoverflow is the best place to ask questions.
I have a string that was serialized with 6.0.3, and fails to deserialize with 6.0.8. It throws InvalidCastException. When I downgrade to 6.0.3, it deserializes fine; when I upgrade again to 6.0.8, the exception is repeatable.
Edit:
Rather than post the actual string, which is 10KB long and contains sensitive information, I was able to create a simple reproducible test case that demonstrates the problem.
The line that throws exception is:
this.SomeStrings = (string[])infoEnum.Current.Value;
The InvalidCastException says "Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'System.String[]'"
As mentioned in the comments below, I serialized an instance of FooClass with 6.0.3, and then hard-coded the string into the asdf() method and attempt to deserialize. Deserialization succeeds on 6.0.3, and fails with InvalidCastException on 6.0.8.
Obviously, in the trivial repro case below, there is no point in doing ISerializable on FooClass, but in real life I have a need to use ISerializable in a complex data type that serializes and deserializes itself as a string array; the following is just to academically illustrate reproduction of the bug behavior.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using System.Runtime.Serialization;
namespace CBClasses
{
[Serializable]
public class FooClass : ISerializable
{
public string[] SomeStrings { get; set; }
public FooClass() { }
protected FooClass(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new ArgumentNullException();
SerializationInfoEnumerator infoEnum = info.GetEnumerator();
while (infoEnum.MoveNext()) {
SerializationEntry entry = infoEnum.Current;
switch (entry.Name) {
case "SomeStrings":
this.SomeStrings = (string[])infoEnum.Current.Value;
break;
default:
throw new SerializationException("Deserialization failed on unhandled member '" + entry.Name + "'");
}
}
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("SomeStrings", this.SomeStrings, this.SomeStrings.GetType());
}
}
public class NewtonsoftDebugTest
{
private static JsonSerializerSettings settings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
public static void asdf()
{
/* FooClass foo = new FooClass() { SomeStrings = new string[0] };
* string serializedBy603 = JsonConvert.SerializeObject(foo, settings);
* Resulted in:
*
* {
* "$id": "1",
* "$type": "CBClasses.FooClass, CBClasses",
* "SomeStrings": {
* "$type": "System.String[], mscorlib",
* "$values": []
* }
* }
*
* Now hard-coded below:
*/
string serializedBy603 =
"{\n" +
" \"$id\": \"1\",\n" +
" \"$type\": \"CBClasses.FooClass, CBClasses\",\n" +
" \"SomeStrings\": {\n" +
" \"$type\": \"System.String[], mscorlib\",\n" +
" \"$values\": []\n" +
" }\n" +
"}\n";
FooClass deserialized = (FooClass)JsonConvert.DeserializeObject(serializedBy603, settings);
System.Diagnostics.Debugger.Break();
}
}
}
I did some investigation into this and can confirm that the problem first appeared in version 6.0.7 and is still reproducible with the latest release (9.0.1 as of this writing). The change appears to have been made as part of the commit to "Support reference preservation for ISerializable objects" from November 4, 2014. According to the source code diffs, the following code in the CreateISerializable() method of the JsonSerializerInternalReader class was changed from this:
if (reader.TokenType == JsonToken.StartObject)
{
// this will read any potential type names embedded in json
object o = CreateObject(reader, null, null, null, contract, member, null);
serializationInfo.AddValue(memberName, o);
}
else
{
serializationInfo.AddValue(memberName, JToken.ReadFrom(reader));
}
to this:
serializationInfo.AddValue(memberName, JToken.ReadFrom(reader));
It seems pretty clear that the former code used to handle the embedded type metadata, whereas the replacement code does not. And, in fact, I can confirm that reverting this one section of code back to its original state fixes the problem. However, without knowing the intent of this change (maybe the metadata was supposed to be handled somewhere else in the code?), I can't recommend blindly reverting it, as that might break something else. The current serialization code still adds the type metadata as before (I get the same JSON as posted in the question), so this definitely seems to be a regression on the deserialization end. If you haven't already, you might want to report an issue on GitHub. (And yes, I do realize this question is over a year and a half old; I'm just trying to provide some closure here. ;-))
As a workaround, you can extract the string array from the SerializationEntry like this:
this.SomeStrings = ((JObject)entry.Value)["$values"].ToObject<string[]>();

Running a runtime compiled C# script in a sandbox AppDomain

My application should be scriptable by the users in C#, but the user's script should run in a restricted AppDomain to prevent scripts accidentally causing damage, but I can't really get it to work, and since my understanding of AppDomains is sadly limited, I can't really tell why.
The solution I am currently trying is based on this answer https://stackoverflow.com/a/5998886/276070.
This is a model of my situation (everything except Script.cs residing in a strongly named assembly). Please excuse the wall of code, I could not condense the problem any further.
class Program
{
static void Main(string[] args)
{
// Compile the script
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters parameters = new CompilerParameters()
{
GenerateExecutable = false,
OutputAssembly = System.IO.Path.GetTempFileName() + ".dll",
};
parameters.ReferencedAssemblies.Add(Assembly.GetEntryAssembly().Location);
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "Script.cs");
// ... here error checks happen ....//
var sandbox = Sandbox.Create();
var script = (IExecutable)sandbox.CreateInstance(results.PathToAssembly, "Script");
if(script != null)
script.Execute();
}
}
public interface IExecutable
{
void Execute();
}
The Sandbox class:
public class Sandbox : MarshalByRefObject
{
const string BaseDirectory = "Untrusted";
const string DomainName = "Sandbox";
public static Sandbox Create()
{
var setup = new AppDomainSetup()
{
ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
ApplicationName = DomainName,
DisallowBindingRedirects = true,
DisallowCodeDownload = true,
DisallowPublisherPolicy = true
};
var permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());
return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
}
public object CreateInstance(string assemblyPath, string typeName)
{
new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
var assembly = Assembly.LoadFile(assemblyPath);
CodeAccessPermission.RevertAssert();
Type type = assembly.GetType(typeName); // ****** I get null here
if (type == null)
return null;
return Activator.CreateInstance(type);
}
}
The loaded Script:
using System;
public class Script : IExecutable
{
public void Execute()
{
Console.WriteLine("Boo");
}
}
In CreateInstance of SandBox, I always get null at the marked line. I tried various forms of giving the name, including reading the type name (or fuly qualified name) from results.CompiledAssembly using reflection.
What am I doing wrong here?
The first thing that i'll check is if there are compilation errors (i had several headache caused by this issues)
The second idea is about the resolution of assemblies. I always add as a security check an event handler for AppDomain.CurrentDomain.AssemblyResolve, where i seek on my known path for the missing Assemblies. When the not found assembly is the one i just compiled i add a static reference to it and return it.
What I usually do is this:
Create the new Assembly on file system with the compiler
Load its content with the File.ReadAllBytes
Load the dll with the Assembly.Load in the AppDomain in which i will be using the object
Add the AppDomain.CurrentDomain.AssemblyResolve event
Just in case (since i use this a lot) i created a small library to accomply this kind of things
The code and documentation are here: Kendar Expression Builder
While the nuget package is here: Nuget Sharp Template

Type.GetType() returning null [duplicate]

This question already has answers here:
Type.GetType("namespace.a.b.ClassName") returns null
(17 answers)
Closed 7 years ago.
I have a web application that dynamically creates a web page using usercontrols.
Within my code I have the following:
private void Render_Modules()
{
foreach (OnlineSystemPageCustom.OnlineSystemPageHdr.OnlineSystemPageModule item in custompage.Header.Modules)
{
if (item.ModuleCustomOrder != 99 && !item.ModuleOptional)
{
string typeName = item.ModuleInternetFile;
Type child = Type.GetType(typeName);
webonlinecustombase ctl = (webonlinecustombase)Page.LoadControl("../IPAM_Controls/webtemplatecontrols/" + child.Name.ToString() + ".ascx");
ctl.Event = Event;
ctl.custompage = custompage;
ctl.custommodule = item;
this.eventprogrammodules.Controls.Add(ctl);
}
}
}
The "typeName" that is being returned (example) is:
IPAMIntranet.IPAM_Controls.webtemplatecontrols.eventorgcommittee
The namespace for the user controls is as follows:
namespace IPAMIntranet.IPAM_Controls
The problem I am having is that Type.GetType(typeName) is returning null. What am I missing here?
Type.GetType(string) only looks in the currently executing assembly and mscorlib when you don't specify the assembly name within the string.
Options:
Use the assembly-qualified name instead
Call Assembly.GetType(name) on the appropriate assembly instead
If you have an easy way of getting hold of the relevant assembly (e.g. via typeof(SomeKnownType).Assembly) then the second option is probably simpler.
Type.GetType looks as the calling assembly, and a few system assemblies. For anything else, you must either use assemblyInstance.GetType(typeName), or you must use the "assembly qualified name" of the type, which includes the assembly details in which the type can be found. Otherwise, it wont be found, and will return null. You can get that from:
string aqn = someType.AssemblyQualifiedName;
I had a very similar problem to the original poster, except that I needed to instantiate the code-behind class of my custom user-control in a static utility class rather than an ASPX page, so LoadControl wasn't available to me. Here's what I ended up doing:
public static class Utils
{
public static string MyFunc(string controlClassName)
{
string result = "";
// get a list of all assemblies in this application domain
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
// the trouble is that we don't know which assembly the class is defined in,
// because we are using the "Web Site" model in Visual Studio that compiles
// them on the fly into assemblies with random names
// -> however, we do know that the assembly will be named App_Web_*
// (http://msdn.microsoft.com/en-us/magazine/cc163496.aspx)
foreach (Assembly assembly in assemblies)
{
if (assembly.FullName.StartsWith("App_Web_"))
{
// I have specified the ClassName attribute of the <%# Control %>
// directive in the relevant ASCX files, so this should work
Type t = assembly.GetType("ASP." + controlClassName);
if (t != null)
{
// use reflection to create the instance (as a general object)
object o = Activator.CreateInstance(t);
// cast to the common base type that has the property we need
CommonBaseType ctrl = o as CommonBaseType;
if (ctrl != null)
{
foreach (string key in ctrl.PropertyWeNeed)
{
// finally, do the actual work
result = "something good";
}
}
}
}
}
return result;
}
}
It's not pretty and not very efficient, and is liable to break if the App_Web_* naming convention changes (although you could then just look through all of them): but it does work ...

ILSpy, how to resolve dependencies?

I want to disassemble an entire .NET assembly with ILSpy.
I used this code as base:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx
And it works fine, just when I have an assembly that references Npgsql.dll (or any other non-gac assembly), then I get an AssemblyResolutionException.
Failed to resolve assembly: 'Npgsql, Version=2.0.11.92, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7'
I know how I can get the referenced assemblies, but how can I add them to ast ?
// SqlWebAdmin.Models.Decompiler.DecompileAssembly("xy.dll");
public static string DecompileAssembly(string pathToAssembly)
{
//Assembly assembly = Assembly.LoadFrom(pathToAssembly);
System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
//assembly.GetReferencedAssemblies();
//assembly.GetReferencedAssemblies(assembly);
Mono.Cecil.AssemblyDefinition assemblyDefinition =
Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);
ICSharpCode.Decompiler.Ast.AstBuilder astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);
//new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
using (System.IO.StringWriter output = new System.IO.StringWriter())
{
astBuilder.GenerateCode(new ICSharpCode.Decompiler.PlainTextOutput(output));
string result = output.ToString();
return result;
}
return "";
} // End Function DecompileAssembly
You need to tell Cecil, the underlying metadata reader that ILSpy is using, where your assemblies are. You can write:
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory("path/to/my/assemblies");
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters);
This is the most natural way to tell Cecil where to resolve referenced assemblies. This way you can remove the line where you load the assembly using System.Reflection, and only use the ILSpy stack.
This is improved #Nayan answer. If you want to ignore missing assemblies, copy this class:
using Mono.Cecil;
public class IgnoringExceptionsAssemblyResolver : DefaultAssemblyResolver
{
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
try
{
return base.Resolve(name);
}
catch
{
return null;
}
}
}
and use it like that:
var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters() {
AssemblyResolver = new IgnoringExceptionsAssemblyResolver()
});
In addition to what JB Evain suggested, this code will help in avoiding the exception. All you have to do is handle the exception in resolver.
Not the best way, I admit. But it works for this scenario: "If I am decompiling a DLL on a system where the referred assemblies are not present, the decompilation fails (with exception.) At least, i would like to see the decompile code, for whatever has been resolved."
using System;
using System.Collections.Generic;
using Mono.Cecil;
public class MyAssemblyResolver : BaseAssemblyResolver
{
private readonly IDictionary<string, AssemblyDefinition> cache;
public MyAssemblyResolver()
{
this.cache = new Dictionary<string, AssemblyDefinition>(StringComparer.Ordinal);
}
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
if (name == null)
throw new ArgumentNullException("name");
AssemblyDefinition assemblyDefinition = null;
if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
return assemblyDefinition;
try //< -------- My addition to the code.
{
assemblyDefinition = base.Resolve(name);
this.cache[name.FullName] = assemblyDefinition;
}
catch { } //< -------- My addition to the code.
return assemblyDefinition;
}
protected void RegisterAssembly(AssemblyDefinition assembly)
{
if (assembly == null)
throw new ArgumentNullException("assembly");
string fullName = assembly.Name.FullName;
if (this.cache.ContainsKey(fullName))
return;
this.cache[fullName] = assembly;
}
}
And use it like this:
var rp = new Mono.Cecil.ReaderParameters() { AssemblyResolver = new MyAssemblyResolver() };
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
var astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(
new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);
I would actually like to see an enhancement in the decompiler: it currently ignores the ReaderParameters that user sets, in DefaultAssemblyResolver class.
Usage:
var rp = new Mono.Cecil.ReaderParameters();
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
Current DefaultAssemblyResolver code:
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
AssemblyDefinition assemblyDefinition;
if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
{
return assemblyDefinition;
}
assemblyDefinition = base.Resolve(name); // <---------
// Is the `ReaderParameters` object set by user, used to resolve in `base` class?
this.cache[name.FullName] = assemblyDefinition;
return assemblyDefinition;
}
Based on the Mono.Cecil source, I would guess that you could probably handle this using the Mono.Cecil.DefaultAssemblyResolver class.
Instead of this code:
Mono.Cecil.AssemblyDefinition assemblyDefinition =
Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);
try this:
Mono.Cecil.AssemblyDefinition assemblyDefinition =
new Mono.Cecil.DefaultAssemblyResolver().Resolve(System.Reflection.AssemblyName.GetAssemblyName(pathToAssembly).ToString());
EDIT
While my original suggestion may or may not work (I've never done it, so no guarantees), you may want to look into the Mono.Addins.CecilReflector.dll assembly from the Mono.Addins project to help mitigate these sort of problems. It is also based on Mono.Cecil (just as ILSpy is) so even though the general premise that Mono.Addins is an extensibility library doesn't meet your needs it may contain some code use for your purposes or at least learn from.

Reflection - Can't create a new object instance C#

I am trying to create a plug-in type archetecure for my project. I would like the ability to load an assembly, get a type that is derived from an abstract base class in my project, instantiate it and load that derived type into the main processing object.
My problem right now is that when I instantiate the object from the reflected assembly, it is always null. I feel that the problem may lie in the fact that the referenced assembly has 3rd party dlls that it is using. Here is the code: the only exception that gets hit is the final one.
static void Main(string[] args)
{
string engineFilePath = ConfigurationManager.AppSettings["EngineFilesDirectory"]
+ "\\" + ConfigurationManager.AppSettings["EngineDllFileName"];
Assembly asm = Assembly.LoadFile(engineFilePath);
Type engineType = asm.GetType(ConfigurationManager.AppSettings["EngineType"]);
if (!engineType.IsSubclassOf(typeof(EngineConcrete)))
{
throw new ArgumentException("Engine is not derived from base implimentation.");
}
object engineInstance = asm.CreateInstance(engineType.Namespace + "." + engineType);
if (engineInstance == null)
{
//always thrown at this point
throw new Exception(string.Format("Engine object is null."));
}
return;
}
If I change the instantiation line to Activator.CreateInstance(engineType), I receive an error saying that one of the 3rd party dll's being referenced by the reflected assembly can't be found, though they are in the same directory as the .dll being reflected.
There is a public constructor for the type that is being reflected as well. It has no parameters and inherits from the EngineConcrete class.
I think null is what you get when the type can't be found; the namespace name plus the type name may not be sufficient (strong naming issues). What happens if you put the 3rd-party dlls in the directory of the executing application, not the directory of the plugin?
This is the bug: engineType.Namespace + "." + engineType — this expression evaluates into "The.Namespace.The.Namespace.Type" instead of "The.Namespace.Type". Either use engineType.FullName or the Activator.CreateInstance class and use the engineType directly.
Update: Note that MSDN says “… or null if typeName is not found.” about Assembly.CreateInstance's return value.
The issue is that the CLR is not finding the third-party assemblies because they are not in a folder that is being probed.
You could add an event handler for the AppDomain.CurrentDomain.AssemblyResolve event to handle those cases. Below is an example implementation that worked for me where assemblies are being dynamically loaded from a "Plugins" folder that is contained within the bin folder:
class Program
{
static string _PluginDirectory;
static string PluginDirectory
{
get
{
if (_PluginDirectory == null)
{
_PluginDirectory = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), #"Plugins");
}
return _PluginDirectory;
}
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
return Assembly.LoadFile(System.IO.Path.Combine(PluginDirectory, assemblyName.Name + ".dll"));
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
var asm = Assembly.LoadFile(System.IO.Path.Combine(PluginDirectory, #"Plugin.dll"));
var transmogrifier = asm.CreateInstance("Plugin.ConcreteTransmogrifier") as Common.Transmogrifier;
if (transmogrifier != null)
{
Console.WriteLine(transmogrifier.Transmogrify("Wowzers!"));
}
}
}

Categories

Resources