Pass objects to Dynamic Assembly - c#

I have some types whose objects i need to pass to a dynamic assembly. But my types are not recognised and i am getting compile errors
Here is my code to compile
using System.Linq;
using System.Text;
using System.Collections.Generic;
using SkillBuilder.AutoGens.Libs;
namespace TempNs
{
public class MyClass
{
public MyClass()
{}
public Autozen ag;
//do stuff
}
}
The above code throws error saying that type Autozen could not be found. Are you missing an Assembly reference?. The type is in SkillBuilder.AutoGens.Libs.
This is how i compile it.
public static Assembly Compile(string sourceCode)
{
var provider_options = new Dictionary<string, string>
{
{"CompilerVersion","v4.0"}
};
CodeDomProvider cpd = new CSharpCodeProvider(provider_options);
var cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.Dll");
// True - memory generation, false - external file generation
cp.GenerateInMemory = true;
cp.GenerateExecutable = false;
CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
if (cr.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in cr.Errors)
{
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
return cr.CompiledAssembly;
}
I want to be able to assign the value of "ag" by the calling application.
How do i make Autozen available during compile time ? Is there any other way?
EDIT :
The code is generated and compiled by an EXE. The compiled output is again going to be used by the same EXE.
thanks for your help.
EDIT :
After adding Assembly.GetExecutingAssembly().Location to the referenced assembly collection now i get System.Core.dll missing referece error. Since i have included CompilerVersion, it should pickup the assembly from the GAC. Then why am i still getting this error ?

Related

Is there a way to make debugger work with modified assembly

I am trying to modify assembly before using it.
Main file:
using IlGenTestTarget;
using Lokad.ILPack;
using System.Reflection;
using Mono.Cecil;
using IlGenTest;
Assembly inAssembly = Assembly.GetAssembly(typeof(Class1));
AssemblyGenerator assemblyGenerator = new AssemblyGenerator();
byte[] b = assemblyGenerator.GenerateAssemblyBytes(inAssembly);
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(b));
foreach (ModuleDefinition module in assemblyDefinition.Modules) {
IlGenTestUtils.RemoveRemovedElements(module.Types);
foreach (TypeDefinition type in module.Types) {
IlGenTestUtils.RemoveRemovedElements(type.Methods);
IlGenTestUtils.RemoveRemovedElements(type.Fields);
}
}
MemoryStream ms = new MemoryStream();
assemblyDefinition.Write(ms);
byte[] res = ms.ToArray();
Assembly resAssembly = Assembly.Load(res);
Module resModule = resAssembly.GetModules()[0];
Type resType = resModule.GetType("IlGenTestTarget.Class1");
MethodInfo resMethod = resType.GetMethod("Method1");
resMethod.Invoke(null, null);
IlGenTestUtils:
using Mono.Cecil;
using Mono.Collections.Generic;
namespace IlGenTest
{
public class IlGenTestUtils
{
public static List<T> GetRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
return collection
.Where(t => t.CustomAttributes.Any(attr => attr.AttributeType.Name == "RemovedAttribute"))
.ToList();
}
public static void RemoveRemovedElements<T>(Collection<T> collection) where T : ICustomAttributeProvider
{
foreach (T t in GetRemovedElements<T>(collection))
{
collection.Remove(t);
}
}
}
}
When I put breakpoint on Method1, everything works fine, but progam is not paused on it. When I invoke Method1 directly, without creating new assembly, program is paused on breakpoint as expected. Is there a way to make breakpoints work with dynamic assembly?
The link between "source line at which a breakpoint is placed" and "assembly file contents" is defined by the .pdb file for the assembly. After modifying the assembly, I would actually be surprised if the link between them would still work.
You would need to also rebuild the .pdb file, which seems hard if not impossible.

Type information not correctly resolved by Roslyn Semantic Model

