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();
Related
I have a problem with compiling code dynamically using below code:
CSharpCodeProvider provider = new CSharpCodeProvider();
// Build the parameters for source compilation.
CompilerParameters cp = new CompilerParameters();
// Add an assembly reference.
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Management.dll");
// Generate an executable instead of
// a class library.
cp.GenerateExecutable = true;
// Set the assembly file name to generate.
cp.OutputAssembly = "test.exe";
// Save the assembly as a physical file.
cp.GenerateInMemory = false;
// Invoke compilation.
CompilerResults cr = provider.CompileAssemblyFromFile(cp, "data.txt");
if (cr.Errors.Count > 0)
{
// Display compilation errors.
MessageBox.Show("Errors building {0} into {1}","data,txt");
foreach (CompilerError ce in cr.Errors)
{
MessageBox.Show(ce.ToString());
}
}
else
{
MessageBox.Show("Source {0} built into {1} successfully.","data.txt");
}
The code in data.txt file has line:
Properties.Resources.something
Now when I compile code dynamically it shows error:
Name "Properites" does not exist in current context.
I don't know how to fix it. There is no DLL named Properties.dll so I cannot refernce it.
I tried System.References.dll but It does not solve the problem.
Edit sample:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApp1
{
public static void Main()
{
byte[] resource = Properties.Resources.someresource;
}
}
Based on my search, I find that your source file is wrong. As usual, the source file should end with .cs instead of .txt.
I make a successful code example and you could have a look.
Code:
class Program
{
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// First parameter is the source file name.
if (File.Exists(args[0]))
{
CompileExecutable(args[0]);// Here args[0] is 'D:\\test.cs'
}
else
{
Console.WriteLine("Input source file not found - {0}",
args[0]);
}
}
else
{
Console.WriteLine("Input source file not specified on command line!");
}
Console.WriteLine("success");
Console.ReadKey();
}
public static bool CompileExecutable(String sourceName)
{
FileInfo sourceFile = new FileInfo(sourceName);
CodeDomProvider provider = null;
bool compileOk = false;
// Select the code provider based on the input file extension.
if (sourceFile.Extension.ToUpper(CultureInfo.InvariantCulture) == ".CS")
{
provider = CodeDomProvider.CreateProvider("CSharp");
}
else if (sourceFile.Extension.ToUpper(CultureInfo.InvariantCulture) == ".VB")
{
provider = CodeDomProvider.CreateProvider("VisualBasic");
}
else
{
Console.WriteLine("Source file must have a .cs or .vb extension");
}
if (provider != null)
{
// Format the executable file name.
// Build the output assembly path using the current directory
// and <source>_cs.exe or <source>_vb.exe.
String exeName = String.Format(#"{0}\{1}.exe",
System.Environment.CurrentDirectory,
sourceFile.Name.Replace(".", "_"));
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Console.dll");
cp.ReferencedAssemblies.Add("System.Resources.ResourceManager.dll");
cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");// I have changed here.
cp.GenerateExecutable = true;
// Specify the assembly file name to generate.
cp.OutputAssembly = exeName;
// Save the assembly as a physical file.
cp.GenerateInMemory = false;
// Set whether to treat all warnings as errors.
cp.TreatWarningsAsErrors = false;
// Invoke compilation of the source file.
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
sourceName);
if (cr.Errors.Count > 0)
{
// Display compilation errors.
Console.WriteLine("Errors building {0} into {1}",
sourceName, cr.PathToAssembly);
foreach (CompilerError ce in cr.Errors)
{
Console.WriteLine(" {0}", ce.ToString());
Console.WriteLine();
}
}
else
{
// Display a successful compilation message.
Console.WriteLine("Source {0} built into {1} successfully.",
sourceName, cr.PathToAssembly);
}
// Return the results of the compilation.
if (cr.Errors.Count > 0)
{
compileOk = false;
}
else
{
compileOk = true;
}
}
return compileOk;
}
}
.cs file
using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
namespace ConsoleApp1
{
public class Student
{
public static void Main()
{
using (ResXResourceWriter resx = new ResXResourceWriter(#".\CarResources.resx"))
{
resx.AddResource("Title", "Classic American Cars");
resx.AddResource("Name", "test1");
resx.AddResource("Age", "22");
}
Dictionary<string, string> pairs = new Dictionary<string, string>();
using (ResXResourceReader resxReader = new ResXResourceReader(#".\CarResources.resx"))
{
foreach (DictionaryEntry entry in resxReader)
{
pairs.Add(entry.Key.ToString(), entry.Value.ToString());
Console.WriteLine(entry.Value);
}
}
Console.WriteLine("success");
Console.ReadKey();
}
}
}
Result:
When you click the .exe file, you will get the following:
I`m use WIX.sharp for create msi.
Please help me: how to run exe file after installation servise?
Now it leads to an error (when I use my msi).
My application starts for init cofiguration file of service.
Now it seems like this:
project.Binaries = new[]
{
new Binary(new Id("StartAdmin"), "xxx.exe")
};
project.Actions = new WixSharp.Action[]
{
new BinaryFileAction("StartAdmin", "Executing ...", Return.check, When.After, Step.InstallExecute, Condition.NOT_Installed)
{
Execute = Execute.commit
}
Thanks.
I did what I asked.
I want to share the result.
using Microsoft.Deployment.WindowsInstaller;
...
var project = new Project("Application Name", GetAllEntities(releaseDir, parentDir, out service));
...
private static Dir GetDirs(string releaseDir, string parentDir, out File service)
{
var docDir = System.IO.Path.GetFullPath(System.IO.Path.Combine(parentDir, #"pub\\doc\\"));
return new Dir(new Id("SERVICEDIR"), #"%ProgramFiles%\Application\",
new Dir(new Id("INSTALLSERVICEDIR"), "App name",
new Dir(new Id("BINSERVICEDIR"), "bin",
...
new File(string.Format("{0}{1}", releaseDir, "Admin.exe")),
...
project.AddAction(new ManagedAction(CustomActions.MyAction, Return.check, When.After, Step.InstallFinalize, Condition.NOT_Installed));
...
public class CustomActions
{
[CustomAction]
public static ActionResult MyAction(Session session)
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = string.Format("{0}\\Admin.exe", session["BINSERVICEDIR"]);
process.StartInfo.Arguments = "-n";
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
process.Start();
process.WaitForExit();
return ActionResult.Success;
}
}
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())
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
}
}
I have some code to create an exe from c# source on-the-fly using the CSharpCodeProvider, here it is:
public static void BuildAssembly(string code)
{
Microsoft.CSharp.CSharpCodeProvider provider =
new CSharpCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
CompilerParameters compilerparams = new CompilerParameters();
compilerparams.ReferencedAssemblies.Add("System.dll");
compilerparams.GenerateExecutable = true;
compilerparams.GenerateInMemory = false;
compilerparams.OutputAssembly = #"C:\Users\me\AppData\Roaming\test.exe";
CompilerResults results =
compiler.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
foreach (CompilerError error in results.Errors)
{
errors.AppendFormat("Line {0},{1}\t: {2}\n",
error.Line, error.Column, error.ErrorText);
}
throw new Exception(errors.ToString());
}
else
{
}
}
all works fine, it compiles my console application c# code. But when I'm writing a console app and i requires that the console not be visible I simply go to the application properties and change output type from console to Windows Application. So how would I do this via the function I pasted above? I've looked through all the options but .. no idea :(
thanks.
Try this:
compilerparams.CompilerOptions = "/target:winexe";