Hi I am loading dll into another domain, it works fine when loaded into that domain but when i want some information from that domain through proxy object it gives me exception below is the code for review is there any wrong step ???
public class AssemblyProxy
{
System.Type[] _types;
public System.Type[] GetTypes()
{
return _types;
}
public string FullName { get; set; }
public void LoadAssembly(string path)
{
try
{
Evidence evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
AppDomain TestDomain = AppDomain.CreateDomain("AssemblyDomain", evidence, AppDomain.CurrentDomain.BaseDirectory, System.IO.Path.GetFullPath(path), true);
Proxy _asmProxy = (Proxy)TestDomain.CreateInstanceFromAndUnwrap(AppDomain.CurrentDomain.BaseDirectory+"Common.dll", typeof(Proxy).FullName);
_asmProxy.LoadAssembly(path);
FullName = _asmProxy.FullName;
_types = _asmProxy.GetTypes(); //Here i got Exception [Can not load file or assembly]
AppDomain.Unload(TestDomain);
}
catch (Exception ex)
{
}
}
}
class Proxy : MarshalByRefObject
{
System.Type[] _types;
public string FullName { get; set; }
public System.Type[] GetTypes()
{
return _types;
}
public void LoadAssembly(string path)
{
System.Reflection.Assembly _assembly = System.Reflection.Assembly.Load(System.IO.File.ReadAllBytes(path));
_types = _assembly.GetTypes();
FullName = _assembly.FullName;
}
}
The exception I get is:
Can not load file or assembly
The way I solved this problem was by calling LoadFrom (not Load) and in the context of the AppDomain:
sDomain = AppDomain.CreateDomain(DOMAIN_NAME);
sDomain.DoCallBack(AppDomainCallback);
// runs in the context of the AppDomain
private void AppDomainCallback()
{
Assembly assembly = Assembly.LoadFrom(mAssemblyName);
}
I Have solved the issue by reading following Blog Post: the problem in my case is that i am returning System.Type Object from new domain which is no allowed you can return strings from proxy object but not System.Type object
Link
Related
I have created a new domain, then loaded the assembly into this domain, but when GetTypes() gives an error like the picture attached, hope everyone helps, thanks.
Code
public class Program
{
public static void Main()
{
string assemblyPath = #"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";
AppDomain appDomain = CreateChildDomain(AppDomain.CurrentDomain, Guid.NewGuid().ToString());
appDomain.AssemblyResolve += AssemblyResolve;
var value = (Proxy)appDomain.CreateInstanceAndUnwrap(typeof(Proxy).Assembly.FullName, typeof(Proxy).FullName);
var assembly = value.GetAssembly(assemblyPath);
var types = assembly.GetTypes();
Console.ReadKey();
}
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
string dependentAssemblyFilename = Path.Combine(#"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug", assemblyName.Name + ".dll");
if (File.Exists(dependentAssemblyFilename)) return null;
return Assembly.LoadFile(dependentAssemblyFilename);
}
public static AppDomain CreateChildDomain(AppDomain parentDomain, string domainName)
{
Evidence evidence = new Evidence(parentDomain.Evidence);
AppDomainSetup setup = parentDomain.SetupInformation;
return AppDomain.CreateDomain(domainName, evidence, setup);
}
}
public class Proxy : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
return Assembly.LoadFile(assemblyPath);
}
catch { return null; }
}
}
Error
Have you checked if BeyConsRevitProject.dll assembly is in the bin directory of your application? This is a possible cause. Try deleting the bin/ and obj/ folders and rebuilding your solution, if the error persists, use this code below to ascertain the real reason for the error:
using System.IO;
using System.Reflection;
using System.Text;
try
{
//The code that causes the error goes here.
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (exFileNotFound != null)
{
if(!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
//Display or log the error based on your application.
}
This code was suggested by Ben Gripka here:
Error message 'Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.'
I'm having difficulties trying to run a .dll in a new AppDomain. My object is always of type System.MarshalByRefObject, so I cannot get the methods from the plugin.
What I have right now, is a plugin, that implements an interface and extending a MarshalByRefObject, looking like this:
public interface IPlugin
{
string Name { get; }
string Description { get; }
string Author { get; }
void Execute();
}
Then I have my plugin implemented like this:
[Serializable]
public class IPPlugin : MarshalByRefObject, IPlugin
{
public string Author
{
get
{
return "John John";
}
}
public string Description
{
get
{
return "description";
}
}
public string Name
{
get
{
return "name";
}
}
public void Execute()
{
//do stuff here
}
}
So I built the plugin, got the dll, placed it in a folder and now in my project I'm trying to load it like this:
AppDomain domain = AppDomain.CreateDomain("PluginDomain");
Object obj = domain.CreateInstanceFromAndUnwrap(path + "\\" + plugins[option].getAssemblyName(), plugins[option].getTypeName());
Console.WriteLine(obj.GetType());
if (RemotingServices.IsTransparentProxy(obj))
{
Type type = obj.GetType();
MethodInfo Execute = type.GetMethod("Execute");
Execute.Invoke(obj, null); //crashes here
}
But it crashes on Execute.Invoke(...), because it doesn't know the method Execute, since the object is of wrong type.
Error message is:
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
I need to load .dll(plugins) in another domain. In main app I don't know anything about plugins types, only that they implement common interface ICommonInterface with some methods. So this code wouldn't help, because I can't create an instance with interface type.
AppDomain domain = AppDomain.CreateDomain("New domain name");
//Do other things to the domain like set the security policy
string pathToDll = #"C:\myDll.dll"; //Full path to dll you want to load
Type t = typeof(TypeIWantToLoad);
TypeIWantToLoad myObject = (TypeIWantToLoad)domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName);
My question is how I can load assembly in new domain and get the instance, if I know only interface name which implements type I want to create.
UPDATE:
Here is my code:
MainLib.dll
namespace MainLib
{
public interface ICommonInterface
{
void ShowDllName();
}
}
PluginWithOutException.dll
namespace PluginWithOutException
{
public class WithOutException : MarshalByRefObject, ICommonInterface
{
public void ShowDllName()
{
Console.WriteLine("PluginWithOutException");
}
}
}
PluginWithException.dll
namespace PluginWithException
{
public class WithException : MarshalByRefObject, ICommonInterface
{
public void ShowDllName()
{
Console.WriteLine("WithException");
throw new NotImplementedException();
}
}
}
And main application:
static void Main(string[] args)
{
string path = #"E:\Plugins\";
string[] assemblies = Directory.GetFiles(path);
List<string> plugins = SearchPlugins(assemblies);
foreach (string item in plugins)
{
CreateDomainAndLoadAssebly(item);
}
Console.ReadKey();
}
public static List<string> SearchPlugins(string[] names)
{
AppDomain domain = AppDomain.CreateDomain("tmpDomain");
domain.Load(Assembly.LoadFrom(#"E:\Plugins\MainLib.dll").FullName);
List<string> plugins = new List<string>();
foreach (string asm in names)
{
Assembly loadedAssembly = domain.Load(Assembly.LoadFrom(asm).FullName);
var theClassTypes = from t in loadedAssembly.GetTypes()
where t.IsClass &&
(t.GetInterface("ICommonInterface") != null)
select t;
if (theClassTypes.Count() > 0)
{
plugins.Add(asm);
}
}
AppDomain.Unload(domain);
return plugins;
}
Plugins and main app have reference to MainLib.dll. The main aim is to not to load assemblies in default domain, but load them to another domains, so when I don't need them, I just Unload() domain and unload all plugins from application.
For now the exception is FileNotFoundException, Could not load file or assembly 'PluginWithException, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.) on string Assembly loadedAssembly = domain.Load(Assembly.LoadFrom(asm).FullName);(I trying to load plugin with name PluginWithException), I've delete all the dependencies in plugins, exept System, I loaded System.dll in this domain(it loaded correct and it is in domain), but still cant load plugins into domain. Also I checked, that PluginWithException has 2 dependencies - mscorlib and MainLib, and all of them loaded to this domain.
UPDATE: Here I asked this question with more details.
I'm not sure if it's what you need, i'd try to help you with this.
This is how I do to load plugin assemblies. I use a helper class to manage new AppDomain and the instance of the class on that assembly. This is the helper class:
[Serializable, ClassInterface(ClassInterfaceType.AutoDual)]
class helperDomain<T>: MarshalByRefObject where T: class
{
#region private
private AppDomain _app_domain;
private AppDomainSetup _app_domain_info;
private string _assembly_class_name;
private string _assembly_file;
private string _assembly_file_name;
private T _inner_class;
private bool _load_ok;
private string _loading_errors;
private string _path;
#endregion
#region .ctor
public helperDomain(string AssemblyFile,
string configFile = null, string domainName)
{
this._load_ok = false;
try
{
this._assembly_file = AssemblyFile; //full path to assembly
this._assembly_file_name = System.IO.Path.GetFileName(this._assembly_file); //assmbly file name
this._path = System.IO.Path.GetDirectoryName(this._assembly_file); //get root directory from assembly path
this._assembly_class_name = typeof(T).ToString(); //the class name to instantiate in the domain from the assembly
//start to configure domain
this._app_domain_info = new AppDomainSetup();
this._app_domain_info.ApplicationBase = this._path;
this._app_domain_info.PrivateBinPath = this._path;
this._app_domain_info.PrivateBinPathProbe = this._path;
if (!string.IsNullOrEmpty(configFile))
{
this._app_domain_info.ConfigurationFile = configFile;
}
//lets create the domain
this._app_domain = AppDomain.CreateDomain(domainName, null, this._app_domain_info);
//instantiate the class
this._inner_class = (T) this._app_domain.CreateInstanceFromAndUnwrap(this._assembly_file, this._assembly_class_name);
this._load_ok = true;
}
catch (Exception exception)
{
//There was a problema setting up the new appDomain
this._load_ok = false;
this._loading_errors = exception.ToString();
}
}
#endregion
#region public properties
public string AssemblyFile
{
get
{
return _assembly_file;
}
}
public string AssemblyFileName
{
get
{
return _assembly_file_name;
}
}
public AppDomain AtomicAppDomain
{
get
{
return _app_domain;
}
}
public T InstancedObject
{
get
{
return _inner_class;
}
}
public string LoadingErrors
{
get
{
return _loading_errors;
}
}
public bool LoadOK
{
get
{
return _load_ok;
}
}
public string Path
{
get
{
return _path;
}
}
#endregion
}
and then load plugins (each in a diferent folder).
foreach(string pluginassemblypath in pluginspaths)
{
//Each pluginassemblypath (as it says..) is the full path to the assembly
helperDomain<IPluginClass> isoDomain =
helperDomain<IPluginClass>(pluginassemblypath,
pluginassemblypath + ".config",
System.IO.Path.GetFileName(pluginassemblypath) + ".domain");
if (isoDomain.LoadOK)
{
//We can access instance of the class (.InstancedObject)
Console.WriteLine("Plugin loaded..." + isoDomain.InstancedObject.GetType().Name);
}
else
{
//Something happened...
Console.WriteLine("There was en error loading plugin " +
pluginassemblypath + " - " + helperDomain.LoadingErrors);
}
}
Hope it will helps you...
This question seems relevant to what you want to do.
How to Load an Assembly to AppDomain with all references recursively?
After you've loaded the assembly, you can use Assembly.GetTypes() and iterate to find the types that implement your interface.
I want to decompile a method in my assembly, which references e.g. 'Microsoft.SharePoint.dll', but I have no SharePoint installed on the machine.
If I use the following code I get an Mono.Cecil.AssemblyResolutionException 'failed to resolve assembly Microsoft.SharePoint...'.
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("PathToMyAssembly");
ICSharpCode.Decompiler.DecompilerContext context = new ICSharpCode.Decompiler.DecompilerContext(assembly.MainModule);
AstBuilder decompiler = new AstBuilder(context);
decompiler.AddMethod(method); <!-- here it crashes -->
With the ILSpy GUI I can load my assembly without errors (on the same machine without SharePoint).
What do I need to change in my code?
I found the solution myself. I created my own AssemblyResolver which catches the AssemblyResolutionException and returns null for missing referenced assemblies.
public class MyDefaultAssemblyResolver : DefaultAssemblyResolver
{
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
try
{
return base.Resolve(name);
}
catch { }
return null;
}
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
try
{
return base.Resolve(name, parameters);
}
catch { }
return null;
}
public override AssemblyDefinition Resolve(string fullName)
{
try
{
return base.Resolve(fullName);
}
catch { }
return null;
}
public override AssemblyDefinition Resolve(string fullName, ReaderParameters parameters)
{
try
{
return base.Resolve(fullName, parameters);
}
catch { }
return null;
}
}
Then I use it
var resolver = new MyDefaultAssemblyResolver();
resolver.AddSearchDirectory("FolderOfMyAssembly");
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("PathToMyAssembly", parameters);
I found the solution by debugging the ILSpy GUI (source code is fortunately available).
I got that error
Could not load file or assembly
'TranslationFormsApplication.TranslationForm.SavedData>' or one of its
dependencies. The parameter is incorrect. (Exception from HRESULT:
0x80070057 (E_INVALIDARG)
when I debug my code in C#. The SavedData is a class name inside another class.
Below is the sample code
namespace TranslationFormsApplication
{
partial class TranslationForm
{
private class SavedData
{
public SavedData(int id, string s, string t)
{
index = id;
source = s;
translation = t;
}
private int m_index;
public int index { get { return m_index; } set { m_index = value; }
}
}
}
The error stack when I tried to open the form designer looks like this:
at System.IO.Path.CheckInvalidPathChars(String path)
at System.IO.Path.Combine(String path1, String path2)
at Microsoft.VisualStudio.Platform.VsAppDomainManager.d__1.MoveNext()
at Microsoft.VisualStudio.Platform.VsAppDomainManager.InnerResolveHandler(String name)
at Microsoft.VisualStudio.Platform.VsAppDomainManager.ResolveHandler(Object sender, ResolveEventArgs args)
at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
I looked at the x86/Debug/ folder and there is indeed no TranslationFormsApplication.TranslationForm.SavedData file.
Since I use Windows 7, I know we're supposed to clean the temporary ASP.net files in
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\
And all other versions
and also in
C:\Windows\Microsoft.NET\Framework\
And also in
C:\Users\Username\AppData\Tmp\
I have done that, actually there is no "Temporary ASP.NET Files" that I can find of. I have also deleted the bin/Debug and obj86/Debug folder but it doesn't help.
All the access to the class are done inside the TranslationForm class. Below are the examples:
private Dictionary textIndexes;
private void InitializeComponent()
{
this.textIndexes = new Dictionary<int,SavedData>();
}
private void accept_Click(object sender, System.EventArgs e)
{
SavedData data;
if (textIndexes.ContainsKey(selectedIndex))
{
data = textIndexes[selectedIndex];
data.source = sourceEdit.Text;
data.translation = transEdit.Text;
}
else
{
data = new SavedData(selectedIndex, sourceEdit.Text, transEdit.Text);
textIndexes.Add(selectedIndex, data);
}
}
private void saveTrainingFiles()
{
foreach (KeyValuePair<int, SavedData> line in textIndexes)
{
SavedData data = line.Value;
sw.WriteLine(data.source);
sw2.WriteLine(data.translation);
}
textIndexes.Clear();
}
When I changed the code to use tuple instead of class SavedData, I got the following warning:
Warning 1 The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
The error stack when I tried to open FormDesigner is:
1. Hide Call Stack
at System.Reflection.AssemblyName.nInit(RuntimeAssembly& assembly, Boolean forIntrospection, Boolean raiseResolveEvent)
at System.Reflection.AssemblyName..ctor(String assemblyName)
at Microsoft.VisualStudio.Design.VSTypeResolutionService.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, ReferenceType refType)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.AggregateTypeResolutionService.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.AggregateTypeResolutionService.GetType(String name)
at System.ComponentModel.Design.Serialization.DesignerSerializationManager.GetRuntimeType(String typeName)
at System.ComponentModel.Design.Serialization.DesignerSerializationManager.GetType(String typeName)
at System.ComponentModel.Design.Serialization.DesignerSerializationManager.System.ComponentModel.Design.Serialization.IDesignerSerializationManager.GetType(String typeName)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.GetType(IDesignerSerializationManager manager, String name, Dictionary`2 names)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.FillStatementTable(IDesignerSerializationManager manager, IDictionary table, Dictionary`2 names, CodeStatementCollection statements, String className)
at System.ComponentModel.Design.Serialization.TypeCodeDomSerializer.Deserialize(IDesignerSerializationManager manager, CodeTypeDeclaration declaration)
at System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager manager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager serializationManager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio.TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted(Int32 fReload)
I guess it has something to do with the versioning of .NET and also using 64 bit machine. Any idea how to resolve it with and without using the class?
[SavedData class should be public to be accessed like TranslationFormsApplication.TranslationForm.SavedData
public class SavedData
[UPDATE]:
On another note, shouldn't your code be:
namespace TranslationFormsApplication
{
partial class TranslationForm
{
public class SavedData
{
public SavedData(int id, string s, string t)
{
index = id;
source = s;
translation = t;
}
private int m_index;
private string m_source;
private string m_translation;
public int index { get { return m_index; } set { m_index = value; } }
public string source { get { return m_source; } set { m_source = value; } }
public string translation { get { return m_translation; } set { m_translation = value; } }
}
}
}