How to compile a C# file with Roslyn programmatically? - c#

I read that you can't compile C# 6.0 with CSharpCodeProvider and therefor trying to do with with Roslyn. But I can't find a good example how to load a file and then compile it to a dll.
How should I write something similar to this code with Roslyn? Or is there some other way to do it? Now when I try to compile files that contain reference to projects with C# 6.0 code it just say "The type or namespace name 'x' does not exist in the namespace 'y' (are you missing an assembly reference?)"
public string CompileCode()
{
var provider = new CSharpCodeProvider();
var outputPath = Path.Combine(Path.GetDirectoryName(_path), $"Code.dll");
var compilerparams = new CompilerParameters(_referencedAssemblies, outputPath);
CompilerResults results = provider.CompileAssemblyFromFile(compilerparams, _path);
var dllPath = results.PathToAssembly;
if (!results.Errors.HasErrors)
return dllPath;
PrintError(results.Errors);
return "";
}
In summary I want to:
Load a C# file
Compile it to a dll so I can load it later.

I have created a sample for you to work with. You need to tweak it to use the run time for .Net 4.6 so that CSharp6 version is availble to you. I have added little details so that you can choose the options of compilations.
Changes required -
Change the path of runtime to target .Net 4.6
Change the LanguageVersion.Csharp5 to LanguageVersion.Csharp6 in below sample.
class Program
{
private static readonly IEnumerable<string> DefaultNamespaces =
new[]
{
"System",
"System.IO",
"System.Net",
"System.Linq",
"System.Text",
"System.Text.RegularExpressions",
"System.Collections.Generic"
};
private static string runtimePath = #"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\{0}.dll";
private static readonly IEnumerable<MetadataReference> DefaultReferences =
new[]
{
MetadataReference.CreateFromFile(string.Format(runtimePath, "mscorlib")),
MetadataReference.CreateFromFile(string.Format(runtimePath, "System")),
MetadataReference.CreateFromFile(string.Format(runtimePath, "System.Core"))
};
private static readonly CSharpCompilationOptions DefaultCompilationOptions =
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release)
.WithUsings(DefaultNamespaces);
public static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
{
var stringText = SourceText.From(text, Encoding.UTF8);
return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
static void Main(string[] args)
{
var fileToCompile = #"C:\Users\DesktopHome\Documents\Visual Studio 2013\Projects\ConsoleForEverything\SignalR_Everything\Program.cs";
var source = File.ReadAllText(fileToCompile);
var parsedSyntaxTree = Parse(source, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
var compilation
= CSharpCompilation.Create("Test.dll", new SyntaxTree[] { parsedSyntaxTree }, DefaultReferences, DefaultCompilationOptions);
try
{
var result = compilation.Emit(#"c:\temp\Test.dll");
Console.WriteLine(result.Success ? "Sucess!!" : "Failed");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}
This would need little tweaks but it should give you desired results. Change it as you may wish.

You have to use the NuGet package Microsoft.CodeAnalysis.CSharp.
var syntaxTree = CSharpSyntaxTree.ParseText(source);
CSharpCompilation compilation = CSharpCompilation.Create(
"assemblyName",
new[] { syntaxTree },
new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) },
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var dllStream = new MemoryStream())
using (var pdbStream = new MemoryStream())
{
var emitResult = compilation.Emit(dllStream, pdbStream);
if (!emitResult.Success)
{
// emitResult.Diagnostics
}
}

Related

can't Specify an audio profile using Google.Cloud.TextToSpeech.V1 in c#

I'm trying to convert Text To audio using Google.Cloud.TextToSpeech.V1. it works fine but I do not know how can I Specify an audio profile to use using c# while I found code in Node.js and python But Not anything in c# this is my code
static void Main(string[] args)
{
List<Word> lst = IntialData();
System.Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", #"C:\Users\Admin\TextToSpeechApiDemo\key.json");
var client = TextToSpeechClient.Create();
// The input to be synthesized, can be provided as text or SSML.
foreach (Word item in lst)
{
var input = new SynthesisInput
{
Text = item.Name,
};
// Build the voice request.
var voiceSelection = new VoiceSelectionParams
{
LanguageCode = "ar",
//SsmlGender = SsmlVoiceGender.Female,
Name = "ar-XA-Wavenet-A"
};
// Specify the type of audio file.
var audioConfig = new AudioConfig
{
AudioEncoding = AudioEncoding.Linear16,
};
// Perform the text-to-speech request.
var response = client.SynthesizeSpeech(input, voiceSelection, audioConfig);
// Write the response to the output file.
using (var output = File.Create(#"E:\Noursound\sim\ar-XA-Wavenet-A\" + item.Id.ToString() + ".wav"))
{
response.AudioContent.WriteTo(output);
}
}
}
I found this code in python he set effects_profile_id
audio_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.MP3,
effects_profile_id=[effects_profile_id],
How can i do that using c#
The problem was in the version on the NuGet package i used 1.0.0-beta01 , and it's not have the EffectsProfileId property but after update it to version to Google.Cloud.TextToSpeech.V1 version 2.3.0 i found the property.
using Google.Cloud.TextToSpeech.V1;
class Program
{
static void Main(string[] args)
{
var config = new AudioConfig
{
AudioEncoding = AudioEncoding.Mp3,
EffectsProfileId = { "your profile ID" }
};
}
}
i created git issue for that on github Here's a link!

How to use project methods with Roslyn?

I am using Roslyn to run C# code from text.
It works, but I can't figure out how can I use my project class methods, didn't find a way to reference my project methods. I can't use a dll as there too many classes and forms I need to use.
For exmaple, using the Number set method, or the DoMultiAction() method inside the roslyn ExecuteUserCodeTest() text code. How to do that they will be referenced?
Is it even possible? Any explanation or example will be appreciated.
My Code:
namespace bot1
{
class Testing
{
private int number;
public int Number
{
get { return number; }
set { this.number = value; }
}
public int DoMultiAction(int num1, int num2)
{
return num1 * num2 * Number;
}
public void ExecuteUserCodeTest()
{
String text = #"
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(String text)
{
MessageBox.Show(text);
}
}
}";
// define source code, then parse it (to the type used for compilation)
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(text);
// define other necessary objects for compilation
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Drawing.Point).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Windows.Forms.MessageBox).Assembly.Location),
MetadataReference.CreateFromFile(typeof (ScriptManagerHandler.ScriptHandler).Assembly.Location)
};
// analyse and generate IL code from syntax tree
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
// write IL code into memory
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
// handle exceptions
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
String error = "";
foreach (Diagnostic diagnostic in failures)
{
error += "" + diagnostic.Id + ", " + diagnostic.GetMessage() + "\n";
}
if (error != "")
MessageBox.Show(error);
}
else
{
// load this 'virtual' DLL so that we can use
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
// create instance of the desired class and call the desired function
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
}
}
}
}
}

