I have following Main method. LocalCabelTV and LocalDishTV classes are in the main application program. The program works fine.
I want to keep LocalCabelTV and LocalDishTV in separate dll files. I wonder then how will I load those classes at runtime? I understand then we'll not use switch but for loop to look for all dll files that implements IVideoSource interface in a particular directory and load those...
Need to know how to dynamically load dlls and create objects and use their methods ?
foreach (string dll in Directory.GetFiles("C:\DLLs\*.dll"))
{
Assembly assemb = Assembly.LoadFrom(dll);
??
}
Following works fine:
static void Main(string[] args)
{
SmartTv myTv = new SmartTv();
Console.WriteLine("Select A source to get TV Guide and Play");
Console.WriteLine("1. Local Cable TV\n2. Local Dish TV");
ConsoleKeyInfo input = Console.ReadKey();
switch (input.KeyChar)
{
case '1':
myTv.VideoSource = new LocalCabelTv();
break;
case '2':
myTv.VideoSource = new LocalDishTv();
break;
}
Console.WriteLine();
myTv.ShowTvGuide();
myTv.PlayTV();
Console.ReadKey();
}
class SmartTv
{
IVideoSource currentVideoSource = null;
public IVideoSource VideoSource
{
get
{
return currentVideoSource;
}
set
{
currentVideoSource = value;
}
}
public void ShowTvGuide()
{
if (currentVideoSource != null)
{
Console.WriteLine(currentVideoSource.GetTvGuide());
}
else
{
Console.WriteLine("Please select a Video Source to get TV guide from");
}
}
public void PlayTV()
{
if (currentVideoSource != null)
{
Console.WriteLine(currentVideoSource.PlayVideo());
}
else
{
Console.WriteLine("Please select a Video Source to play");
}
}
class LocalCabelTv : IVideoSource
{
const string SOURCE_NAME = "Local Cabel TV";
string IVideoSource.GetTvGuide()
{
return string.Format("Getting TV guide from - {0}", SOURCE_NAME);
}
string IVideoSource.PlayVideo()
{
return string.Format("Playing - {0}", SOURCE_NAME);
}
}
class LocalDishTv : IVideoSource
{
const string SOURCE_NAME = "Local DISH TV";
string IVideoSource.GetTvGuide()
{
return string.Format("Getting TV guide from - {0}", SOURCE_NAME);
}
string IVideoSource.PlayVideo()
{
return string.Format("Playing - {0}", SOURCE_NAME);
}
}
To load this assembly at runtime and create an object:
Assembly MyDALL = Assembly.Load("DALL"); // DALL is name of my dll
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // LoadClass is my class
object obj = Activator.CreateInstance(Type.GetType("DALL.LoadClass, DALL", true));
For your dynamic Method you can also use Dynamic Method . Its faster than reflection (This method takes only 1/10th time needed by Activator.)
Here is a sample code for creating Object using Dynamic Method.
void CreateMethod(ConstructorInfo target)
{
DynamicMethod dynamic = new DynamicMethod(string.Empty,
typeof(object),
new Type[0],
target.DeclaringType);
methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
}
Check out these link for more info: Load Assembly at runtime and create class instance
EDIT: As user #taffer mentioned the DynamicMethod.CreateDelegate is much more slower than reflection. So I would use this only if the created delegate is invoked hundreds or thousands of times. Using Activator with a cache is faster. Secondly, Activator is really fast for parameterless constructors, unless you instantiate so many types, which renders the inner small cache useless.
You need to load the DLLs with the desire classes and iterate over their types, than look for those that implement IVideoSource and activate them:
public static IEnumerable<IVideoSource> GetVideoSources(List<string> assemblyPathes)
{
IEnumerable<Assembly> yourAssembliesWithClasses = assemblyPathes.Select(x => Assembly.Load(x));
IEnumerable<Type> implementingTypes = yourAssembliesWithClasses
.GetTypes()
.Where(x => x.IsAssignableFrom(IVideoSource));
return implementingTypes.Select(x => Activator.CreateInstance(x));
}
Note that Activator.CreateInstance() requires the types to have an empty constructor, if they don't have one you can use, Type.GetUniGetUninitializedObject(Type type) from FormatterServices to initialize them.
Related
I'm working in a WinForm app in 4 layers:
DAL (Data access)
BOL (Bussiness objects)
BAL (Bussiness access)
INT (Intermediate access).
I'm using the Intermediate layer to run any operation needed by the Presentation layer, trying to make it independent, as we can use it in a WinForm, ASP, and so.
I've created a Class that executes those operations like this:
// Clase: ProjectStatusMID
using System.Collections.Generic;
namespace Trevo.FrameWork
{
public class ProjectStatusMID
{
#region Propiedades
private ProjectStatusBOL _Data = new ProjectStatusBOL();
private ProjectStatusBAL _Operations = new ProjectStatusBAL();
private Acciones _Action = Acciones.Nada;
#endregion Propiedades
public ProjectStatusBOL Data
{
get { return _Data; }
set
{
_Data = value;
}
}
public ProjectStatusBAL Operations
{
get { return _Operations; }
set
{
_Operations = value;
}
}
public Acciones Action
{
get { return _Action; }
set
{
_Action = value;
}
}
public int IDProject
{
get { return _Data.IDProject; }
set
{
_Data.IDProject = value;
}
}
public List<Codigos> ProjectsList
{
get { return LoadProjects(); }
}
public ProjectStatusMID()
{
//Load();
}
public void Load()
{
Operations.Consultar(Data);
}
public List<Codigos> LoadProjects()
{
List<Codigos> oRet = new List<Codigos>();
MyProjectsBAL _Operations = new MyProjectsBAL();
MyProjectsBOL _Data = new MyProjectsBOL();
List<MyProjectsBOL> _MyList = _Operations.Lista(_Data);
foreach (MyProjectsBOL o in _MyList)
{
oRet.Add(new Codigos(o.IDProject, o.Project));
}
return oRet;
}
}
}
// Clase: ProjectStatusMID
At the front-end (in this case is WinForm), we are instancing this class as follows:
ProjectStatusMID OO = new ProjectStatusMID();
So, the issue comes when calling one of the methods:
parProject.DataSource = OO.LoadProjects();
Everything is referenced, the app compiles without any problems, the project that contains the class is part of the solution in a separated project (as any other layer), BUT we have the following error:
System.MissingMethodException occurred
HResult=-2146233069
Message=Método no encontrado: 'System.Collections.Generic.List`1 Trevo.FrameWork.ProjectStatusMID.LoadProjects()'.
Source=WorkLoadPresentation
StackTrace:
en Trevo.FrameWork.PS_ProjectStatus_Datos.CargarListas()
en Trevo.FrameWork.PS_ProjectStatus_Datos.PS_ProjectStatus_Datos_Load(Object sender, EventArgs e) en C:\Users\fbravo\OneDrive\Particular_Sistemas\WorkLoad\WorkLoadPresentation\ProjectStatus\PS_ProjectStatus_Datos.cs:línea 25
InnerException:
I've tried to make the class static, re-creating the entire app, deleting the GAC, and so, but a week loose trying different things.
Any help will be appreciated
Could be several issues. The most common one is that you included the DLL library which is the wrong version (e.g. without the method that's missing). Easiest thing to do is to open the exe in the decompiler (e.g. Reflector) and step through it.
Another issue could be the wrong bitness (but probably not).
You have to make sure you referenced the external project dll in your main Winforms application
I have created the fallowing Sample-Code:
class Program {
static void Main(string[] args) {
var x = new ActionTestClass();
x.ActionTest();
var y = x.Act.Target;
}
}
public class ActionTestClass {
public Action Act;
public void ActionTest() {
this.Act = new Action(this.ActionMethod);
}
private void ActionMethod() {
MessageBox.Show("This is a test.");
}
}
When I do this on this way, y will an object of type ActionTestClass (which is created for x). Now, when I change the line
this.Act = new Action(this.ActionMethod);
to
this.Act = new Action(() => MessageBox.Show("This is a test."));
y (the Target of the Action) will be null. Is there a way, that I can get the Target (in the sample the ActionTestClass-object) also on the way I use an Anonymous Action?
The lack of Target (iow == null) implies the delegate is either calling a static method or no environment has been captured (iow not a closure, just a 'function pointer').
the reason why you see the target as empty is because the anonymous method is not part of any class. If you open your program in reflector, it will show you the code that is generated by the compiler, here you will see the following
public void ActionTest()
{
this.Act = delegate {
Console.WriteLine("This is a test.");
};
}
You can use the following:
Act.Method.DeclaringType
i have this class to use to get instances of objects from different DLL depending on a string which have the name of dlls.
public class PluginFactory
{
private static volatile PluginFactory Factory;
private static object syncRoot = new Object();
private PluginFactory()
{
}
public static PluginFactory Instance
{
get
{
if (Factory == null)
{
lock (syncRoot)
{
if (Factory == null)
{
Factory = new PluginFactory();
}
}
}
return Factory;
}
}
public IPlugableInterface GetPlugin(string assemblyName)
{
ObjectFactory.Initialize(x => x.AddRegistry(new PluginRegistery(assemblyName)));
_prog = ObjectFactory.GetInstance<PluginProgrammer>();
return _prog.Plugin;
}
PluginProgrammer _prog;
[Pluggable("Default")]
[PluginFamily("Default")]
internal class PluginProgrammer
{
public readonly IPlugableInterface Plugin;
public PluginProgrammer(IPlugableInterface Plugin)
{
this.Plugin = Plugin;
}
}
internal class PluginRegistery : Registry
{
public PluginRegistery(string assembly)
{
Scan(
scanner =>
{
scanner.AssembliesFromApplicationBaseDirectory(x => x.ManifestModule.Name == assembly);
scanner.AddAllTypesOf<IPlugableInterface>();
});
}
}
}
This works fine for the first call , it inject to the DLL which has its name as assembly-name and return an object of it, the second time i call it with a different assemblyname it doesnt work and doesnt return an object , the funny thing is it never pass this line if i debug and if i run it without breakpoint nothing just happen!.
_prog = ObjectFactory.GetInstance<PluginProgrammer>();
any idea why this is happening ? any idea how can i fix this or redesign it to accomplish what i want ?
I think you should be using named instances as you want to have different registrations in different DLLs. You could use the dllname as the name of the instance for example.
See also: http://geekswithblogs.net/michelotti/archive/2009/10/14/structuremap-with-named-instance-and-with-method.aspx
I want to write a rule that will fail if an object allocation is made within any method called by a method marked with a particular attribute.
I've got this working so far, by iterating up all methods calling my method to check using CallGraph.CallersFor(), to see if any of those parent methods have the attribute.
This works for checking parent methods within the same assembly as the method to be checked, however reading online, it appears that at one time CallGraph.CallersFor() did look at all assemblies, however now it does not.
Question: Is there a way of getting a list of methods that call a given method, including those in a different assembly?
Alternative Answer: If the above is not possible, how do i loop through every method that is called by a given method, including those in a different assembly.
Example:
-----In Assembly A
public class ClassA
{
public MethodA()
{
MethodB();
}
public MethodB()
{
object o = new object(); // Allocation i want to break the rule
// Currently my rule walks up the call tree,
// checking for a calling method with the NoAllocationsAllowed attribute.
// Problem is, because of the different assemblies,
// it can't go from ClassA.MethodA to ClassB.MethodB.
}
}
----In Assembly B
public var ClassAInstance = new ClassA();
public class ClassB
{
[NoAllocationsAllowed] // Attribute that kicks off the rule-checking.
public MethodA()
{
MethodB();
}
public MethodB()
{
ClassAInstance.MethodA();
}
}
I don't really mind where the rule reports the error, at this stage getting the error is enough.
I got round this issue by adding all referenced dlls in my FxCop project, and using the code below, which builds a call tree manually (it also adds calls for derived classes to work round another problem i encountered, here.
public class CallGraphBuilder : BinaryReadOnlyVisitor
{
public Dictionary<TypeNode, List<TypeNode>> ChildTypes;
public Dictionary<Method, List<Method>> CallersOfMethod;
private Method _CurrentMethod;
public CallGraphBuilder()
: base()
{
CallersOfMethod = new Dictionary<Method, List<Method>>();
ChildTypes = new Dictionary<TypeNode, List<TypeNode>>();
}
public override void VisitMethod(Method method)
{
_CurrentMethod = method;
base.VisitMethod(method);
}
public void CreateTypesTree(AssemblyNode Assy)
{
foreach (var Type in Assy.Types)
{
if (Type.FullName != "System.Object")
{
TypeNode BaseType = Type.BaseType;
if (BaseType != null && BaseType.FullName != "System.Object")
{
if (!ChildTypes.ContainsKey(BaseType))
ChildTypes.Add(BaseType, new List<TypeNode>());
if (!ChildTypes[BaseType].Contains(Type))
ChildTypes[BaseType].Add(Type);
}
}
}
}
public override void VisitMethodCall(MethodCall call)
{
Method CalledMethod = (call.Callee as MemberBinding).BoundMember as Method;
AddCallerOfMethod(CalledMethod, _CurrentMethod);
Queue<Method> MethodsToCheck = new Queue<Method>();
MethodsToCheck.Enqueue(CalledMethod);
while (MethodsToCheck.Count != 0)
{
Method CurrentMethod = MethodsToCheck.Dequeue();
if (ChildTypes.ContainsKey(CurrentMethod.DeclaringType))
{
foreach (var DerivedType in ChildTypes[CurrentMethod.DeclaringType])
{
var DerivedCalledMethod = DerivedType.Members.OfType<Method>().Where(M => MethodHidesMethod(M, CurrentMethod)).SingleOrDefault();
if (DerivedCalledMethod != null)
{
AddCallerOfMethod(DerivedCalledMethod, CurrentMethod);
MethodsToCheck.Enqueue(DerivedCalledMethod);
}
}
}
}
base.VisitMethodCall(call);
}
private void AddCallerOfMethod(Method CalledMethod, Method CallingMethod)
{
if (!CallersOfMethod.ContainsKey(CalledMethod))
CallersOfMethod.Add(CalledMethod, new List<Method>());
if (!CallersOfMethod[CalledMethod].Contains(CallingMethod))
CallersOfMethod[CalledMethod].Add(CallingMethod);
}
private bool MethodHidesMethod(Method ChildMethod, Method BaseMethod)
{
while (ChildMethod != null)
{
if (ChildMethod == BaseMethod)
return true;
ChildMethod = ChildMethod.OverriddenMethod ?? ChildMethod.HiddenMethod;
}
return false;
}
}
Did you give it a try in this way,
StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
object [] items = methodBase.GetCustomAttributes(typeof (NoAllocationsAllowed));
if(items.Length > 0)
//do whatever you want!
I'm using xml comments to document public as well as internal and private members of my components. I would like to package the generated documentation xml files with component assemblies in order to enable "rich" (e.g. with method, exceptions and parameter descriptions) Visual Studio Intellisense with the end product. The problem with it is that the C# compiler creates documentation entries for everything (including internal classes, methods, private fields of internal enums etc.) and there seems to be no switch to "only public members" mode.
Now I don't want to go over 50 files with XX methods in each and remove all comments for private and internal members. Even if I did that, I probably would not have much success with auto-gen'd resource files, because these strongly-typed resource classes are automatically commented and non-public.
My question is: is there some option/flag that I'm overlooking? If no, are there some tools that could help separate public members from the rest (before I start to code one)?
SandCastle Help File Builder has an option to recreate the xml files containing only the configured access modes for methods, properties, etc...
The only "downside" is that you'll have to generate a documentation.
EDIT
Since it's been a long time ago I forgot that I added a "component" to SHFB to generate the XML.
The good news is that this component is included in SHFB.
You have to add the "Intellisense Component" to the SHFB project. It will then generate the XML according to the configured SHFB project.
For more info: Intellisense Component in SHFB
There is a tool inside the eazfuscator that can remove non-public documentation. You can see an example here
I've given this some thought and I decided to change the way I go about solving this particular problem. Instead of finding type/member within the assembly trying to parse the XML documentation notation. I decided to simply build a string set (of XML documentation notation) for the public API that can then be used to tes wheter a member isn't public.
It's really simple. Send an assembly to the XmlDocumentationStringSet and it will build a string set of the public API and delete the elements that aren't public.
static void Main(string[] args)
{
var el = XElement.Load("ConsoleApplication18.XML");
// obviously, improve this if necessary (might not work like this if DLL isn't already loaded)
// you can use file paths
var assemblyName = el.Descendants("assembly").FirstOrDefault();
var assembly = Assembly.ReflectionOnlyLoad(assemblyName.Value);
var stringSet = new XmlDocumentationStringSet(assembly);
foreach (var member in el.Descendants("member").ToList()) // .ToList enables removing while traversing
{
var attr = member.Attribute("name");
if (attr == null)
{
continue;
}
if (!stringSet.Contains(attr.Value))
{
member.Remove();
}
}
el.Save("ConsoleApplication18-public.XML");
}
And here's the class that builds the XML documentation names (it's a bit large but I thought I post the entire source here anyway):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ConsoleApplication18
{
public class XmlDocumentationStringSet : IEnumerable<string>
{
private HashSet<string> stringSet = new HashSet<string>(StringComparer.Ordinal);
public XmlDocumentationStringSet(Assembly assembly)
{
AddRange(assembly.GetExportedTypes());
}
public bool Contains(string name)
{
return stringSet.Contains(name);
}
/// <summary>
/// Heelloasdasdasd
/// </summary>
/// <param name="types"></param>
public void AddRange(IEnumerable<Type> types)
{
foreach (var type in types)
{
Add(type);
}
}
public void Add(Type type)
{
// Public API only
if (!type.IsVisible)
{
return;
}
var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var member in members)
{
Add(type, member);
}
}
StringBuilder sb = new StringBuilder();
private void Add(Type type, MemberInfo member)
{
Type nestedType = null;
sb.Length = 0;
switch (member.MemberType)
{
case MemberTypes.Constructor:
sb.Append("M:");
AppendConstructor(sb, (ConstructorInfo)member);
break;
case MemberTypes.Event:
sb.Append("E:");
AppendEvent(sb, (EventInfo)member);
break;
case MemberTypes.Field:
sb.Append("F:");
AppendField(sb, (FieldInfo)member);
break;
case MemberTypes.Method:
sb.Append("M:");
AppendMethod(sb, (MethodInfo)member);
break;
case MemberTypes.NestedType:
nestedType = (Type)member;
if (IsVisible(nestedType))
{
sb.Append("T:");
AppendNestedType(sb, (Type)member);
}
break;
case MemberTypes.Property:
sb.Append("P:");
AppendProperty(sb, (PropertyInfo)member);
break;
}
if (sb.Length > 0)
{
stringSet.Add(sb.ToString());
}
if (nestedType != null)
{
Add(nestedType);
}
}
private bool IsVisible(Type nestedType)
{
return nestedType.IsVisible;
}
private void AppendProperty(StringBuilder sb, PropertyInfo propertyInfo)
{
if (!IsVisible(propertyInfo))
{
sb.Length = 0;
return;
}
AppendType(sb, propertyInfo.DeclaringType);
sb.Append('.').Append(propertyInfo.Name);
}
private bool IsVisible(PropertyInfo propertyInfo)
{
var getter = propertyInfo.GetGetMethod();
var setter = propertyInfo.GetSetMethod();
return (getter != null && IsVisible(getter)) || (setter != null && IsVisible(setter));
}
private void AppendNestedType(StringBuilder sb, Type type)
{
AppendType(sb, type.DeclaringType);
}
private void AppendMethod(StringBuilder sb, MethodInfo methodInfo)
{
if (!IsVisible(methodInfo) || (methodInfo.IsHideBySig && methodInfo.IsSpecialName))
{
sb.Length = 0;
return;
}
AppendType(sb, methodInfo.DeclaringType);
sb.Append('.').Append(methodInfo.Name);
AppendParameters(sb, methodInfo.GetParameters());
}
private bool IsVisible(MethodInfo methodInfo)
{
return methodInfo.IsFamily || methodInfo.IsPublic;
}
private void AppendParameters(StringBuilder sb, ParameterInfo[] parameterInfo)
{
if (parameterInfo.Length == 0)
{
return;
}
sb.Append('(');
for (int i = 0; i < parameterInfo.Length; i++)
{
if (i > 0)
{
sb.Append(',');
}
var p = parameterInfo[i];
AppendType(sb, p.ParameterType);
}
sb.Append(')');
}
private void AppendField(StringBuilder sb, FieldInfo fieldInfo)
{
if (!IsVisible(fieldInfo))
{
sb.Length = 0;
return;
}
AppendType(sb, fieldInfo.DeclaringType);
sb.Append('.').Append(fieldInfo.Name);
}
private bool IsVisible(FieldInfo fieldInfo)
{
return fieldInfo.IsFamily || fieldInfo.IsPublic;
}
private void AppendEvent(StringBuilder sb, EventInfo eventInfo)
{
if (!IsVisible(eventInfo))
{
sb.Length = 0;
return;
}
AppendType(sb, eventInfo.DeclaringType);
sb.Append('.').Append(eventInfo.Name);
}
private bool IsVisible(EventInfo eventInfo)
{
return true; // hu?
}
private void AppendConstructor(StringBuilder sb, ConstructorInfo constructorInfo)
{
if (!IsVisible(constructorInfo))
{
sb.Length = 0;
return;
}
AppendType(sb, constructorInfo.DeclaringType);
sb.Append('.').Append("#ctor");
AppendParameters(sb, constructorInfo.GetParameters());
}
private bool IsVisible(ConstructorInfo constructorInfo)
{
return constructorInfo.IsFamily || constructorInfo.IsPublic;
}
private void AppendType(StringBuilder sb, Type type)
{
if (type.DeclaringType != null)
{
AppendType(sb, type.DeclaringType);
sb.Append('.');
}
else if (!string.IsNullOrEmpty(type.Namespace))
{
sb.Append(type.Namespace);
sb.Append('.');
}
sb.Append(type.Name);
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
// Remove "`1" suffix from type name
while (char.IsDigit(sb[sb.Length - 1]))
sb.Length--;
sb.Length--;
{
var args = type.GetGenericArguments();
sb.Append('{');
for (int i = 0; i < args.Length; i++)
{
if (i > 0)
{
sb.Append(',');
}
AppendType(sb, args[i]);
}
sb.Append('}');
}
}
}
public IEnumerator<string> GetEnumerator()
{
return stringSet.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Oh, and I haven't figured out how to handle events yet, they are always visible in this example.
#John Leidegren
I've got the same requirement, and I've found the answer to the missing bit of your code. An Event has 2 methods, Add and Remove, and is considered public if either of them is public. So it would be something like:
private bool IsVisible(EventInfo eventInfo)
{
return eventInfo.GetAddMethod(false) != null
|| eventInfo.GetRemoveMethod(false) != null;
}
although I can't think of any reason why one would be public and not the other.
What tool are you using to generate the documentation? I use Sandcastle and that gives you the option to select members to include by accessability.
In general it seems expected that the XML would contain all info that could be needed and its up to the processing tool to select from it what is needed.
I was facing the same problem. SHFB is slow as hell and since we have another documentation code-base we did not need it to generate the documentation for us.
I ended up using XMLStarlet plus a separate namespace for internal classes. For example, all my internal classes would reside in MyCompany.MyProduct.Internal. Then I can use one simple command
xml ed -L -d "//member[contains(#name, 'MyCompany.MyProduct.Internal')]" MyProduct.xml
to cleanse the XML. This of course is not bullet-proof -- it does not cover internal members in public classes, and it does require some discipline to remember to put internal classes into internal. But this is the cleanest and least intrusive method which works for me. It is also a standalone EXE file easily checked into build server, no sweat.