I want to Load and Create Assemblies during runtime and someone told me to use the Namespace System.Reflection.Assembly and System.Reflection.Emit.
Only reference I found was on the msdn, but it´s not as good to work with it when you don´t know where and how to start. I already googled but I didn´t find any useful tutorials/samples/references.
Can someone explain the functionality to me or give me some samples/tutorials?
http://msdn.microsoft.com/en-us/library/saf5ce06.aspx
public static void CompileScript(string source)
{
CompilerParameters parms = new CompilerParameters();
parms.GenerateExecutable = true;
parms.GenerateInMemory = true;
parms.IncludeDebugInformation = false;
parms.ReferencedAssemblies.Add("System.dll");
// Add whatever references you might need here
CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp");
CompilerResults results = compiler.CompileAssemblyFromSource(parms, source);
file.move(results.CompiledAssembly.Location,"c:\myassembly.dll");
}
One possible way to create assembly from a source file(s) is simply run CSC (C# command line compiler) passing in source files and references as arguments. Manual IL generation is likely way too advanced, especially if you want to build assemblies from code provided by someone else.
To load - use Assembly.Load.
Related
I am trying to compile code at runtime using C#, Unity and this tutorial. I've got everything running, but want to be able to use UnityEngine methods like Debug.Log(); in the pseudo-code. In order to do so, I wrote using UnityEngine; in it, but got the following error:
The type or namespace name "UnityEngine" could not be found. Are you missing an assembly reference?
How do I solve this?
I have tried adding a reference of UnityEngine to CompilerParameters.ReferencedAssemblies like this:
//parameters is of type CompilerParameters
parameters.ReferencedAssemblies.Add("UnityEngine.dll");
but that gives me the following error:
Metadata file UnityEngine.dll could not be found.
This is the most relevant part of my code, but you won't be able to reproduce it like that. Instead, get the code from the above mentioned tutorial.
public void Compile()
{
string code = #"
using UnityEngine;
namespace First
{
public class Program
{
public static void Main()
{
" + "Debug.Log(\"Test!\");" + #"
}
}
}
";
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("UnityEngine.dll");
}
Since I am inexperienced with C#, and only use it with Unity, I don't know what a .dll file is and am clueless about where to start when fixing this. I am thankful for any kind of help!
You are supposed to put the path of the UnityEngine.dll there, not just UnityEngine.dll, unless it exists in the working directory, which is highly unlikely.
According to this answer, you can easily find UnityEngine.dll (and other dlls as well) in your file system. It is under Editor\Data\Managed, so you should write:
parameters.ReferencedAssemblies.Add(#"C:\\path\to\your\unity\installation\Editor\Data\Managed\UnityEngine.dll");
I tried some of the open source solutions, but couldn't find a solution which worked on all 3 platforms. Finally I bought this asset, which works without problems on Windows, Mac and Linux: https://assetstore.unity.com/packages/tools/integration/dynamic-c-82084 The documentation and support is very good. For example I had a problem that I couldn't compile a class which uses the Unity Screen class, and the support told me that I had to include the UnityEngine.CoreModule.dll in the settings page of the asset, which solved the problem.
PS: I don't get money for this recommendation, I'm just a happy user of the asset.
I'm building a utility that uses Microsoft's DACPAC libraries. For the purpose of this tool, I want to embed all requisite libraries in the executable. It appears that when I execute DacServices.GenerateDeployScript() it's trying to use the Microsoft.SqlServer.Dac.Extensions library. The library is also embedded, but perhaps isn't being resolved with my EventHandler the way other DLLs are. My EventHandler is like this:
private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args)
{
//Debugger.Break();
String dllName = new AssemblyName(args.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null;
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
This works for resolving other items, but I believe that the likely issue is that the Microsoft.SqlServer.Dac namespace is making an execution time call to the .Extensions namespace and isn't able to resolve the namespace or the methods in it. I could be wrong, but I'm not sure what else could be the cause.
The calls to methods and classes in .Dac itself are being handled fine, so I know the EventHandler is working properly. I'm not really sure what to do and would appreciate any guidance. I've tried using Microsoft.SqlServer.Dac.Extenions at the top of the .cs file, but since I don't directly call anything in that namespace, it's grey and probably is ignored by the compiler.
Thanks!
Update:
I made a call to the .Extensions namespace in the code to force it to be read into memory prior to the failing call, though it appears that it already was. I set a breakpoint where the resolver kicks off. Just prior to it failing, it's trying to resolve .resource for each DLL, e.g. Microsoft.SqlServer.Dac.resource and Microsoft.SqlServer.TransactSql.ScriptDom.resource - all for DLLs embedded in the executable. The resolver doesn't see anything because there are no .resource files in the project, so nothing compiled into the manifest. Aren't these supposed to just be resident in memory while a DLL is being utilized? When the DLLs are all present in the same directory as the .exe, it functions fine, and also doesn't create temporary .resource files in the directory, so I'm unsure what I'm looking to resolve.
Update 2:
Using a PDB of the DAC libraries, it appears the failing line is:
IOperation operation = DacServices.CreateDeploymentArtifactGenerationOperation(OperationResources.GenerateDeployScriptCaption, (ErrorManager errorManager) => this.CreatePackageToDatabaseDeployment(package.PackageSource, targetDatabaseName, dacDeployOption, errorManager), (IDeploymentController controller, DeploymentPlan plan, ErrorManager errorManager) => DacServices.WriteDeploymentScript(streamWriter, controller, plan, errorManager), cancellationToken1, dacLoggingContext);
And the resulting exceptions are:
The extension type Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanExecutor could not be instantiated.
and
The extension type Microsoft.SqlServer.Dac.Deployment.Internal.InternalDeploymentPlanModifier could not be instantiated.
I'm actually integrating the amazing RoslynPad into a WinForms application and working damn well.
The point of the integration is allowing the user to type in some C# code so it can be used in a future.
Thing is I'm interested on "capping" the user so he could just use some System or even LinQ functions. I don't want to allow the user to think he is allowed to use System.IO and others. Of course I can't prevent him/her typing System.IO.File.Delete, but will surely help if the System.IO's Assembly is not loaded into the RoslynPad's IntelliSense.
The source code typed by the user is going to be compiled locally before being saved into the DB. I'm adding just a few and necessary Assemblies for the compilation, so if System.IO it won't compile, of course.
As I explained, I just want to cap the Intellisense, so they don't think they have access to almost the whole .NET Framework.
EDIT: Added the actual implementation actually done. I'm loading "RoslynPad.Roslyn.Windows" and "RoslynPad.Editor.Windows" assemblies to the editor.
private RoslynCodeEditor _editor;
private void InitializeEditor(string sourceCode)
{
if (string.IsNullOrWhiteSpace(sourceCode))
sourceCode = string.Empty;
_editor = new RoslynCodeEditor();
var workingDirectory = Directory.GetCurrentDirectory();
var roslynHost = new RoslynHost(additionalAssemblies: new[]
{
Assembly.Load("RoslynPad.Roslyn.Windows"),
Assembly.Load("RoslynPad.Editor.Windows")
});
_editor.Initialize(roslynHost, new ClassificationHighlightColors(), workingDirectory, sourceCode);
_editor.FontFamily = new System.Windows.Media.FontFamily("Consolas");
_editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
_editor.FontSize = 12.75f;
elementHost1.Child = _editor;
this.Controls.Add(elementHost1);
}
You can use pass a RoslynHostReferences instance to the RoslynHost constructor, and decide which assemblies and namespaces are imported by default.
You could use the same logic as Default, just remove System.IO.Path from the type list.
Note that System.IO is not an assembly, but rather a namespace, which is in the core library, so there's no simple way to completely remove it.
I am aware of a class called AssemblyBuilder, and I would have thought I could use it to pass a folder containing C# source files, or pass a single C# source file to it in order to compile the source into an assembly (.dll) which can then be referenced in a config file.
I'm aware of csc.exe which can compile C#, and I'm effectively looking for a way to replicate this dynamically.
I couldn't figure out how to use AssemblyBuilder, or whether this is the wrong class to be using, or whether I should be doing something similar to the following:
http://support.microsoft.com/kb/304655
Can you point me in the right direction please.
You might want to look into CodeDomProvider
Example snippet:
CompilerParameters parms = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true,
IncludeDebugInformation = false
};
parms.ReferencedAssemblies.Add("System.dll");
parms.ReferencedAssemblies.Add("System.Data.dll");
CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp");
return compiler.CompileAssemblyFromSource(parms, source);
Warning: assemblies built dynamically in this fashion won't be handled by the garbage collector.
I'm stuck on run-time compilation and CodeDom.
Here's a simplified example of what I have so far.
public static void Testing()
{
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
string Output = "Out.exe";
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Drawing.Dll");
parameters.ReferencedAssemblies.Add("System.Windows.Forms.Dll");
parameters.CompilerOptions = "/t:winexe";
string[] text = new string[] { #"C:\MyProject\Test.cs", #"C:\MyProject\Test.Designer.cs",
#"C:\MyProject\Program.cs"};
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, text);
Process.Start(Output);
}
It works perfectly alright, and loads the Test form.
But! I need to pass a parameter to this Test form (a list of Panel controls) to populate the form.
How can I do this? Maybe, I am looking in the wrong direction, and it has to be done in a different way?
Thanks a lot in advance!
EDIT
In the end, I give up on CodeDom and used Mono.Cecil instead, injecting .exe file with information from my main program.
What you are doing is compiling an executable assembly then starting it in another process.
If you want to pass it information, command line arguments are one option. However, passing a .Net object on the command line will not work.
If you want to pass somthing managed you will have to use your new assembly with some late binding and pass your object to the constructor perhaps, rather depends what the code you are compiling accepts, if you have that at design time ...
Are you re-writing Visual Studio?