Create dll file in runtime doesn't work in c# - c#

I am trying to create a DLL file in runtime ,as a matter of fact i need to save an encoded data to DLL .My code is like this :
class DllFile
{
public static void CreateDllFile(string source)
{
var provider = new CSharpCodeProvider();
var options = new CompilerParameters
{
OutputAssembly = "test.dll"
};
var results = provider.CompileAssemblyFromSource(options, new[] { source });
}
}
I expect from this code to create a dll file but it doesn't create
The error is :The pointer for this method was null
Best regards.Any ideas will be appreciated.

Compilation errors are reported via the returned value:
var results = provider.CompileAssemblyFromSource(options, new[] { source });
Now check results, and in particular results.Errors.
You can also check results.NativeCompilerReturnValue - that should be 0 for success, and non-zero for failure.

Any errors would be in the Errors property of the CompilerResults returned from the CompileAssemblyFromSource method. Have you tried printing them out to see if there are errors ?
CompilerResults results = provider.CompileAssemblyFromSource(options, new[] { source });
foreach(CompilerError error in results.Errors)
{
Console.WriteLine(error.ToString());
}

Related

How do I grab what is written to console with CSharpCodeProvider

I use following to compile C# in runtime:
CompilerParameters parameters = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = true,
IncludeDebugInformation = true
};
// True - memory generation, false - external file generation
// True - exe file generation, false - dll file generation
var res = pro.CompileAssemblyFromSource(parameters,
code);
Assembly assembly = res.CompiledAssembly;
Type program = assembly.GetType("First.Program");
MethodInfo main = program.GetMethod("Main");
var invoke = main?.Invoke(null, null);
res.Output is an empty list, and If the code has Console.WriteLine(), It gets written to main application's console, however; I wanna grab what is written.
You also should check res.Errors. If there are errors, then they will be there instead. If both Errors and Output are empty, then you may have had a successful compilation without any output. Check: cr.NativeCompilerReturnValue.ToString()

Replace method's Body with Body of another method using Mono.Cecil?

With Mono.Cecil it looks quite simple when we can just set the Body of the target MethodDefinition to the Body of the source MethodDefinition. For simple methods, that works OK. But for some methods whereas a custom type is used (such as to init a new object), it won't work (with an exception thrown at the time writing the assembly back).
Here is my code:
//in current app
public class Form1 {
public string Test(){
return "Modified Test";
}
}
//in another assembly
public class Target {
public string Test(){
return "Test";
}
}
//the copying code, this works for the above pair of methods
//the context here is of course in the current app
var targetAsm = AssemblyDefinition.ReadAssembly("target_path");
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test"));
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target");
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test");
var m1 = mr1.Resolve();
var m1IL = m1.Body.GetILProcessor();
foreach(var i in m1.Body.Instructions.ToList()){
var ci = i;
if(i.Operand is MethodReference){
var mref = i.Operand as MethodReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref));
}
else if(i.Operand is TypeReference){
var tref = i.Operand as TypeReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref));
}
if(ci != i){
m1IL.Replace(i, ci);
}
}
//here the source Body should have its Instructions set imported fine
//so we just need to set its Body to the target's Body
m2.Body = m1.Body;
//finally write to another output assembly
targetAsm.Write("modified_target_path");
The code above was not referenced from anywhere, I just tried it myself and found out it works for simple cases (such as for the 2 methods Test I posted above). But if the source method (defined in the current app) contains some Type reference (such as some constructor init ...), like this:
public class Form1 {
public string Test(){
var u = new Uri("SomeUri");
return u.AbsolutePath;
}
}
Then it will fail at the time writing the assembly back. The exception thrown is ArgumentException with the following message:
"Member 'System.Uri' is declared in another module and needs to be imported"
In fact I've encountered a similar message before but it's for method calls like (string.Concat). And that's why I've tried importing the MethodReference (you can see the if inside the foreach loop in the code I posted). And really that worked for that case.
But this case is different, I don't know how to import the used/referenced types (in this case it is System.Uri) correctly. As I know the result of Import should be used, for MethodReference you can see that the result is used to replace the Operand for each Instruction. But for Type reference in this case I totally have no idea on how.
All my code posted in my question is fine BUT not enough. Actually the exception message:
"Member 'System.Uri' is declared in another module and needs to be imported"
complains about the VariableDefinition's VariableType. I just import the instructions but not the Variables (which are just referenced exactly from the source MethodBody). So the solution is we need to import the variables in the same way as well (and maybe import the ExceptionHandlers as well because an ExceptionHandler has CatchType which should be imported).
Here is just the similar code to import VariableDefinition:
var vars = m1.Body.Variables.ToList();
m1.Body.Variables.Clear();
foreach(var v in vars){
var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType));
m1.Body.Variables.Add(nv);
}

MongoDB db.runCommand() from C#