I am not able to retrieve the type information for a field, using the Semantic Model of Roslyn. It works for field of simple types like int or string, but not for Dictionary<,>.
Here is the code:
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace SemanticsCS
{
class Program
{
static void Main(string[] args)
{
var tree = CSharpSyntaxTree.ParseText(#"
public class MyClass {
int z;
Dictionary<string, string> dict;
int Method1() { int x = 3; return 0; }
void Method2()
{
int x = Method1();
}
}
}");
//
Dictionary<string, string> dict;
var Mscorlib = PortableExecutableReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
//Looking at the first method symbol
foreach (var nodeSyntax in tree.GetRoot().DescendantNodes())
{
var methodSymbol = model.GetSymbolInfo(nodeSyntax);
var symbolInfo = model.GetSymbolInfo(nodeSyntax);
var typeInfo = model.GetTypeInfo(nodeSyntax);
if (typeInfo.Type != null)
Console.WriteLine(nodeSyntax.GetText() + ":" + typeInfo.Type.Kind);
}
}
}
}
when I run it, I get
int :NamedType
Dictionary<string, string> :ErrorType
string:NamedType
string:NamedType
int :NamedType
int :NamedType
3:NamedType
0:NamedType
void :NamedType
int :NamedType
Method1():NamedType
I suppose ErrorType is a default used by Roslyn when the actual type is not retrieved.
The definition for Dictionary should come from mscorlib. Can it be that it is not found? Or, do I need to change something in the code? Apparently it is working on one of my colleagues computers, but not on mine. Is is a matter of configuring the usage of .Net?
Defining a reference assembly is not enought to tell to the compiler where a class name come from. You must specify either the full type name (System.Collections.Generic.Dictionary<string, string>) or define via using statement.
To help you understand errors you could take a look to diagnostic report. (From CSharpCompilation or SyntaxTree).
foreach (var d in compilation.GetDiagnostics())
{
Console.WriteLine(CSharpDiagnosticFormatter.Instance.Format(d));
}
It will give you this :
(11,1): error CS1022: Type or namespace definition, or end-of-file expected
(4,10): error CS0246: The type or namespace name 'Dictionary<,>' could not be found (are you missing a using directive or an assembly reference?)
error CS5001: Program does not contain a static 'Main' method suitable for an entry point
(5,30): warning CS0219: The variable 'x' is assigned but its value is never used
(4,37): warning CS0169: The field 'MyClass.dict' is never used
(3,14): warning CS0169: The field 'MyClass.z' is never used
The following error is the namespace error.
(4,10): error CS0246: The type or namespace name 'Dictionary<,>' could not be found (are you missing a using directive or an assembly reference?)
But you can see that some other are there. The following is here because of the extra } at the end of the script.
(11,1): error CS1022: Type or namespace definition, or end-of-file expected
End the last one :
error CS5001: Program does not contain a static 'Main' method suitable for an entry point
Is because, by default the compiler try to build an executable instead of library. You can change that with OutputKind enum.
var compilation = CSharpCompilation.Create(
"MyCompilation",
syntaxTrees: new[] { tree },
references: new[] { Mscorlib },
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
Edit :
If you want to find the OutputKind from an existing csproj file, you can do it like that : (inspired by this gist
// Nuget :
// https://www.nuget.org/packages/Microsoft.Build.Locator
// https://www.nuget.org/packages/Microsoft.CodeAnalysis.Workspaces.MSBuild/
// https://www.nuget.org/packages/Microsoft.CodeAnalysis
if (!MSBuildLocator.IsRegistered)
{
MSBuildLocator.RegisterDefaults();
}
using(var wp = MSBuildWorkspace.Create()){
var project = await wp.OpenProjectAsync(#"pathtocsprojfile.csproj");
Console.WriteLine(project.CompilationOptions.OutputKind);
}

Microsoft.CodeAnalysis: Error compiling dynamic code with Newtonsoft JObject

I'm having a strange problem that I can't fix. I have been compiling dynamic assemblies successfully for the most part but have come up with a strange problem compiling the following line:
return new JObject().Properties().ElementAt(0).Value();
with the error:
System.ApplicationException: 'Error creating dynamic code assembly 'IEnumerable<JProperty>' does not contain a definition for 'ElementAt' and no accessible extension method 'ElementAt' accepting a first argument of type 'IEnumerable<JProperty>' could be found (are you missing a using directive or an assembly reference?) '
The emitted text output works fine when created as a real class in the project but not when in a dynamic assembly. The project is an asp.net core 2.2 project and it references an assembly which creates the dynamic assemblies.
Here is the code that creates the assembly:
public static class Class2
{
public static Assembly GenerateAssenbly()
{
//generate the code
StringBuilder sb = new StringBuilder("");
sb.AppendLine("using System;");
sb.AppendLine("using System.Linq;");
sb.AppendLine("using Newtonsoft.Json;");
sb.AppendLine("using Newtonsoft.Json.Linq;");
sb.AppendLine("namespace test");
sb.AppendLine("{");
sb.AppendLine($"class Parser");
sb.AppendLine("{");
sb.AppendLine($"public object test() ");
sb.AppendLine("{");
sb.AppendLine("return new JObject().Properties().ElementAt(0).Value<string>();");
sb.AppendLine("}");
sb.AppendLine("}"); //class
sb.AppendLine("}"); //namespace
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sb.ToString());
var runtimeAssemblyDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location);
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "netstandard.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "System.Core.dll")),
MetadataReference.CreateFromFile(typeof(JObject).GetTypeInfo().Assembly.Location),
};
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
Debug.Print(sb.ToString()); // copy output to a class to test
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
throw new ApplicationException($"Error creating dynamic code assembly " + GetCompilerResultsErrors(result));
}
else
{
return Assembly.Load(ms.GetBuffer());
}
}
}
private static string GetCompilerResultsErrors(EmitResult result)
{
StringBuilder sb = new StringBuilder();
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
sb.AppendLine(diagnostic.GetMessage());
}
return sb.ToString();
}
}
(the code shown is not intended for working purposes, it is simplified to demonstrate the problem)
Thanks in advance,
solution was to add specific references:
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "System.Linq.Expressions.dll")),
MetadataReference.CreateFromFile(Path.Combine(runtimeAssemblyDirectory, "System.Linq.dll")),

