Forgive me for just grasping these terms, I'm on the edge of my C# knowledge here and need to ask for guidance.
I have a DLL which includes two classes and a form (additional class) one of the classes workitems has public (string name, int id).
// in the DLL:
public class workitems {
public string name {get;set;}
public int id{get;set;}
}
The workhorse class has a variable used in several functions
// in the DLL:
public class workhorse {
List<workitems> WorkLoad = new List<workitems>();
public function DoThings() { ..... stuff ...... }
}
In another program, I need to call this dll (I assume via reflection). I've tried
// in a separate C# script that needs to call this via reflection
Assembly asm = Assembly.LoadFile(thedll);
but I can't figure out how to load workitems into the variable, and then call a function from the dll with those workitems... I'm getting confused with type/class/methodinfo/.GetType... any guidance would be appreciated.
From the program that has to call the dll file I need do something like :
otherdll.workload.add( stuff )
otherdll.DoThings(); (which uses the workload from that class)
That code assumes, that you have already your assembly and that Workload is a Field, not property:
//Get workhorse TypeInfo
var type = asm.ExportedTypes.Single(t => t.Name == "workhorse");
// Create instance of workhorse
var obj = Activator.CreateInstance(type);
// Get FieldInfo WorkLoad
var prop = type.GetField("WorkLoad");
// Get object workhorse.WorkLoad
var list = prop.GetValue(obj);
// Get MethodInfo for Add method
var method = prop.FieldType.GetMethod("Add");
// Call it with new object
method.Invoke(list, new [] { (object)new workitems()});
// Get DoThings methodinfo
var doThings = type.GetMethod("DoThings");
// call it without parameters
doThings.Invoke(obj, new object[0]);
Related
I understand how I can execute entire scripts using Roslyn in C# but what I now want to accomplish is to compile a class inside the script, instantiate it, parse it to an interface and then invoke methods that the compiled and instantiated class implements.
Does Roslyn expose such functionality? Can you someone please point me to such approach?
Thanks
I think you can do what you want for example like this:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
// create class and return its type from script
// reference current assembly to use interface defined below
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
return typeof(Test);
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()));
script.Compile();
// run and you get Type object for your fresh type
var testType = (Type) script.RunAsync().Result.ReturnValue;
// create and cast to interface
var runnable = (IRunnable)Activator.CreateInstance(testType);
// use
runnable.Run();
Console.ReadKey();
}
}
public interface IRunnable {
void Run();
}
}
Instead of returning type you created from script you can also use globals and return it that way:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
MyTypes.Add(typeof(Test).Name, typeof(Test));
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()), globalsType: typeof(ScriptGlobals));
script.Compile();
var globals = new ScriptGlobals();
script.RunAsync(globals).Wait();
var runnable = (IRunnable)Activator.CreateInstance(globals.MyTypes["Test"]);
runnable.Run();
Console.ReadKey();
}
}
public class ScriptGlobals {
public Dictionary<string, Type> MyTypes { get; } = new Dictionary<string, Type>();
}
public interface IRunnable {
void Run();
}
}
Edit to answer your comment.
what if I know the name and type of the class in the script? My
understanding is that script.Compile() adds the compiled assembly to
gac? Am I incorrect? If I then simply use
Activator.CreateInstance(typeofClass) would this not solve my problem
without even having to run the script
Compiled assembly is not added to gac - it is compiled and stored in memory, similar to how you can load assembly with Assembly.Load(someByteArray). Anyway, after you call Compile that assembly is loaded in current app domain so you can access your types without RunAsunc(). Problem is this assembly has cryptic name, for example: ℛ*fde34898-86d2-42e9-a786-e3c1e1befa78#1-0. To find it you can for example do this:
script.Compile();
var asmAfterCompile = AppDomain.CurrentDomain.GetAssemblies().Single(c =>
String.IsNullOrWhiteSpace(c.Location) && c.CodeBase.EndsWith("Microsoft.CodeAnalysis.Scripting.dll"));
But note this is not stable, because if you compile multiple scripts in your app domain (or even same script multiple times) - multiple such assemblies are generated, so it is hard to distinguish between them. If that is not a problem for you - you can use this way (but ensure that you properly test all this).
After you found generated assembly - problems are not over. All your script contents are compiled under wrapping class. I see its named "Submission#0" but I cannot guarantee it's always named like that. So suppose you have class Test in your script. It will be child class of that wrapper, so real type name will be "Submission#0+Test". So to get your type from generated assembly it's better to do this:
var testType = asmAfterCompile.GetTypes().Single(c => c.Name == "Test");
I consider this approach somewhat more fragile compared to previous, but if previous are not applicable for you - try this one.
Another alternative suggested in comments:
script.Compile();
var stream = new MemoryStream();
var emitResult = script.GetCompilation().Emit(stream);
if (emitResult.Success) {
var asm = Assembly.Load(stream.ToArray());
}
That way you create assembly yourself and so do not need to search it in current app domain.
I want to create a Visual Studio Extension(VSIX) using Roslyn. This extension should find all invocations, then looks for their definitions to analyse attributes defined on them. The definition of the method can be any where in the solution.
My first class is like this:
namespace MainProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(BusinessProject.Calc.AddNumbers(5, 8));
}
}
}
My second class is something like the following:
namespace BusinessProject
{
public class Calc
{
[CustomAttr("Do something")]
public static long AddNumbers(int a, int b)
{
return a + b;
}
}
}
In above sample, in class Program, I have an invocation of AddNumbers method. I want to 1) analyse the code, 2) find this invocation, 3) get all attributes of the reference method in class Calc, 4) process attributes parameters, 5) and then make a warning/error if needed.
I can analyse current class to find all invocations by RegisterCodeBlockAction, But what I can't do is accessing the entire solution to find definition of the invocation and after that, accessing attributes of the reference.
How can I access the entire solution in RegiserCodeBlockAction?
You're calling the wrong method.
You actually want your analyzer to run on every method invocation, not every code block.
Therefore, you should call RegisterSyntaxNodeAction, and pass SyntaxKind.InvocationExpression.
It will then call you with an InvocationExpressionSyntax which has all the information you need (mostly in the Semantic Model).
As #SLaks mentioned in his comment, by using SemanticModel.Compilation.SyntaxTrees we can access all of the source codes in solution. I found the method and its parameters. But this has a problem which you can't get the TypeInfo of objects using SemanticModel.GetTypeInfo. You have to create a new Semantic Model like as follows:
foreach (var tree in context.SemanticModel.Compilation.SyntaxTrees)
{
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new List<MetadataReference>());
var syntaxRoot = tree.GetRoot();
var model = compilation.GetSemanticModel(tree);
var targetMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>().FirstOrDefault(f => f.Identifier.ToString() == "Class name to find");
if (targetMethod == null)
continue;
var typeInfo = model.GetTypeInfo(targetMethod.First().ParameterList.Parameters[i].ChildNodes().First());
// Do any thing with typeInfo
}
I need to create attribute for functions that will create files according to a given name before the call to the function or even if there is no call to the function.
For example, if I have function with atribute [some("C:\\hello.txt")]:
[some("C:\\hello.txt")]
private void foo()
{
// do something
}
When I will run the application it will create this file ("C:\hello.txt") before calling the function or even if there is no call to the function..
I tried with two techniques:
1. Creating the file in the constructor
2. Creating the file with reflection.
But none of them worked for me.
First try (with constructor):
I tried create the file in the constructor every time there is a new attribute.
In this method I tried to create the file before the enter to the Main function.
While it parsing the functions it will find the attributes and create the files.
Expected:
Two files should be created:
1. C:\hello.txt
2. C:\bye.txt
In reality => nothing happen.
[some("C:\\hello.txt")]
private void foo()
{
// do something
}
[some("C:\\bye.txt")]
private void foo()
{
// do something
}
public class someAttribute : Attribute
{
public someAttribute(string fileToCreate)
{
// File.Create(fileToCreate);
Console.WriteLine("Create file " + fileToCreate);
}
}
static void Main(string[] args)
{
// something
}
Second try (with reflection):
Expected:
One file should be created:
1. C:\hello.txt
In reality => "types" variables is empty and nothing is being created.
[some(fileToCreate = "C:\\hello.txt")]
private void foo()
{
// do something
}
public class someAttribute : Attribute
{
public string fileToCreate {get; set;}
}
static void Main(string[] args)
{
var types = from t in Assembly.GetExecutingAssembly().GetTypes()
where t.GetCustomAttributes<someAttribute>().Count() > 0
select t;
foreach(var t in types) // types is null
{
string n = t.Name;
foreach(var p in t.GetProperties())
{
// File.Create(fileToCreate)
Console.WriteLine(p.fileToCreate);
}
}
}
Your first attempt didn't work because an attribute's constructor is run when an attribute is examined See When is a custom attribute's constructor run? for additional details. It won't be run just by the fact that a method has that attribute in the code. So reflection will be needed to get a list of methods that have the desired attribute.
Your second attempt came close, but didn't work because you only looked at the attributes attached to the class types. You'll need to go one step further to look at the methods within the classes.
I came up with a solution, but be warned that it could affect performance since it looks at every type and method in the assemblies linked to your project. You may want to limit the assemblies to only the ones that you can expect to have someAttribute. See C#: List All Classes in Assembly for some examples on how to do this.
static void Main()
{
var methods = AppDomain.CurrentDomain.GetAssemblies()
//Get a sequence of all types in the referenced assemblies
.SelectMany(assembly => assembly.GetTypes())
//Get a sequence of all the methods in those types.
//The BindingFlags are needed to make sure both public and non-public instance methods are included.
//Otherwise private methods are skipped.
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
//And finally, filter to only those methods that have someAttribute attached to them
.Where(method => Attribute.IsDefined(method, typeof(someAttribute)));
foreach (MethodInfo methodInfo in methods)
{
IEnumerable<someAttribute> SomeAttributes = methodInfo.GetCustomAttributes<someAttribute>();
foreach (var attr in SomeAttributes)
{
//Here, you can create the file.
Console.WriteLine(attr.fileToCreate);
}
}
}
This question already has answers here:
How can I get the values of the parameters of a calling method?
(6 answers)
Closed 6 years ago.
This question is related to my previous question How to get a IDictionary<string, object> of the parameters previous method called in C#?. I wrote the code, but there is still a missing piece. How do I get the values from the parameters?
If the following code is executed, the output only shows the parameter's names, but not the values.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Question {
internal class Program {
public static void Main(string[] args) {
var impl = new Implementation();
var otherClass = new OtherClass { Name = "John", Age = 100 };
impl.MethodA(1, "two", otherClass);
}
}
internal class Implementation {
public void MethodA(int param1, string param2, OtherClass param3) {
Logger.LogParameters();
}
}
internal class OtherClass {
public string Name { get; set; }
public int Age { get; set; }
}
internal class Logger {
public static void LogParameters() {
var parameters = GetParametersFromPreviousMethodCall();
foreach (var keyValuePair in parameters)
Console.WriteLine(keyValuePair.Key + "=" + keyValuePair.Value);
}
private static IDictionary<string, object> GetParametersFromPreviousMethodCall() {
var stackTrace = new StackTrace();
var frame = stackTrace.GetFrame(2);
var method = frame.GetMethod();
var dictionary = new Dictionary<string, object>();
foreach (var parameterInfo in method.GetParameters())
dictionary.Add(parameterInfo.Name, parameterInfo.DefaultValue);
return dictionary;
}
}
}
Your code is probably a dead-end. There is nothing in the stack frame that lets you get parameter values.
However, it is completely possible to do this task. What you want is very similar to writing a profiler. You would want to inject code into any method you want to log to vector off its parameters. Let's say that you started with a class like this:
public class ParameterBlob {
public ParameterInfo Info { get; set; }
public object Value { get; set; }
}
And let's say you have a method somewhere like this:
public static void LogMethodCall(MethodInfo method, param ParameterBlob[] parameterBlobs) { /* ... */ }
Here is more or less what you want to inject:
MethodInfo methodInfo = MyLogging.GetMyMethodInfo();
ParameterBlob[] blobs = new ParameterBlobs[MyLogging.GetMyParameterCount(methodInfo);
ParameterBlob blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 0);
blob.Value = param0; // More on this
blobs[0] = blob;
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, 1);
blob.Value = param1; // More on this
blobs[1] = blob;
// ...
blob = new ParameterBlob();
blob.Info = MyLogging.GetParameterInfo(methodInfo, n);
blob.Value = paramn; // more on this
blobs[n] = blob;
MyLogging.LogMethodCall(methodInfo, blobs);
So those lines that say "More on this"? You can't actually write them. But you could write a sample routine that refers to its own parameters to do it. What you would have is an ldarg instruction and a stloc instruction (and a few others in between). The point is, write it in C# then use the compiler and ILDASM to show you the correct code that you would need for one parameter, then you could write a routine to generate that CIL for you, which you would then plug into the .NET profiling API to attach to any routine you want.
See the article Rewrite MSIL Code on the Fly with the .NET Framework Profiling API for more information on that.
You probably also want to use an attribute to mark methods as loggable or not.
The only issue is that you have to have runtime access to do this, which you might not. Are you totally out of luck? No.
By using Cecil, you can have access to the entire assembly before it is run and preprocess it to inject the logging calls for you. Cecil is incredibly straightforward and it should be a couple of days work to rewrite any assembly to include logging calls. Less if you can know a priori that you're doing this in the target assembly and have the appropriate references already set up. Essentially, you will visit every method in the assembly and if it is loggable, you will inject the CIL to log all its parameters, just like the sample above.
According to a prior question (How can I get the values of the parameters of a calling method?), it is not possible through the StackTrace.
AOP is your friend.
Sample console program.
class Program
{
static void Main(string[] args)
{
// ... code to build dll ... not written yet ...
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
// don't know what or how to cast here
// looking for a better way to do next 3 lines
IRunnable r = assembly.CreateInstance("TestRunner");
if (r == null) throw new Exception("broke");
r.Run();
}
}
I want to dynamically build an assembly (.dll), and then load the assembly, instantiate a class, and call the Run() method of that class. Should I try casting the TestRunner class to something? Not sure how the types in one assembly (dynamic code) would know about my types in my (static assembly / shell app). Is it better to just use a few lines of reflection code to call Run() on just an object? What should that code look like?
UPDATE:
William Edmondson - see comment
Use an AppDomain
It is safer and more flexible to load the assembly into its own AppDomain first.
So instead of the answer given previously:
var asm = Assembly.LoadFile(#"C:\myDll.dll");
var type = asm.GetType("TestRunner");
var runnable = Activator.CreateInstance(type) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
I would suggest the following (adapted from this answer to a related question):
var domain = AppDomain.CreateDomain("NewDomainName");
var t = typeof(TypeIWantToLoad);
var runnable = domain.CreateInstanceFromAndUnwrap(#"C:\myDll.dll", t.Name) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
Now you can unload the assembly and have different security settings.
If you want even more flexibility and power for dynamic loading and unloading of assemblies, you should look at the Managed Add-ins Framework (i.e. the System.AddIn namespace). For more information, see this article on Add-ins and Extensibility on MSDN.
If you do not have access to the TestRunner type information in the calling assembly (it sounds like you may not), you can call the method like this:
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
var obj = Activator.CreateInstance(type);
// Alternately you could get the MethodInfo for the TestRunner.Run method
type.InvokeMember("Run",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
null);
If you have access to the IRunnable interface type, you can cast your instance to that (rather than the TestRunner type, which is implemented in the dynamically created or loaded assembly, right?):
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
IRunnable runnable = Activator.CreateInstance(type) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
I'm doing exactly what you're looking for in my rules engine, which uses CS-Script for dynamically compiling, loading, and running C#. It should be easily translatable into what you're looking for, and I'll give an example. First, the code (stripped-down):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CSScriptLibrary;
namespace RulesEngine
{
/// <summary>
/// Make sure <typeparamref name="T"/> is an interface, not just any type of class.
///
/// Should be enforced by the compiler, but just in case it's not, here's your warning.
/// </summary>
/// <typeparam name="T"></typeparam>
public class RulesEngine<T> where T : class
{
public RulesEngine(string rulesScriptFileName, string classToInstantiate)
: this()
{
if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName");
if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate");
if (!File.Exists(rulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName);
}
RulesScriptFileName = rulesScriptFileName;
ClassToInstantiate = classToInstantiate;
LoadRules();
}
public T #Interface;
public string RulesScriptFileName { get; private set; }
public string ClassToInstantiate { get; private set; }
public DateTime RulesLastModified { get; private set; }
private RulesEngine()
{
#Interface = null;
}
private void LoadRules()
{
if (!File.Exists(RulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName);
}
FileInfo file = new FileInfo(RulesScriptFileName);
DateTime lastModified = file.LastWriteTime;
if (lastModified == RulesLastModified)
{
// No need to load the same rules twice.
return;
}
string rulesScript = File.ReadAllText(RulesScriptFileName);
Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true);
#Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>();
RulesLastModified = lastModified;
}
}
}
This will take an interface of type T, compile a .cs file into an assembly, instantiate a class of a given type, and align that instantiated class to the T interface. Basically, you just have to make sure the instantiated class implements that interface. I use properties to setup and access everything, like so:
private RulesEngine<IRulesEngine> rulesEngine;
public RulesEngine<IRulesEngine> RulesEngine
{
get
{
if (null == rulesEngine)
{
string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs");
rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName);
}
return rulesEngine;
}
}
public IRulesEngine RulesEngineInterface
{
get { return RulesEngine.Interface; }
}
For your example, you want to call Run(), so I'd make an interface that defines the Run() method, like this:
public interface ITestRunner
{
void Run();
}
Then make a class that implements it, like this:
public class TestRunner : ITestRunner
{
public void Run()
{
// implementation goes here
}
}
Change the name of RulesEngine to something like TestHarness, and set your properties:
private TestHarness<ITestRunner> testHarness;
public TestHarness<ITestRunner> TestHarness
{
get
{
if (null == testHarness)
{
string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs");
testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName);
}
return testHarness;
}
}
public ITestRunner TestHarnessInterface
{
get { return TestHarness.Interface; }
}
Then, anywhere you want to call it, you can just run:
ITestRunner testRunner = TestHarnessInterface;
if (null != testRunner)
{
testRunner.Run();
}
It would probably work great for a plugin system, but my code as-is is limited to loading and running one file, since all of our rules are in one C# source file. I would think it'd be pretty easy to modify it to just pass in the type/source file for each one you wanted to run, though. You'd just have to move the code from the getter into a method that took those two parameters.
Also, use your IRunnable in place of ITestRunner.
You will need to use reflection to get the type "TestRunner". Use the Assembly.GetType method.
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.LoadFile(#"C:\dyn.dll");
Type type = assembly.GetType("TestRunner");
var obj = (TestRunner)Activator.CreateInstance(type);
obj.Run();
}
}
When you build your assembly, you can call AssemblyBuilder.SetEntryPoint, and then get it back from the Assembly.EntryPoint property to invoke it.
Keep in mind you'll want to use this signature, and note that it doesn't have to be named Main:
static void Run(string[] args)