C# Runtime Compilation with CSharpCodeProvider

I have had success using this tutorial: http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime to set up a framework for runtime compilation and execution of C# code. Below is the code I currently have:
public static class CodeCompiler {
public static object InterpretString(string executable) {
string compilation_string =
#"
static class RuntimeCompilationCode {
public static void Main() {}
public static object Custom() {
/* CODE HERE */
}
}";
compilation_string = compilation_string.Replace("/* CODE HERE */", executable);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters compiler_parameters = new CompilerParameters();
// True - memory generation, false - external file generation
compiler_parameters.GenerateInMemory = true;
// True - exe file generation, false - dll file generation
compiler_parameters.GenerateExecutable = true;
// Compile
CompilerResults results = provider.CompileAssemblyFromSource(compiler_parameters, compilation_string);
// Check errors
if (results.Errors.HasErrors) {
StringBuilder builder = new StringBuilder();
foreach (CompilerError error in results.Errors) {
builder.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(builder.ToString());
}
// Execute
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("RuntimeCompilationCode");
MethodInfo execute = program.GetMethod("Custom");
return execute.Invoke(null, null);
}
}
I can pass a statement in the form of a string (ex. "return 2;") to InterpretString() and it will be compiled and executed as part of the Custom() function. However I am wondering if it is possible to use the same approach to execute a method that is in my original file. For instance, suppose the CodeCompiler class had another method returnsTwo() which returns the integer 2. Is there a way to call such a method by passing "CodeCompiler.returnsTwo();" or a similar string to InterpretString()?
Provided that the function is a static function this should not be a problem, as long as you add the appropriate reference to the compilation. I've done this short of thing on several projects.
If the CodeCompiler is in your current executable you have to include the references in this fashion:
string exePath = Assembly.GetExecutingAssembly().Location;
string exeDir = Path.GetDirectoryName(exePath);
AssemblyName[] assemRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
List<string> references = new List<string>();
foreach (AssemblyName assemblyName in assemRefs)
references.Add(assemblyName.Name + ".dll");
for (int i = 0; i < references.Count; i++)
{
string localName = Path.Combine(exeDir, references[i]);
if (File.Exists(localName))
references[i] = localName;
}
references.Add(exePath);
CompilerParameters compiler_parameters = new CompilerParameters(references.ToArray())

Compile entire project from another project

I have a Windows Forms application, which I use to generate resource files. I'm trying to add such functionality to this application, that would allow me to compile another Windows Forms application into an executable, that would have these resources included. However, I'm stuck on that compile another Windows Forms project part.
I tried to follow this article, my code so far looks like this:
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.IncludeDebugInformation = true;
parameters.GenerateInMemory = false;
//parameters.TreatWarningsAsErrors = true;
parameters.WarningLevel = 3;
parameters.CompilerOptions = "/optimize";
parameters.OutputAssembly = "Program.exe";
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, new string[] { "Program.cs" });
I'm not exactly sure, if I'm doing this correctly (if I should compile Program.cs file). Program.cs file looks like this:
using System;
using System.Windows.Forms;
namespace example
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
And when I try to compile it with the code above, I get this error:
Line number 16, Error Number: CS0246, 'The type or namespace name 'Form1' could not be found (are you missing a using directive or an assembly reference?)
I'd really appreciate if you guys could help me out, I've never compiled anything from another project.
Assuming the other project you want to compile is project.csproj, reference Microsoft.Build.Framework and use this code:
var globalProperty = new Dictionary<string, string> { { "Configuration", "Debug"}, { "Platform", "Any CPU" } };
var buildParameters = new BuildParameters(new ProjectCollection()) { Loggers = new List<ILogger> { new ConsoleLogger() } };
var buildRequest = new BuildRequestData(#"C:\example\project.csproj", globalProperty, "4.0", new[] {"Build" }, null);
BuildResult buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequest);
Or you could just wrap the MsBuild.exe
var p = new Process();
p.StartInfo = new ProcessStartInfo(#"C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe");
p.StartInfo.Arguments = #"C:\example\project.csproj";
p.Start();

C# - Cannot getting a string from ResourceManager (from satellite assembly)

I'm developing a localisable application. In my "local" resource file, I've the language used by default (english) and if possible, I load the user's preference and culture and load strings translated in is language.
So what I've done :
private static CultureInfo _culture = CultureInfo.CurrentUICulture;
private static ResourceManager _manager;
private static void ToNeutralCulture()
{
while (!_culture.IsNeutralCulture)
{
_culture = _culture.Parent;
}
}
private static void LoadCulture()
{
ResourceManager manager = Properties.Resources.ResourceManager;
try
{
ToNeutralCulture();
string assembly = Assembly.GetCallingAssembly().GetName().CodeBase;
string assemblyDir = Path.GetDirectoryName(assembly);
string assemblyName = Path.GetFileNameWithoutExtension(assembly);
string resourceFileName = string.Format(CultureInfo.InvariantCulture,
#"{0}\{1}_{2}.dll",
assemblyDir,
assemblyName,
_culture.Name.ToUpper());
FileInfo resourceFile = new FileInfo(resourceFileName);
if (resourceFile.Exists)
{
Assembly resourceAssembly = Assembly.LoadFrom(resourceFile.FullName);
string[] manifests = resourceAssembly.GetManifestResourceNames();
if (manifests.Length == 1)
{
manager = new ResourceManager(manifests[0], resourceAssembly);
}
using (ResourceReader reader = new ResourceReader(resourceAssembly.GetManifestResourceStream(manifests[0])))
{
IDictionaryEnumerator dict = reader.GetEnumerator();
while (dict.MoveNext())
{
string key = dict.Key as string;
object val = dict.Value;
//string mVal = manager.GetString(key);
}
}
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture,
"Fail to loading culture {0}",
(_culture == null) ? "--" : _culture.EnglishName));
}
_manager = manager;
}
Assembly is correctly loaded and the enumerator will display me all resources present in the resource file, well, works fine except :
string mVal = manager.GetString(key);
When I uncommented this line, I've an System.Resources.MissingManifestResourceException, can someone tell me why?
Thanks !
[EDIT]
Project "MyApp"
namespace MyApp
{
Assembly resourceAssembly = Assembly.LoadFrom(resourceFileName);
string[] manifests = resourceAssembly.GetManifestResourceNames();
if (manifests.Length == 1)
{
manager = new ResourceManager(manifests[0], resourceAssembly);
}
// Throws the exception
manager.GetString("PleaseCallIT", null);
// Works
using (ResourceReader reader = new ResourceReader(resourceAssembly.GetManifestResourceStream(manifests[0])))
{
IDictionaryEnumerator dict = reader.GetEnumerator();
while (dict.MoveNext())
{
string key = dict.key as string; // PleaseCallIT
object val = dict.value; // Please call IT.
}
}
}
Project "MyApp_FR" (Resources.Designer.cs auto-generated file)
namespace MyApp.Properties {
// ...
internal static string PleaseCallIT {
get {
return ResourceManager.GetString("PleaseCallIT", resourceCulture);
}
}
}
I don't understand...
I found why, hope this will help someone that is in the same case.
So, I looked in MyApp_FR.dll the code generated to use the Resource file, it is :
new global::System.Resources.ResourceManager("MyApp_FR.Properties.Resources", typeof(Resources).Assembly);
but when retrieving the manifest file names, I got :
"MyApp_FR.Properties.Resources.resources"
Seems to be there is a .resource to much in this room... By removing it, I can use my ResourceManager normally, all works fine...
Final code :
Assembly resourceAssembly = Assembly.LoadFrom(resourceFileName);
string[] manifests = resourceAssembly.GetManifestResourceNames();
if (manifests.Length == 1)
{
string manifest = manifests[0].Replace(".resources", string.Empty);
manager = new ResourceManager(manifest, resourceAssembly);
}
// Works !
manager.GetString("PleaseCallIT", null);
From Microsoft Support:
This problem occurs if you use a localized resource that exists in a satellite assembly that you created by using a .resources file that has an inappropriate file name. This problem typically occurs if you manually create a satellite assembly:
Try this KB:
http://support.microsoft.com/kb/839861
An alternate approach, put in the following as test code:
string[] resources =
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
In debugging, check the contents of resources to see if it matches what you are loading with ResourceManager.
Especially note, if you get something like 'MyAssembly..Resources.resources', then you will need to explicitly add 'Resources' to the ResourceManager constructor:
private static readonly ResourceManager stringTable =
new ResourceManager("MyAssembly.Resources", Assembly.GetExecutingAssembly());

Categories