System.ArgumentException Invalid file name in Assembly.LoadModule(string,byte[])

During my studies of reflection, I have encountered the .net module.
I understand that means I can compile a single class as .net module (correct me if I wrong) then load this compiled .net module using Assembly.LoadModule(string,byte[]).
I wrote a class that look like this:
using System;
using System.Text;
public class Mycs {
public static string GiveString(){
return "Hello World !";
}
}
and compiled it using the switch "/target:module" using this code:
CodeDomProvider CDP = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters CP = new CompilerParameters();
CP.GenerateExecutable = false;
CP.CompilerOptions = "/target:module";
CP.OutputAssembly = FilePathText.Text.Replace(Strings.Right(FilePathText.Text, 3), ".netmodule");
string source = File.ReadAllText(FilePathText.Text);
CompilerResults RS = CDP.CompileAssemblyFromSource(CP, source);
I then retrieved the resulted file bytes:
byte[] b = File.ReadAllBytes(FilePathText.Text);
And finally I tried to load the module to the current executed assembly:
Module[] Modules = Assembly.GetExecutingAssembly().GetModules();
Module[] moduless = Assembly.GetExecutingAssembly().GetLoadedModules();
Module A = Assembly.GetExecutingAssembly().LoadModule(Modules[0].Name, b);
Whether I passed the Modules[0].Name or moduless[0].Name both cause this exception:
An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: Invalid file name
Why do I get this invalid file name error?
You cannot load dynamically created module to an existing assembly, assemblies once compiled are immutable.
My best guess is that you need to use AssemblyBuilder.DefineDynamicModule() to create your classes in it. In this case you the type you created will be loaded automatically. or compile your classes as assemblies, not the modules, and load those assemblies dynamically using Assembly.LoadFile method.

badimageformatexception dynamic code generation

I'm trying to dynamically generate an executable with the CSharpCodeProvider whose only purpose is to invoke one single method from a specific dll. When I execute the generated file I get a BadImageFormatException.
I already set the platform to x86. When I manually write the code which invokes the method and debug it in Visual Studio, it works perfectly fine.
This is the code for the executable:
using DLLName;
namespace ExampleNamespace
{
class Program
{
public static void Main(string[] args)
{
MyClass obj = new MyClass();
obj.MyMethod();
}
}
}
Before dynamically compiling the code I add the assembly via
compilerParameters.ReferencedAssemblies.Add("PathToDLL");
I write the executable to the same directory as the dll.
EDIT
This is the code that I use to invoke the compiler:
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.GenerateInMemory = false;
parameters.OutputAssembly = #"DirectoryOfDLL\MyMethod.exe";
parameters.ReferencedAssemblies.Add("PathToDLL");
provider.CompileAssemblyFromSource(parameters, code);

Categories