im having a Problem creating a Class at runtime. Everytime i debug the code below i get the following error message at var cls = results.CompiledAssembly.GetType("test.DummyHelloWorldHandler");
Could not load file or assembly 'file:///C:\Users\MyName\AppData\Local\Temp\1ivc3qic.dll' or one of its dependencies. Das System kann die angegebene Datei nicht finden.
the Name of the *.dll file differs everytime i debug the programm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace DynamicNS
{
class Program
{
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.Collections.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("test.DummyHelloWorldHandler");
var method = cls.GetMethod("Received", BindingFlags.Static | BindingFlags.Public);
object[] parms = { "Hallo Welt" };
method.Invoke(null, parms);
Console.ReadLine();
}
static string[] GetCode()
{
return new string[]
{
#"using System.Collections;
namespace test
{
public class DummyHelloWorldHandler
{
protected internal Queue _queue;
public void Received(string message)
{
lock (_queue)
{
_queue.Enqueue(message);
}
Console.WriteLine('Enqueued');
}
public DummyHelloWorldHandler()
{
_queue = new Queue();
}
}
}"
};
}
}
}
The code returned by GetCode does not compile because of the wrong quotes.
You can check that by iterating over the Errors property of your CompilerResults.
You have to remove this line:
parameters.ReferencedAssemblies.Add("System.Collections.dll");
and change the GetCode() method like this:
private static string[] GetCode()
{
return new string[]
{
#"using System;
using System.Collections;
namespace test
{
public class DummyHelloWorldHandler
{
protected internal Queue _queue;
public void Received(string message)
{
lock (_queue)
{
_queue.Enqueue(message);
}
Console.WriteLine(""Enqueued"");
}
public DummyHelloWorldHandler()
{
_queue = new Queue();
}
}
}"
};
}
#"using System.Collection
Should probably be
#"using System.Collections;
Also: Console.WirteLine()? Hmmm. Perhaps you should paste that entire GetCode() string into a test program, make it compile, and then paste it back into your original project.
After your edits, this still won't compile:
Console.WriteLine('Enqueued');
It's got single quotes instead of double quotes around the string.
Are you sure your code could actually be compiled? You seem to be missing a semicolon after your using statement.
Check the Errors property of your results, it contains the errors that were encountered while compiling your source.
Related
Using the Roslyn API with Visual Studio 2015, can I convert an object instance to source code? Can I create an extension method like ".ToSourceCode()" as shown below?
class Foo { }
class Program
{
static string classSourceCode = "class Foo { }";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
No. However, ILSpy can.
Based on the comments on the question and what I understand about Roslyn, decompilation is not supported. However, thanks to #Bradley's ILSpy tip, there is a solution:
Download the ILSpy binaries from http://ilspy.net/
Reference the following assemblies: ICSharpCode.Decompiler.dll, ILSpy.exe, Mono.Cecil.dll, ILSpy.BamlDecompiler.Plugin.dll
Implement the ".ToSourceCode()" extension method as shown below:
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy;
using Mono.Cecil;
class Foo { }
class Program
{
static string classSourceCode = "using System; internal class Foo { } ";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
static class TypeExtensions
{
public static string ToSourceCode(this Type source)
{
var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
var type = assembly.MainModule.Types.FirstOrDefault(t => t.FullName == source.FullName);
if (type == null) return string.Empty;
var plainTextOutput = new PlainTextOutput();
var decompiler = new CSharpLanguage();
decompiler.DecompileType(type, plainTextOutput, new DecompilationOptions());
return Regex.Replace(Regex.Replace(plainTextOutput.ToString(), #"\n|\r", " "), #"\s+", " ");
}
}
I am trying to write a visualizer that maps expressions in an expression tree to Roslyn syntax nodes, in order to generate code for the expression tree. Part of the syntax tree generation is a call to the AdhocWorkspace constructor.
When I run the visualizer using the VisualizerDevelopmentHost, everything works just fine:
using Microsoft.VisualStudio.DebuggerVisualizers;
using System;
using System.Linq.Expressions;
namespace _testVisualizer {
class Program {
[STAThread]
static void Main(string[] args) {
Expression<Func<bool>> expr = () => true;
var data = new TestVisualizerData(expr);
var visualizerHost = new VisualizerDevelopmentHost(data, typeof(TestVisualizer));
visualizerHost.ShowVisualizer();
Console.ReadKey(true);
}
}
}
But when I try to use the visualizer through the Visual Studio UI (by hovering over expr, clicking on the magnifying glass icon and choosing my visualizer), I get the following message:
Unable to perform function evaluation on the process being debugged.
Additional information
The function evaluation requires all threads to run.
I've identified the following as triggering the error:
workspace = new AdhocWorkspace();
which assigns to the workspace field on my Mapper class (source).
Why does calling the AdhocWorkspace constructor trigger this warning? How can I work around this?
This is an MCVE that demonstrates the issue:
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.DebuggerVisualizers;
using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
[assembly: DebuggerVisualizer(typeof(_testVisualizer.TestVisualizer), typeof(_testVisualizer.TestVisualizerDataObjectSource), Target = typeof(System.Linq.Expressions.Expression), Description = "Test Visualizer")]
namespace _testVisualizer {
public class TestVisualizer : DialogDebuggerVisualizer {
protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider) {
var data = (TestVisualizerData)objectProvider.GetObject();
var txt = new TextBlock();
txt.SetBinding(TextBlock.TextProperty, "Status");
var window = new Window {
DataContext = data,
Content = txt
};
window.ShowDialog();
}
}
[Serializable]
public class TestVisualizerData {
public TestVisualizerData() { }
public TestVisualizerData(System.Linq.Expressions.Expression expr) {
var workspace = new AdhocWorkspace();
Status = "Success";
}
public string Status { get; set; }
}
public class TestVisualizerDataObjectSource : VisualizerObjectSource {
public override void GetData(object target, Stream outgoingData) {
var expr = (System.Linq.Expressions.Expression)target;
var data = new TestVisualizerData(expr);
base.GetData(data, outgoingData);
}
}
}
Perhaps the AdhocWorkspace alternate constructor could be leveraged to use the SyntaxNode API in a single thread.
I've filed an issue.
I am having an issue with compiling a C# dll from a piece of code I wrote. it compiles just fine with no errors, but when I try to include it to a visual studio 2010 C# application the namespace does not show up while trying to call it from the "using" command.
Here is the code of the .dll file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PAD_SCRIPT;
using System.Threading;
namespace PARKER_SCRIPT
{
class PScript
{
//LOAD_REF
private void loadCustomScripts()
{
}
//END_REF
PADScript p = new PADScript();
private void loadScripts()
{
}
public PScript()
{
}
public void runFile(string file)
{
if (file.ToLower().Contains(".pb"))
{
//file = file.Replace(".pb", "");
p.executeLuaWithThread(file);
Console.ReadLine();
}
else
{
Console.WriteLine("ERROR 1: must be .pb file");
Console.ReadLine();
}
}
Core cp = new Core();
public PADScript loadScipt(PADScript l)
{
loadScripts();
loadCustomScripts();
l.addLuaCommand("runFile", this);
return l;
}
//this will be dynamically be updated when custom cs code gets added
private string[] getPatchNotes()
{
string[] info = null;
try
{
info = System.IO.File.ReadAllLines("info\\patch_notes.txt");
return info;
}
catch (Exception i)
{
Console.WriteLine(i.Message);
return info;
}
}
private string getVersion()
{
string info = null;
try
{
info = System.IO.File.ReadAllLines("info\\version.txt")[0];
return info;
}
catch (Exception i)
{
Console.WriteLine(i.Message);
return info;
}
}
}
}
I don't think the functions in the .dll file is an issue, but by compiling it on the command line I think I am missing a key parameter or something. I know when I compile it in visual studio it works just fine implementing it to a new project. Thank you in advance.
edit: here is the command line I did:
C:\WINDOWS\Microsoft.Net\Framework\v4.0.30319\csc.exe /out:Release\ParkerScript.dll /target:library /platform:x86 /reference:core\LuaInterface.dll /reference:core\System.Speech.dll /reference:core\PAD_SCRIPT.dll /reference:core\lua51.dll core\Program_lib.cs core\AssemblyInfo.cs core\lib\*.cs
The class definition is internal, which will not show up when you reference it.
Define your class like this:
namespace PARKER_SCRIPT
{
public class PScript
{
//Code goes here...
}
}
I have a promblem with run-time compiled classes. I have something like this 2 classes:
first class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program.Bullet {
public class Class1{
private int i;
public Class1(int j){
i=j;
}
}
}
and second class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Program.Bullet;
namespace Program.Object {
public class Class2{
public Class2(){
Class1 c1 = new Class1(5);
}
}
}
This two classes I would like to compile in run-time and use them in my project. So I have function to compile it (XmlNode has data about fullPath etc):
private ModuleBuilder moduleBuilder;
private List<string> isCompiled;
private void Compile(XmlNode compileingClasses) {
foreach (XmlNode compileingClass in compileingClasses) {
string fullPath = compileingClass.Attributes["path"].InnerText;
string type = compileingClass.Attributes["name"].InnerText; ;
if (!isCompiled.Contains(type)) {
isCompiled.Add(type);
var syntaxTree = SyntaxTree.ParseFile("../../File1/File2/" + fullPath);
var comp = Compilation.Create("Test.dll"
, syntaxTrees: new[] { syntaxTree }
, references: metadataRef
, options: comilationOption
);
// Runtime compilation and check errors
var result = comp.Emit(moduleBuilder);
if (!result.Success) {
foreach (var d in result.Diagnostics) {
Console.WriteLine(d);
}
throw new XmlLoadException("Class not found " + fullPath);
}
}
}
}
Is it possible to get the reference on Class1 to Class2?
Edit: Better question
Is it possible to create MetadataReference on compiled Class1?
Something like:
string fullName = bullet.Attributes["fullName"].InnerText;
var o = moduleBuilder.GetType(fullName);
metadataRef.Add(new MetadataFileReference(o.Assembly.Location));
This throw NotSupportedException
You're trying to reference the assembly which is currently being built and I don't think Roslyn can do that.
What you can do instead is to create a single Compilation from all your classes (probably having a separate SyntaxTree for each class). If you do that, you won't need any references.
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("ConsoleApplication1.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace ConsoleApplication1
{
class Program
{
public static void DynamicMethod()
{
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}
I would like to know if It's possible to access variable int xx from the runetime code (eg. putting xx = 2; line after "hello world". That would be awesome
Thanks :)
Yes, you can make that available, but you need to:
add a reference to the assembly that contains xx (I'm calling this assembly StaticAssembly, for convenience - so it is probably StaticAssembly.exe, the console exe that is running)
make Program in StaticAssembly a public type so that it is accessible
either make xx into a static field, or instantiate an instance of Program and pass it to the dynamic code somehow
The following works, for example (I'm using the "instantiate an instance and pass it" approach, so I've added a parameter to DynamicMethod):
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace StaticAssembly
{
public class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("StaticAssembly.exe");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("GeneratedAssembly.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
var p = new Program();
p.xx = 42;
method.Invoke(null, new object[] {p});
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace GeneratedAssembly
{
class Program
{
public static void DynamicMethod(StaticAssembly.Program p)
{
Console.WriteLine(p.xx);
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}