Hi I'm using C# with MongoDB Official driver v2.2.4 and I want to run db.runCommand() on the admin database.
So far i have this and i am able to connect to the admin database but db.runCommand is giving me this error "An unhandled exception of type 'System.FormatException' occurred in MongoDB.Bson.dll Additional information: JSON reader was expecting a value but found 'db'."
MongoClient client = new MongoClient();
database = client.GetDatabase("admin");
var collection = database.GetCollection<BsonDocument>("test");
var commandResult = database.RunCommand<string>(#"db.createCollection(test1)");
After I resolve this test I want to run this command from C# but I am stuck.
db.runCommand( { addshard : “localhost:10001”, name : “shard10001” } );
Any one can resolve this problem and provide me with a good explanation and example. After some search I have tried this code does seems to make more sense but still getting an error.
"Additional information: Command addshard failed: no such command: 'addshard', bad cmd: '{ addshard: "192.168.1.4:27017", name: "shard1" }'."
Any ideas please of what I'm doing wrong! Thanks.
var addShardCommand = new BsonDocument {
{ "addshard", "192.168.1.4:27017"},
{ "name", "shard1" }
};
var addShardResult = database.RunCommand<BsonDocument>(addShardCommand);
You need to check what is the correct command in mongodb. like sometime name need Document object instead of just string.
I am using something like this. check if this help
var name = new BsonDocument { { "name", "regions" } };
var command = new BsonDocument { { "listCollections", 1 }, { "filter", name } };
var result = Database.RunCommand<BsonDocument>(command);
var k = result.ToJson();
Here name is again object which I found from this documentation https://docs.mongodb.com/manual/reference/command/listCollections/
Some more help you can take from here
https://zetcode.com/csharp/mongodb/

Add class to compiled assembly (in memory)

Since CompileAssemblyFromSource add custom functions in a smart way was ignored im going to ask this question differently so people will bother to read it.
cutting at the chase,i am making a language by "translating" the new syntax into c# and compiling it in memory in this fashion.
using (Microsoft.CSharp.CSharpCodeProvider CodeProv =
new Microsoft.CSharp.CSharpCodeProvider())
{
CompilerResults results = CodeProv.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
code);
var type = results.CompiledAssembly.GetType("MainClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine(output);
}
basically i am executing a "main" function written inside the code variable.
and i am using some functions in the code variable i would like to include without adding it as a string at the bottom like this:
code += #"public void Write(string path, object thevar)
{
if (thevar.GetType() == typeof(string))
{
System.IO.File.WriteAllText(path,(string)thevar);
}
if (thevar.GetType() == typeof(string[]))
{
System.IO.File.WriteAllLines(path,(string[])thevar);
}
}";
Can i somehow add a class from my Actual main project in VS and let the compiled in memory code access it? without adding it as a string.
You can embed your source code file(s) as resources. With this technique you can edit the file in Visual Studio and access the contents of the files as if it was a string during run-time.
This link shows how to do it:
https://stackoverflow.com/a/433182/540832

How to pass a generic list to a dynamically compiled C# code?

I want to let the user to write a block of code as text, compile it on the fly, execute it on a collection which I obtain from a data source and get the result.
I have been tying to pass a generic list as a parameter to the dynamically compiled code, but I could not figure out a way. Following is my code:
//User writes this code in a textbox and executes
var executeCode = #"//this line doesn't work because I don't know the type
MessageBox.Show(Parameters[0].Count());
//following works fine
var t = new List<string>{""asd"", ""xyz""};
var a = t.Select(x => x).First();
MessageBox.Show(a);
return (object) a;";
#region template Code
executeCode = #"
using System;
using System.IO;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;
namespace MyNamespace {
public class MyClass {
public object DynamicCode(params object[] Parameters) {
" + executeCode +
"} } }";
#endregion template Code
var references = new[] { "System.dll", "System.Core.dll", "System.Windows.Forms.dll" };
var compilerParams = new CompilerParameters
{
GenerateInMemory = true,
TreatWarningsAsErrors = false,
GenerateExecutable = false,
CompilerOptions = "/optimize"
};
compilerParams.ReferencedAssemblies.AddRange(references);
var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(compilerParams, executeCode);
if (compile.Errors.HasErrors)
{
var text = compile.Errors.Cast<CompilerError>()
.Aggregate("Compile Error: ", (current, ce) => current + ("rn" + ce.ToString()));
throw new Exception(text);
}
// execute the compiled code
var assembly = compile.CompiledAssembly;
var myObject = assembly.CreateInstance("MyNamespace.MyClass");
if (myObject == null)
{
MessageBox.Show("Couldn't load class.");
return;
}
var sampleList = new List<string> { "abcd", "bcd" };
var codeParams = new object[] { sampleList };
try
{
var loResult = myObject.GetType().InvokeMember("DynamicCode",BindingFlags.InvokeMethod, null, myObject, codeParams);
MessageBox.Show("Method Call Result:\r\n\r\n" + loResult, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception loError)
{
MessageBox.Show(loError.Message, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
In the above code I just pass a string list. But I will replace it with an object. User will write Linq queries to filter the collection, which I compile on the fly and return the result.
Any pointers on this will be really helpful. (I use C# 4.5)
There are several options.
Change the type of your Parameters object to List<string>[]. This is the way to go if you always know you are passing in a List<string>.
Cast in your dynamically generated code: ((List<string)Parameters[0]).Count; It is a little bit clunky, but will get rid of the error.
Change the type of your Parameters object to dynamic. Since your are compiling the code at runtime, compile time type checking may not be a priority for you.

Categories

Resources