I tried Assembly.ReflectionOnlyLoadFrom(#"path\System.Core.dll") and ReflectionOnlyLoad but i got exceptions and errors. How do i properly get all the namespaces/classes in an assembly?
For example i got this exception.
Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
To load an assembly and then get a list of all types:
Assembly assembly = Assembly.ReflectionOnlyLoadFrom("System.Core.dll");
Type[] types = assembly.GetTypes();
Unfortunately this will throw an exception if any of the types exposed cannot be loaded, and sometimes this load failure cannot be avoided. In this case however the thrown exception contains a list of all types that were successfully loaded and so we can just do this:
Assembly assembly = Assembly.ReflectionOnlyLoadFrom("System.Core.dll");
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
types = ex.Types;
}
This will give you a list of all types, including interfaces, structs, enums etc... (If you want just the classes then you can filter that list).
If you can reference System.Core then
List<string> namespaces = new List<string>();
var refs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
foreach (var rf in refs) {
if (rf.Name == "System.Core")
{
var ass = Assembly.Load(rf);
foreach (var tp in ass.GetTypes())
{
if (!namespaces.Contains(tp.Namespace))
{
namespaces.Add(tp.Namespace);
Console.WriteLine(tp.Namespace);
}
}
}
}
If you cannot, you will need to attach to the AssemblyResolve event of the CurrentDomain and load all assemblies of types that System.Core.dll uses when loading the dll.
Here is your answer to your question.
I do not need to copy & paste it here for you, it might be greener to save space rather that copying code from the other thread. :-)
Related
I have a List of type ITemp. ITemp is a interface.
List<ITemp> temp = new List<ITemp>();
Now what I have to do is to read all the dlls from Specific file Location and add that into this temp list
so what I am doing is this :
List<Assembly> allAssemblies = new List<Assembly>();
string path = Path.GetDirectoryName("C:\\TemplatesDLL\\");
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
allAssemblies.Add(Assembly.LoadFile(dll));
}
foreach (Assembly assembly in allAssemblies)
{
var DLL = Assembly.LoadFile(assembly.Location.ToString());
foreach (var t in DLL.GetTypes())
{
Activator.CreateInstance(t);
var constructors = t.GetConstructors();
temp.Add(t.GetConstructors()); // here I have to add all the dll that implements ITemp interfaces
}
}
adding the interface something like this if its in the same project
temp.Add(new TestTemp());
here TestTemp is a C# file in the same project. Now I move this TestTemp in to a DLL file. And Read it from the same application and add into the list.
Thanks in Advance.
I got it.
List<Assembly> allAssemblies = new List<Assembly>();
string path = Path.GetDirectoryName("C:\\TemplatesDLL\\");
List<ITemp> temp = new List<ITemp>();
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
allAssemblies.Add(Assembly.LoadFile(dll));
}
foreach (Assembly assembly in allAssemblies)
{
var DLL = Assembly.LoadFile(assembly.Location.ToString());
Type[] types = DLL.GetTypes();
if (typeof(ITemp).IsAssignableFrom(types[0]))
{
temp.Add(Activator.CreateInstance(types[0]) as ITemp); //adding all the instance into the list that is what I was looking for.
}
}
I actually needed to do this recently. This example only gets the first type, but you could easily modify it to return a list and iterate the list.
var dll = Assembly.LoadFile(#"C:\SimplePlugin.dll");
Type pluginType = dll.GetExportedTypes()
.Where(t => typeof(SimplePluginContracts.ISomePlugin).IsAssignableFrom(t)
&& !t.IsInterface).First();
SimplePluginContracts.ISomePlugin plugin =
(SimplePluginContracts.ISomePlugin)Activator.CreateInstance(pluginType);
This question covers variations on this, and the top answer has a good discussion of nuances of this technique that my simple example doesn't handle:
Getting all types that implement an interface
You might consider also filtering out non-public classes, in case your DLL has some classes not intended to be loaded which also implement the interface.
I am currently developing client-server application.
It should load interfaces from modules and show them inside of its own window.
But sometimes I need to plugin remote module.
Can I run form from module (with all actions working) without loading module file on disk?
Thank you.
Yes, you can load an assembly sent from a remote computer (I will not discuss here security implications of this, I'd - at least - check for a signature):
var data = new WebClient.DownloadData(url); // For example...
var assembly = Assembly.Load(data);
In C++/CLI (it's not clear in your question what's language you're using):
array<Byte>^ data = (gcnew WebClient())->DownloadData(url);
Assembly^ assembly = Assembly::Load(data);
Now you have assembly and you can load something from it, for example (just for illustration):
var plugins = assembly.GetExportedTypes()
.Where(x => typeof(IYourContract).IsAssignableFrom(x) && !x.IsAbstract)
.Select(x => (IYourContract)Activator.CreateInstance(x));
Please note that this is a very naive implementation because each instance will be different (if you load same plugin multiple times) and it's also expansive in terms of resources (primary memory). You should keep an assembly cache:
private static Dictionary<string, Assembly> _cachedAssemblies =
new Dictionary<string, Assembly>();
public static Assembly LoadRemoteAssembly(string url)
{
lock (_cachedAssemblies)
{
if (_cachedAssemblies.ContainsKey(url))
return _cachedAssemblies[url];
var data = new WebClient.DownloadData(url); // For example...
var assembly = Assembly.Load(data);
_cachedAssemblies.Add(url, assembly);
return assembly;
}
}
I'm having trouble while getting the value of the text property in a non-executing assembly; I read an assembly from disk via reflection, then i get all classes in the assembly to search for the Text property in a windows form class which is initialized by win forms designer. So far i have the following code:
static void Main(string[] args)
{
Assembly asm = Assembly.LoadFrom(Path.Combine(path, "Assembly.exe"));
PropertyInfo[] props;
foreach (Type t in asm.GetTypes())
{
var value = t.GetProperty("Text").GetValue(/*Not sure what to put here*/)
}
}
And this is how the designer generated the form
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
Me.BackColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(128, Byte), Integer), CType(CType(128, Byte), Integer))
Me.ClientSize = New System.Drawing.Size(234, 181)
Me.Cursor = System.Windows.Forms.Cursors.Default
Me.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.ForeColor = System.Drawing.SystemColors.WindowText
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
Me.Location = New System.Drawing.Point(581, 222)
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "winform"
Me.RightToLeft = System.Windows.Forms.RightToLeft.No
Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
Me.Text = "Title"
Me.fraDÃas.ResumeLayout(False)
Me.ResumeLayout(False)
Keep in mind that the assembly is on disk and non-executing and that I want to retrieve the value of the Text property of every winform (I guess it should be somewhere hardcoded in the assembly since it was generated by the winforms designer)
Please tell me if this is possible, thanks!
Your requirements are contradictory, when you load an aseembly via reflection, and instantiate an object or try to get a property value, what happens is that some code begins to run, there is no way around that.
Remember that properties are just "syntax sugar" for a pair of methods, the getter and setter. Their current value is nothing but the value returned by the getter method, and when you change its value, you're in fact calling its setter method. So, to retrieve property values, you must make some code to run, even if it's a trivial get method.
I think maybe your confusion comes from the fact that you're using a designer to create the form. Particularly with the WinForms designer (WPF for instance is substantially different), all it does is to autogenerate some code for you. Setting properties, placing and moving controls around, what's happening under the hood is that it writes code that replicate your actions at runtime, specifically, it codes the InitializeComponent method. The real property value is set when the constructor is called (that in turn calls InitializeComponent), and then you may read/change using many properties.
What you would need to read those designer attributes is that those were hardcoded in some form of metadata, so that it's simply read as data and not as the result of code execution. That's not the case with WinForms, as it "saves" the form as code.
You cannot read a property of a class that has not been instantiated! The parameter you are missing is an instance of your type.
You must create an instance of the type with object o = Activator.CreateInstance(type); before accessing its members (unless they are static).
Your problem is related to how add-ins (plug-ins) can be loaded at runtime.
Here is how I made an Add-In Loader. Below, I will explain how you can adapt it to your problem. Add-Ins have to implement the IAddIn interface in my example. You are totally free in the definition of IAddIn. You could define it like this:
public interface IAddIn
{
bool OnLoad();
string Version { get; set; }
string Text { get; set; }
}
This allows you to access members without reflection.
public class AddInLoader
{
// Loads all non abstract types implementing IAddIn in all DLLs found in a folder.
public IList<IAddIn> Load(string folder)
{
var addIns = new List<IAddIn>();
string[] files = Directory.GetFiles(folder, "*.dll");
foreach (string file in files) {
addIns.AddRange(LoadFromAssembly(file));
}
return addIns;
}
// Loads all non abstract types implementing IAddIn found in a file.
private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
string addInInterfaceName = typeof(IAddIn).FullName;
foreach (Type type in asm.GetExportedTypes()) {
Type interfaceType = type.GetInterface(addInInterfaceName);
if (interfaceType != null &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract){
IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
addIn.Version = asm.GetName().Version.ToString();
yield return addIn;
}
}
}
}
Now you can load and access the add-ins like this:
var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(folderPath);
foreach (IAddIn addIn in addIns) {
if (addIn.OnLoad()) {
Console.WriteLine("Version = {0}, Text = {1}", addIn.Version, addIn.Text);
}
}
Reading the titles of Forms at runtime:
You can easily adapt this example. Instead of searching for types implementing an interface, search for types deriving from System.Windows.Forms.Form.
private static IEnumerable<Form> LoadFormsFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
foreach (Type type in asm.GetExportedTypes()) {
if (typeof(Form).IsAssignableFrom(type) &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
Form form = (Form)Activator.CreateInstance(type);
yield return form;
}
}
}
Now you can get the texts of the forms like this:
var forms = LoadFormsFromAssembly(path);
foreach (Form frm in forms) {
Console.WriteLine(frm.Text);
}
Note: You must instantiate the forms, however you do not need to open (show) them. The code works only if the forms have a default constructor, i.e. a constructor without parameters.
You need an instance object for that type to get the value of a property.
It looks like you just want to check if a type has a "Text" property or not. You can to it by checking
bool hasTextProperty = t.GetProperty("Text") !=null;
I have some domain classes having a property of type Type (the class).
The user can select any class loaded in his project using a custom TypeBrowserEditor.
The serialization works fine, I serialize as myType.AssemblyQualifiedName
But during the deserialization, Type.GetType(str) returns null since the assembly isn't loaded in the app domain.
I can't do AssemblyLoad(str) because he won't find the file.
I need to have access to the IVSHierarchy and enumerate through the References of the user's project. But i can't find a way to have access to it in my DomainPropertyXmlSerializer. If anyone can point me to the right direction for a service provider or anyway to let me connect to the VS current project it would be great.
Thanks in advance.
Edit : I could worst case scenario only work with String and just cast it in my type editor since i can have access to the IVSHierarchy there but I don't really like that solution.
Ok I managed to do it !
In the DslPackage, make a DocData.cs, and create a part for the MyLanguageDocData class (partial)
then in it :
protected override void OnDocumentLoading(EventArgs e)
{
mRes = new ResolveEventHandler(CustomAssemblyResolverDocData);
availableTypes = new Dictionary<string, Type>();
availableAssemblies = new Dictionary<string, Assembly>();
PreloadAssemblies();
if (availableAssemblies.Count == 0)
throw new Exception("Problem");
base.OnDocumentLoading(e);
AppDomain.CurrentDomain.TypeResolve += mRes;
AppDomain.CurrentDomain.AssemblyResolve += mRes;
}
The exception happens when the designer is opened before the solution is loaded (happens when you closed visual studio while editing your design).
Throwing an exception here will prevent the load from happening silently.
In the PreloadAssemblies :
IVsHierarchy hier = VsHelper.ToHierarchy(dteProject);
DynamicTypeService typeService = (DynamicTypeService)this.GetService(typeof(DynamicTypeService));
ITypeDiscoveryService discovery = typeService.GetTypeDiscoveryService(hier);
try
{
foreach (Type type in discovery.GetTypes(typeof(object), true))
{
if (!availableTypes.ContainsKey(type.FullName))
{
availableTypes.Add(type.FullName, type);
}
if (!availableAssemblies.ContainsKey(type.Assembly.GetName().Name))
{
availableAssemblies.Add(type.Assembly.GetName().FullName, type.Assembly);
}
}
}
catch (Exception e)
{
}
And in the resolver just check if the assembly name is in the dictionary. otherwise return null.
override the DocumentClosed as well to remove the Assembly resolver :)
I have a situation and I need to know how to deal with it in the best way.
I have an application (MVC3) and I have couple of integrations for it. I have an interface "IntegrationInterface" and every integration implements it.
I want to load the dlls of the integrations, create a list of them, and run a loop that runs a method for every integration in the list.
For example - let's say I have integrations for facebook, myspace and twitter (for my appliction), and every time the user post a message in my application I want to post a message on his\her facebook, myspace and twitter.
I don't want that the code will know which integrations I have, so if tomorrow I'll create a new integration for google+, I'll just need to add a new DLL without changing the code of my application.
How can I do that?
First, you'll have to find all relevant dlls and classes:
loadedIntegrations.Clear();
if (!Directory.Exists(path))
return;
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files = di.GetFiles("*.dll");
foreach (var file in files)
{
Assembly newAssembly = Assembly.LoadFile(file.FullName);
Type[] types = newAssembly.GetExportedTypes();
foreach (var type in types)
{
//If Type is a class and implements the IntegrationInterface interface
if (type.IsClass && (type.GetInterface(typeof(IntegrationInterface).FullName) != null))
loadedIntegrations.Add(type);
}
}
loadedIntegrations is of type List<Type>. Then you can instantiate each integration and call its methods:
foreach(var integrationType in loadedIntegrations)
{
var ctor = integrationType.GetConstructor(new Type[] { });
var integration = ctor.Invoke(new object[] { }) as IntegrationInterface;
//call methods on integration
}
I am doing something similar to what you described in an import utility that wrote. My issue was that I didn't want to load ALL the assemblies. I only wanted to load assemblies that contained types that were requested.
To accomplish this I've used the AppDomain.CurrentDomain.AssemblyResolve event handler.
This event handler is raised just before the AppDomain throw an exception notifying that an assembly is not found. I execute similar code to what Nico suggested in that handler and return the requested assembly.
NOTE: I have a sub-directory called 'Tasks' (think Import Tasks) where I store all of my assemblies I want to load at runtime.
Here is the code:
var tasks = GetTasks();
var basedir = AppDomain.CurrentDomain.BaseDirectory; // Get AppDomain Path
var tasksPath = Path.Combine(basedir, "Tasks"); // append 'Tasks' subdir
// NOTE: Cannot be factored, relies on 'tasksPath' variable (above).
AppDomain.CurrentDomain.AssemblyResolve += (s, e) => // defined 'AssemblyResolve' handler
{
var assemblyname = e.Name + ".dll"; // append DLL to assembly prefix
// *expected* assembly path
var assemblyPath = Path.Combine(tasksPath, assemblyname); // create full path to assembly
if (File.Exists(assemblyPath)) return Assembly.LoadFile(assemblyPath); // Load Assembly as file.
return null; // return Null if assembly was not found. (throws Exception)
};
foreach (var task in tasks.OrderBy(q => q.ExecutionOrder)) // enumerate Tasks by ExecutionOrder
{
Type importTaskType = Type.GetType(task.TaskType); // load task Type (may cause AssemblyResolve event to fire)
if (importTaskType == null)
{
log.Warning("Task Assembly not found");
continue;
}
ConstructorInfo ctorInfo = importTaskType.GetConstructor(Type.EmptyTypes); // get constructor info
IImportTask taskInstance = (IImportTask)ctorInfo.Invoke(new object[0]); // invoke constructor and cast as IImportTask
taskInstances.Add(taskInstance);
}
// rest of import logic omitted...
If u know MEF (Managed extensibility framework) this might help you I personally use it but have to say that using MEF with MVC is not trivial i think for more information please visit
http://msdn.microsoft.com/en-us/library/dd460648.aspx