How can I dynamically compile a DLL using C# v3.5? - c#

I can compile a DLL at runtime from a simple code string without any issues. Opening it in IL Spy shows exactly what it should. But if I put any extension methods in the code it outputs the following error:
error CS1644: Feature `extension methods' cannot be used because it is not part of the C# 2.0 language specification
The only help I could find says I should be able to set the "CompilerVersion" to "v3.5" by providing a dictionary of options to the code provider, but it doesn't help at all.
Here's my code:
var options = new System.Collections.Generic.Dictionary<string, string> { { "CompilerVersion", "v3.5" } };
var codeProvider = new Microsoft.CSharp.CSharpCodeProvider(options);
var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp");
var parameters = new System.CodeDom.Compiler.CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "AutoGen.dll";
var results = provider.CompileAssemblyFromSource(parameters, code);
Debug.Log(results.Errors.DeepToString());

Related

Why do I get Microsoft.Scripting.SyntaxErrorException: unexpected token ',' when importing libraries using IronPython

I'm using IronPython v 2.7.8.1 in VS 2017. I've installed Python27, 36 and 37. I've tried switching between the various environments in VS. I've tried adding the search paths to the libraries of these installs. The python code will work if I run it in the interpreter. Trying to run the same code in VS throws: "Microsoft.Scripting.SyntaxErrorException: unexpected token ',' ". If I test a python script that doesn't include imports it will work? Is there a specific way python has to be installed to work IronPython? This is the C# Code:
class CallPython
{
public void PatchParameter(string parameter)
{
var FilePath = (#"C:\Users\Pac\Downloads\MvcAuth\MvcAuth\TwurlPy\TwurlPy\SendDirectMsg.py");
var engine = Python.CreateEngine(); // Extract Python language engine from their grasp
ICollection<string> searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib");
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib\site-packages");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope(); // Introduce Python namespace
(scope)
var d = new Dictionary<string, object>
{
{ "text", text},
{ "userID", userID},
};
// Add some sample parameters. Notice that there is no need in
// specifically setting the object type, interpreter will do that part for us
// in the script properly with high probability
scope.SetVariable("params", d); // This will be the name of the
// dictionary in python script, initialized with previously created .NET
// Dictionary
ScriptSource source = engine.CreateScriptSourceFromFile(FilePath);
// Load the script
object result = source.Execute(scope);
parameter = scope.GetVariable<string>("parameter"); // To get the
// finally set variable 'parameter' from the python script
return;
}
}
This is the Python script. If I comment out the import statements it works using IronPython, but of course I need them...
import twitter
import requests
import sys
parameter = "test"
def SendDM(text, userID, access_token, access_secret):
consumer_key = 'YkopsCQjEXccccccccccccccccccZvA9yy'
consumer_secret = 'TQVCoccccccccccccccccccct7y8VfmE'
access_token_key = access_token
access_token_secret = access_secret
api = twitter.Api(
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token_key=access_token_key,
access_token_secret=access_token_secret)
send_msg = api.PostDirectMessage(text, user_id=userID)
print (send_msg)
return
SendDM(text, userID, access_token, access_secret)

Adding references to current project to CSharpCodeProvider

I am trying to compile a string using CodeDom.
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v3.5"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false
};
compilerParams.ReferencedAssemblies.Add(Assembly.GetEntryAssembly().Location);
My problem is: In my compiled code I need to use classes from the project I am creating the code in. So I tried to add my current assembly as a reference, however it gives me the following error:
Metadata file 'Path\to\my\executable\MyProject.exe' could not be
opened -- 'An attempt was made to load a program with an incorrect
format.'
Anybody knows where my mistake is?
I had the same need and I solved this way:
compilerParams.ReferencedAssemblies.AddRange(Assembly.GetExecutingAssembly().GetReferencedAssemblies().Select(a => a.Name + ".dll").ToArray());
the +".dll" worked with me.
if you need to get the full path to the Assembly file you need to load them into a new AppDomain and then get the .Location of each one of them.

Proxy credentials for programmatically web service proxy class

I am having problem to consume a WebService programmatically, using the WSDL under a squid proxy. My application is build in c# .net. I compile an Assembly from the XML, after import the service descripton using this:
// a namespace and compile unit are needed by importer
CodeNamespace codeNamespace = new CodeNamespace();
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(codeNamespace);
ServiceDescriptionImportWarnings importWarnings = descriptionImporter.Import(codeNamespace, codeUnit);
if (importWarnings == 0) // no warnings
{
// create a c# compiler
CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp");
// include the assembly references needed to compile
string[] references = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parameters = new CompilerParameters(references);
// compile into assembly
CompilerResults results = compiler.CompileAssemblyFromDom(parameters, codeUnit);
foreach (CompilerError oops in results.Errors)
{
// trap these errors and make them available to exception object
throw new Exception("Compilation Error Creating Assembly");
}
// all done....
return results.CompiledAssembly;
}
else
{
// warnings issued from importers, something wrong with WSDL
throw new Exception("Invalid WSDL");
}
The problem is when i call the method Invoke(obj, args). Proxy cut the connection, if i call the WSDL using external address, like http://My_external_ip/my_webService.asmx. If i call using internal address, works fine.
When i add a webReference, manually, i use to do some thing like:
WebService WS = new WebService();
WS.Proxy = Proxy.credentials;
it work, but i couldn't find where to give the proxy credentials when using Assembly.
Thanks guys.
#Various,
You probebly want to write some code like this
WebService WS = new WebService();
WS.Proxy = wwwproxy("http://someproxy:8080";
WebProxy wwwproxy(string ptProxyURI)
{
var aProxy = New WebProxy;
aProxy.Credentials = CredentialCache.DefaultCredentials;
aProxy.BypassProxyOnLocal = True;
aProxy.Address = New Uri(ptProxyURI);
Return aProxy;
}
Hope it helps.
Cheers

CSharpCodeProvider: Why is a result of compilation out of context when debugging

I have following code snippet that i use to compile class at the run time.
//now compile the runner
var codeProvider = new CSharpCodeProvider(
new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
string[] references = new string[]
{
"System.dll", "System.Core.dll", "System.Core.dll"
};
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.AddRange(references);
parameters.OutputAssembly = "CGRunner";
parameters.GenerateInMemory = true;
parameters.TreatWarningsAsErrors = true;
CompilerResults result = codeProvider.CompileAssemblyFromSource(parameters, template);
Whenever I step through the code to debug the unit test, and I try to see what is the value of "result" I get an error that name "result" does not exist in current context. Why?
Are you debugging in release mode? This may happen to optimizations of unused variable.
For example:
public void OptimizedMethod()
{
int x = 5; // In optimized mode it's not possible to watch the variable
}
Code optimization happens when running in release mode, or when setting "Optimize code" in project properties (under build tab)

Is there a way to build a new type during Runtime?

I am going to ask a question that might sound weird.
Is there a way to build a new class during Runtime? Or at least, add a new property to an existing class.
I mean creating a class that doesn't exist and not an instance of an existing class. I could later on use reflections to load and use this class.
Adding a property to an existing type is not possible, but you can create a new type at runtime using Reflection.Emit. It's pretty complicated stuff, and it goes something like this:
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(
assemblyName , AssemblyBuilderAccess.Run, assemblyAttributes);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ModuleName");
TypeBuilder typeBuilder = moduleBuilder.DefineType(
"MyNamespace.TypeName" , TypeAttributes.Public);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
// Add a method
newMethod = typeBuilder.DefineMethod("MethodName" , MethodAttributes.Public);
ILGenerator ilGen = newMethod.GetILGenerator();
// Create IL code for the method
ilGen.Emit(...);
// ...
// Create the type itself
Type newType = typeBuilder.CreateType();
This code is just a sample. It could contain errors.
You can also generate classes by compiling C# source code at runtime using System.CodeDom, but I don't know a lot about that.
Take a look at the System.Reflection.Emit namespace. I've never used it myself but the classes in this namespace can be used to generate IL (intermediate language).
This is not a weird question - in some cases it might be very useful. For instance I use this technique for performance tests sometimes:
public static Type[] DynamicTypes;
public void CreateObjects()
{
var codeNamespace = new CodeNamespace( "DynamicClasses" );
codeNamespace.Imports.Add( new CodeNamespaceImport( "System" ) );
codeNamespace.Imports.Add( new CodeNamespaceImport( "System.ComponentModel" ) );
for( var i = 0; i < 2000; i++ )
{
var classToCreate = new CodeTypeDeclaration( "DynamicClass_" + i )
{
TypeAttributes = TypeAttributes.Public
};
var codeConstructor1 = new CodeConstructor
{
Attributes = MemberAttributes.Public
};
classToCreate.Members.Add( codeConstructor1 );
codeNamespace.Types.Add( classToCreate );
}
var codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add( codeNamespace );
var compilerParameters = new CompilerParameters
{
GenerateInMemory = true,
IncludeDebugInformation = true,
TreatWarningsAsErrors = true,
WarningLevel = 4
};
compilerParameters.ReferencedAssemblies.Add( "System.dll" );
var compilerResults = new CSharpCodeProvider().CompileAssemblyFromDom( compilerParameters, codeCompileUnit );
if( compilerResults == null )
{
throw new InvalidOperationException( "ClassCompiler did not return results." );
}
if( compilerResults.Errors.HasErrors )
{
var errors = string.Empty;
foreach( CompilerError compilerError in compilerResults.Errors )
{
errors += compilerError.ErrorText + "\n";
}
Debug.Fail( errors );
throw new InvalidOperationException( "Errors while compiling the dynamic classes:\n" + errors );
}
var dynamicAssembly = compilerResults.CompiledAssembly;
DynamicTypes = dynamicAssembly.GetExportedTypes();
}
You might take a look at the System.CodeDom namespace. According to one of the pages linked from there:
The .NET Framework includes a mechanism called the Code Document Object Model (CodeDOM) that enables developers of programs that emit source code to generate source code in multiple programming languages at run time, based on a single model that represents the code to render.
I'm not at all an expert in this, I just remembered seeing it on my .NET Framework poster on my wall. :)
Edit: Since writing this answer, I have played with System.CodeDom a bit. I've written a blog post that uses some basic CodeDom that may help those wanting to get started with it.

Categories