I've just got my own little custom c# compiler made, using the article from MSDN.
But, when I create a new Windows Forms application using my sample compiler, the MSDOS window also appears, and if I close the DOS window, my WinForms app closes too. How can I tell the Compiler? not to show the MSDOS window at all?
Thank you :)
Here's my code:
using System;
namespace JTS
{
public class CSCompiler
{
protected string ot,
rt,
ss, es;
protected bool rg, cg;
public string Compile(String se, String fe, String[] rdas, String[] fs, Boolean rn)
{
System.CodeDom.Compiler.CodeDomProvider CODEPROV = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp");
ot =
fe;
System.CodeDom.Compiler.CompilerParameters PARAMS = new System.CodeDom.Compiler.CompilerParameters();
// Ensure the compiler generates an EXE file, not a DLL.
PARAMS.GenerateExecutable = true;
PARAMS.OutputAssembly = ot;
PARAMS.CompilerOptions = "/target:winexe"; PARAMS.ReferencedAssemblies.Add(typeof(System.Xml.Linq.Extensions).Assembly.Location);
PARAMS.LinkedResources.Add("this.ico");
foreach (String ay in rdas)
{
if (ay.Contains(".dll"))
PARAMS.ReferencedAssemblies.Add(ay);
else
{
string refd = ay;
refd = refd + ".dll";
PARAMS.ReferencedAssemblies.Add(refd);
}
}
System.CodeDom.Compiler.CompilerResults rs = CODEPROV.CompileAssemblyFromFile(PARAMS, fs);
if (rs.Errors.Count > 0)
{
foreach (System.CodeDom.Compiler.CompilerError COMERR in rs.Errors)
{
es = es +
"Line number: " + COMERR.Line +
", Error number: " + COMERR.ErrorNumber +
", '" + COMERR.ErrorText + ";" +
Environment.NewLine + Environment.NewLine;
}
}
else
{
// Compilation succeeded.
es = "Compilation Succeeded.";
if (rn) System.Diagnostics.Process.Start(ot);
}
return es;
}
}
}
In C# compiler Console window is shown when /target switch is exe. When /target=winexe, Console window is not shown.
http://msdn.microsoft.com/en-us/library/6h25dztx.aspx
Try this:
System.CodeDom.Compiler.CompilerParameters PARAMS = new System.CodeDom.Compiler.CompilerParameters();
PARAMS->CompilerOptions = "/target:winexe";
See:
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.compilerparameters.compileroptions.aspx
I don't know which MSDN Article you are referring to, but if you use the AssemblyBuilder then the "magic" is in the call to SetEntryPoint.
If you have a Windows Forms application, you need to specify PEFileKinds.WindowApplication:
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName(assemblyName), AssemblyBuilderAccess.Save);
var mod = asm.DefineDynamicModule(assemblyName, fileName);
var type = mod.DefineType("Program",
TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public);
var mainMethod = type.DefineMethod("Main",
MethodAttributes.Public | MethodAttributes.Static);
// ... Code for Main method and the rest ...
type.CreateType();
asm.SetEntryPoint(mainMethod,PEFileKinds.WindowApplication);
asm.Save(fileName);
Other PEFileKinds are ConsoleApplication and Dll, although I think the AssemblyBuilder automatically assumes it's a Dll if you don't specify an EntryPoint.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed last year.
Improve this question
I want to create a GUI in C# that will be used to run keytool on cmd.exe behind the scenes to create a keystore, including a key, and certificate data.
Input data then requires
Keystore path
Password
Key alias
Key password
Validity
Certificate info (cn, ou, o, l, st and c)
Unfortunately people may type special characters in their passwords and also space is allowed in the certificate info.
Overall I am worried someone may input some information somewhere that can result in a disastrous command running behind the scenes once this is called (like rm -rf *).
Is there a way to pass a java properties file with the input information to keytool or is there any way that I can safely escape all the data that is passed as string parameters to keytool?
I could not find any type of file that keytool could take, even in separate steps, that would eliminate this issue.
here's the unsafe code (warning: IT'S UNSAFE!!):
using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class AndroidKeystoreCertificateData
{
public string FirstAndLastName;
public string OrganizationalUnit;
public string OrganizationName;
public string CityOrLocality;
public string StateOrProvince;
public string CountryCode;
}
public class AndroidKeystoreData : AndroidKeystoreCertificateData
{
public string KeystorePath;
public string Password;
public string KeyAlias;
public string KeyPassword;
public int ValidityInYears;
}
internal class AndroidUtils
{
private static bool RunCommand(string command, string working_dir, bool show_window = true)
{
using (Process proc = new Process
{
StartInfo =
{
UseShellExecute = false,
FileName = "cmd.exe",
Arguments = command,
CreateNoWindow = !show_window,
WorkingDirectory = working_dir
}
})
{
try
{
proc.Start();
proc.WaitForExit();
return true;
}
catch
{
return false;
}
}
return false;
}
private static string FilterString(string st)
{
return Regex.Replace(st, #"[^\w\d _]", "").Trim();
}
public static string GetKeystoreCertificateInputString(AndroidKeystoreCertificateData data)
{
string strCN = FilterString(data.FirstAndLastName);
string strOU = FilterString(data.OrganizationalUnit);
string strO = FilterString(data.OrganizationName);
string strL = FilterString(data.CityOrLocality);
string cnST = FilterString(data.StateOrProvince);
string cnC = FilterString(data.CountryCode);
string cert = "\"";
if (!string.IsNullOrEmpty(strCN)) cert += "cn=" + strCN + ", ";
if (!string.IsNullOrEmpty(strOU)) cert += "ou=" + strOU + ", ";
if (!string.IsNullOrEmpty(strO)) cert += "o=" + strO + ", ";
if (!string.IsNullOrEmpty(strL)) cert += "l=" + strL + ", ";
if (!string.IsNullOrEmpty(cnST)) cert += "st=" + cnST + ", ";
if (!string.IsNullOrEmpty(cnC)) cert += "c=" + cnC + "\"";
if (cert.Length > 2) return cert;
return string.Empty;
}
private static string GetKeytoolPath()
{
string javaHome = Environment.GetEnvironmentVariable("JAVA_HOME", EnvironmentVariableTarget.User);
return Path.Combine(javaHome, "bin\\keytool");
}
private static string GetKeystoreGenerationCommand(AndroidKeystoreData d)
{
string cert = GetKeystoreCertificateInputString(d);
string keytool = GetKeytoolPath();
string days = (d.ValidityInYears * 365).ToString();
string dname = "-dname \"cn=" + d.KeyAlias + "\"";
if (!string.IsNullOrEmpty(cert)) dname = "-dname " + cert;
string cmd = "echo y | " + keytool + " -genkeypair " + dname +
" -alias " + d.KeyAlias + " -keypass " + d.KeyPassword +
" -keystore " + d.KeystorePath + " -storepass " + d.Password + " -validity " + days;
return cmd;
}
public static bool RunGenerateKeystore(AndroidKeystoreData d)
{
string cmd = GetKeystoreGenerationCommand(d);
string wdir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return RunCommand(cmd, wdir, false);
}
}
An example usage would be:
using System;
class MainClass
{
static void Main(string[] args)
{
AndroidKeystoreData d = new AndroidKeystoreData();
d.KeystorePath = "keystorepath";
d.Password = "pass";
d.KeyAlias = "key0";
d.KeyPassword = "pass";
d.ValidityInYears = 25*365;
d.FirstAndLastName = "self";
d.OrganizationalUnit = "my ou";
d.OrganizationName = "my o";
d.CityOrLocality = "my city";
d.StateOrProvince = "my state";
d.CountryCode = "cc";
AndroidUtils.RunGenerateKeystore(d);
}
}
repository | zip file
Additional information on things I tried:
I am in .NET 4.6.2, and I know about CommandLineBuilderExtension, but it's docs starts saying to not use it: This API supports the product infrastructure and is not intended to be used directly from your code.
Xamarin related codebase seems to rely on whatever commandlinebuilderextension does
Looking the CommandLineBuilder.cs source code, I can't tell how well it escapes (it includes a comment on code injection) and what a minimum version of it for only the purpose of the code above would look like.
For now I have a very restrictive regex going on but I don't know if this may be problematic: people with non A-Za-z0-9 characters in their names, if someone wants to use special characters in their passwords and so on. Ideally if there's a way to pass parameters safely through a file, I would prefer. Or alternatively, some way to generate an Android compatible keystore in pure C# without relying in Java keytool.
Bluntely stealing code from MSBuild above I managed to cut things out and come up with something like below, which seems like about right as minimum with a similar enough functionality to be useful.
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace AndroidSignTool
{
public class CommandArgumentsBuilder
{
private StringBuilder Cmd { get; } = new StringBuilder();
private readonly Regex DefinitelyNeedQuotes = new Regex(#"^[a-z\\/:0-9\._\-+=]*$", RegexOptions.None);
private readonly Regex AllowedUnquoted = new Regex(#"[|><\s,;""]+", RegexOptions.IgnoreCase);
private bool IsQuotingRequired(string parameter)
{
bool isQuotingRequired = false;
if (parameter != null)
{
bool hasAllUnquotedCharacters = AllowedUnquoted.IsMatch(parameter);
bool hasSomeQuotedCharacters = DefinitelyNeedQuotes.IsMatch(parameter);
isQuotingRequired = !hasAllUnquotedCharacters;
isQuotingRequired = isQuotingRequired || hasSomeQuotedCharacters;
}
return isQuotingRequired;
}
private void AppendTextWithQuoting(string unquotedTextToAppend)
{
if (string.IsNullOrEmpty(unquotedTextToAppend))
return;
bool addQuotes = IsQuotingRequired(unquotedTextToAppend);
if (addQuotes)
{
Cmd.Append('"');
}
// Count the number of quotes
int literalQuotes = 0;
for (int i = 0; i < unquotedTextToAppend.Length; i++)
{
if (unquotedTextToAppend[i] == '"')
{
literalQuotes++;
}
}
if (literalQuotes > 0)
{
// Replace any \" sequences with \\"
unquotedTextToAppend = unquotedTextToAppend.Replace("\\\"", "\\\\\"");
// Now replace any " with \"
unquotedTextToAppend = unquotedTextToAppend.Replace("\"", "\\\"");
}
Cmd.Append(unquotedTextToAppend);
// Be careful any trailing slash doesn't escape the quote we're about to add
if (addQuotes && unquotedTextToAppend.EndsWith("\\", StringComparison.Ordinal))
{
Cmd.Append('\\');
}
if (addQuotes)
{
Cmd.Append('"');
}
}
public CommandArgumentsBuilder()
{
}
public void AppendSwitch(string switchName)
{
if (string.IsNullOrEmpty(switchName))
return;
if (Cmd.Length != 0 && Cmd[Cmd.Length - 1] != ' ')
{
Cmd.Append(' ');
}
Cmd.Append(switchName);
}
public void AppendSwitchIfNotNull(string switchName, string parameter)
{
if (string.IsNullOrEmpty(switchName) || string.IsNullOrEmpty(parameter))
return;
AppendSwitch(switchName);
AppendTextWithQuoting(parameter);
}
public override string ToString() => Cmd.ToString();
}
}
then the rewritten GetKeystoreGenerationCommand becomes this
public static string GetKeystoreGenerationCommand(AndroidKeystoreData d)
{
string cert = GetKeystoreCertificateInputString(d);
string keytool = "%JAVA_HOME%\\bin\\keytool" ;// GetKeytoolPath();
string days = (d.ValidityInYears * 365).ToString();
if (!string.IsNullOrEmpty(cert)) cert = d.KeyAlias;
var cmd = new CommandArgumentsBuilder();
cmd.AppendSwitch("echo y | " + keytool);
cmd.AppendSwitch("-genkeypair");
cmd.AppendSwitchIfNotNull("-dname", cert);
cmd.AppendSwitchIfNotNull("-alias", d.KeyAlias);
cmd.AppendSwitchIfNotNull("-keypass", d.KeyPassword);
cmd.AppendSwitchIfNotNull("-storepass", d.Password);
cmd.AppendSwitchIfNotNull("-keystore", d.KeystorePath);
cmd.AppendSwitchIfNotNull("-validity", days);
return cmd.ToString();
}
I believe that invoking the keytool binary directly instead of cmd.exe would do the trick if you don't want the user to inject shell commands.
I am working on functionality that allows users to write VB scripts and attach them to controls as actions. If user wants to perform something more or something specific he can write it in VB script and attach it, for example, to button (whole application is something like powerpoint presentation but with dynamic data etc).
The problem is i have issue compiling the script in C# project.
Input is at least two files (one main script, and other are additional).
This is first method that prepares references adn calls the compilation method.
public CompilerErrorCollection CompileClient(string fname, string[] VbFiles)
{
string currentDir = CurrentWorkingDir();
List<string> names = new List<string>();
string[] anames = CompilerHelper.DefaultReferencedAssembliesWpf;
foreach (string s in anames)
{
names.Add(s);
}
AddReference(names, Path.Combine(currentDir, "PresentationBase.dll"), true);
var sFolderPath = currentDir + "\\S";
AddReference(names, Path.Combine(sFolderPath, "Client.WPF.Common.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "Client.WPF.CommonControls.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "Client.WPF.CommonModels.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "Client.WPF.Presentation.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "Client.WPF.SyncfusionControls.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "ClientAd.WPF.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "ClientCore.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "ClientInfrastructure.dll"), true);
AddReference(names, Path.Combine(sFolderPath, "ClientWcf.dll", true),
string filenamestamp = fname + ".stamp";
string stampPath = Path.Combine(currentDir, filenamestamp);
try
{
string uid = Funcs.UniqeID(true, 12);
TextWriter wr2 = File.CreateText(stampPath);
wr2.WriteLine(uid + " " + DateTime.Now);
wr2.Close();
}
catch (Exception ex)
{
/ ... /
}
CompilerErrorCollection lcErrors;
CompilerHelper.Compile(VbFiles, out lcErrors, names, fname);
return lcErrors;
}
Compile method:
public static Assembly Compile(string[] files, out CompilerErrorCollection errors,
List<string> referencedAssemblies, string outputAssemblyFileName)
{
try
{
FileInfo sourceFile = new FileInfo(files[0]);
CodeDomProvider provider = null;
errors = new CompilerErrorCollection();
bool compileOk = false;
if (sourceFile.Extension.ToUpper(CultureInfo.InvariantCulture) == ".VB")
{
//provider = CodeDomProvider.CreateProvider("VisualBasic");
provider = new VBCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
}
else
{
Console.WriteLine("Source file must have a .vb extension");
}
if (provider != null)
{
CompilerParameters cp = new CompilerParameters();
// Generate an executable instead of
// a class library.
cp.GenerateExecutable = false;
// Specify the assembly file name to generate.
cp.OutputAssembly = outputAssemblyFileName; //exeName;
// Save the assembly as a physical file.
cp.GenerateInMemory = true;
// Set whether to treat all warnings as errors.
cp.TreatWarningsAsErrors = false;
cp.WarningLevel = 4;
cp.TempFiles.KeepFiles = false;
cp.IncludeDebugInformation = true;
string[] names = new string[referencedAssemblies.Count];
int nr = 0;
foreach (string name in referencedAssemblies)
{
names[nr] = name;
nr++;
}
cp.ReferencedAssemblies.AddRange(names);
// Invoke compilation of the source file.
//CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourceName);
CompilerResults cr = provider.CompileAssemblyFromFile(cp, files);
errors = cr.Errors;
if (cr.Errors.Count > 0)
{
// Display compilation errors.
Debug.WriteLine("Errors building assembly into {0}", cr.PathToAssembly);
foreach (CompilerError ce in cr.Errors)
{
Debug.WriteLine(" {0}", ce.ToString());
Debug.WriteLine("");
}
}
else
{
// Display a successful compilation message.
Debug.WriteLine("Source built into {0} successfully.", cr.PathToAssembly);
}
// Return the results of the compilation.
if (cr.Errors.Count > 0)
{
return null;
}
else
{
return cr.CompiledAssembly;
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
throw;
}
return null;
}
And this are references from system:
public static string[] DefaultReferencedAssembliesWpf =
{
NetRootFolderx86_En + "System.dll",
NetRootFolderx86_En + "System.Core.dll",
NetRootFolderx86_En + "System.Data.dll",
NetRootFolderx86_En + "System.Data.DataSetExtensions.dll",
NetRootFolderx86_En + "System.Windows.Forms.dll",
NetRootFolderx86_En + "PresentationCore.dll",
};
After line with CompilerResults cr = provider.CompileAssemblyFromFile(cp, files); i got those errors:
error BC30009: Reference required to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' containing the implemented interface 'System.Collections.Generic.IList`1'. Add one to your project.} object {System.CodeDom.Compiler.CompilerError}
error BC30451: 'MessageBox' is not declared. It may be inaccessible due to its protection level.} object {System.CodeDom.Compiler.CompilerError}
error BC30009: Reference required to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' containing the implemented interface 'System.Collections.Generic.IList`1'. Add one to your project.} object {System.CodeDom.Compiler.CompilerError}
After i add reference netstandard i get another error with System.Windows.Browser, then with System.Private.CoreLib.dll and adding those don't help.
Does someone have any experience in compiling in similar way or know what I am missing?
The scripts compiles fine without errors when i create VB Console app Project in visual studio.
Why do I even need netstandard in this project?
Ok, so i have reinstalled .Net Framework 4.8 and after attaching netstandard.dll from .Net Framework directory everything is fine now.
I am creating a /kind of/ custom compiler for a project. What I am doing is having users enter lines of code into either a textbox, or they can import some from text files.
I've been trying to test this for the last few days, with no results. I have a class, called Pressure, where I have a public method called 'emit' which simply shows a text box, like so...
public void emit()
{
MessageBox.Show("HOORA!");
}
and I have a text file named "testCompile.txt" as follows:
PressureTransducer pt = new PressureTransducer(0,0);
pt.emit();
which, when inserted into VS compiles just fine, as it should. Afterwards, I try to compile the file like so...
String sourceName = #"C:\Users\Devic\Desktop\CompileTester\testCompile.txt";
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
//cp.OutputAssembly = null;
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
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);
}
but VS gives me the error:
c:\Users\Devic\Desktop\CompileTester\testCompile.txt(1,29) : error CS1518: Expected class, delegate, enum, interface, or struct
The thread 0x1290 has exited with code 0 (0x0).
Any ideas as to what is going on?
You need to encapsulate the code from your text file into a usable class and method.
Below is code I've been using for a few years that allows C# scripts to run within my app and it even passes in a user defined variable. I had other parameters being passed in within my code to let the script writer have full access to other existing class instances, but I stripped those out as they are unique to my software. You could do the same if you want to provide access to any existing classes or forms in your app.
To use your class PressureTransducer you will need to ensure the DLL which declares that type is properly referenced and the namespace is included in the using section of the Fake code encapsulation. However I have a section built in to automatically reference all assemblies currently referenced by your running program, so that usually takes care of everything automatically.
Also, this takes the code in as a string for the source code and generates the assembly into memory, so there is no disk access - it runs very fast.
NOTE: There is use of an obsolete function in there, codeProvider.CreateCompiler();, but it's still working for me. I probably should update it eventually though.
private static object RunCSharpCode(string CSharpCode, bool ShowErrors, string StringParameter)
{
try
{
#region Encapsulate Code into a single Method
string Code =
"using System;" + Environment.NewLine +
"using System.Windows.Forms;" + Environment.NewLine +
"using System.IO;" + Environment.NewLine +
"using System.Text;" + Environment.NewLine +
"using System.Collections;" + Environment.NewLine +
"using System.Data.SqlClient;" + Environment.NewLine +
"using System.Data;" + Environment.NewLine +
"using System.Linq;" + Environment.NewLine +
"using System.ComponentModel;" + Environment.NewLine +
"using System.Diagnostics;" + Environment.NewLine +
"using System.Drawing;" + Environment.NewLine +
"using System.Runtime.Serialization;" + Environment.NewLine +
"using System.Runtime.Serialization.Formatters.Binary;" + Environment.NewLine +
"using System.Xml;" + Environment.NewLine +
"using System.Reflection;" + Environment.NewLine +
"public class UserClass" + Environment.NewLine +
"{" + Environment.NewLine +
"public object UserMethod( string StringParameter )" + Environment.NewLine +
"{" + Environment.NewLine +
"object Result = null;" + Environment.NewLine +
Environment.NewLine +
Environment.NewLine +
CSharpCode +
Environment.NewLine +
Environment.NewLine +
"return Result;" + Environment.NewLine +
"}" + Environment.NewLine +
"}";
#endregion
#region Compile the Dll to Memory
#region Make Reference List
Assembly[] FullAssemblyList = AppDomain.CurrentDomain.GetAssemblies();
System.Collections.Specialized.StringCollection ReferencedAssemblies_sc = new System.Collections.Specialized.StringCollection();
foreach (Assembly ThisAssebly in FullAssemblyList)
{
try
{
if (ThisAssebly is System.Reflection.Emit.AssemblyBuilder)
{
// Skip dynamic assemblies
continue;
}
ReferencedAssemblies_sc.Add(ThisAssebly.Location);
}
catch (NotSupportedException)
{
// Skip other dynamic assemblies
continue;
}
}
string[] ReferencedAssemblies = new string[ReferencedAssemblies_sc.Count];
ReferencedAssemblies_sc.CopyTo(ReferencedAssemblies, 0);
#endregion
Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.ICodeCompiler CSharpCompiler = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters(ReferencedAssemblies);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = false;
parameters.OutputAssembly = "ScreenFunction";
System.CodeDom.Compiler.CompilerResults CompileResult = CSharpCompiler.CompileAssemblyFromSource(parameters, Code);
#endregion
if (CompileResult.Errors.HasErrors == false)
{ // Successful Compile
#region Run "UserMethod" from "UserClass"
System.Type UserClass = CompileResult.CompiledAssembly.GetType("UserClass");
object Instance = Activator.CreateInstance(UserClass, false);
return UserClass.GetMethod("UserMethod").Invoke(Instance, new object[] { StringParameter });
#endregion
}
else // Failed Compile
{
if (ShowErrors)
{
#region Show Errors
StringBuilder ErrorText = new StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError Error in CompileResult.Errors)
{
ErrorText.Append("Line " + (Error.Line - 1) +
" (" + Error.ErrorText + ")" +
Environment.NewLine);
}
MessageBox.Show(ErrorText.ToString());
#endregion
}
}
}
catch (Exception E)
{
if (ShowErrors)
MessageBox.Show(E.ToString());
}
return null;
}
You might consider looking at the new Roslyn compiler. You pass a string to the Execute method in the script engine class and it will execute the code on-the-fly.
public class CSharpScriptEngine
{
private static Script _previousInput;
private static Lazy<object> _nextInputState = new Lazy<object>();
public static object Execute(string code)
{
var script = CSharpScript.Create(code, ScriptOptions.Default).WithPrevious(_previousInput);
var endState = script.Run(_nextInputState.Value);
_previousInput = endState.Script;
_nextInputState = new Lazy<object>(() => endState);
return endState.ReturnValue;
}
}
See this article for credit and a complete implementation.
I found this program ( http://support.microsoft.com/kb/304655 ) where i compile the code during runtime, It works for code that uses the reference,
using System;
Following is the the code for the program that compiles code during runtime,
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
string Output = "Out.exe";
Button ButtonObject = (Button)sender;
textBox2.Text = "";
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
//Make sure we generate an EXE, not a DLL
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
CompilerResults results = icc.CompileAssemblyFromSource(parameters, textBox1.Text);
if (results.Errors.Count > 0)
{
textBox2.ForeColor = Color.Red;
foreach (CompilerError CompErr in results.Errors)
{
textBox2.Text = textBox2.Text +
"Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";" +
Environment.NewLine + Environment.NewLine;
}
}
else
{
//Successful Compile
textBox2.ForeColor = Color.Blue;
textBox2.Text = "Success!";
//If we clicked run then launch our EXE
if (ButtonObject.Text == "Run") Process.Start(Output);
}
And Following is the code i need to compile at runtime,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
namespace Tsubame
{
class Program
{
static void Main(string[] args)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(#"url");
// Create Client
WebClient client = new WebClient();
// Assign Credentials
client.Credentials = new NetworkCredential("user", "pass");
//Grab Data
var data = client.DownloadString(#"url");
JObject o = JObject.Parse(data);
string getFristRow = Convert.ToString(o["Body"][0]["RowId"]);
string encaplulateStart = "\\\"";
string encaplulateEnd = "\\\":";
List<string> _matches = new List<string>();
_matches = Regex.Matches(getFristRow, #"(?<=" + encaplulateStart + ").*(?=" + encaplulateEnd + ")")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
foreach (string head in _matches)
{
Console.WriteLine(head);
}
Console.ReadLine();
}
}
}
But when I input this gives the error code,
Error Number: CS0234
For the references other than System. May I please know how to add additional references during runtime so that it can compile sucessfully :) Thank you very much :)
You need to add the references in CompilerParameters using CompilerParameters.ReferencedAssemblies:
var parameters = CompilerParameters
{
GenerateExecutable = true,
OutputAssembly = Output,
ReferencedAssemblies = {
"System.dll",
"System.Core.dll",
// etc
}
};
(Of course you don't have to use object initializer syntax to set this up, but it makes it neater IMO.)
I have used the built in C# methods to write a compiler, like the following:
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
string Output = "Out.exe";
Button ButtonObject = (Button)sender;
this.RadTextBox1.Text = string.Empty;
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
//Make sure we generate an EXE, not a DLL
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, RadTextBox1.Text);
if (results.Errors.Count > 0)
{
RadTextBox2.ForeColor = Color.Red;
foreach (CompilerError CompErr in results.Errors)
{
RadTextBox2.Text = RadTextBox2.Text +
"Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";" +
Environment.NewLine + Environment.NewLine;
}
}
else
{
//Successful Compile
RadTextBox2.ForeColor = Color.Blue;
Guid guid = Guid.NewGuid();
string PathToExe = Server.MapPath(Path.Combine(#"\Compiled" , Output));
FileStream fs = System.IO.File.Create(PathToExe);
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(RadTextBox1.Text);
}
Response.WriteFile(PathToExe);
When I run this code and write a Main method (such as the code sample in http://msdn.microsoft.com/en-us/library/ms228506(VS.80).aspx, I get this error:
Line number 0, Error Number: CS5001, 'Program 'c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Out.exe' does not contain a static 'Main' method suitable for an entry point;
The code above is used as the basis of a compiler on my site (not yet live). So you type in code and generate an .exe assembly. But when I enter code into the textbox for code writing (Radtextbox1), even with a main method, I get the error.
What gives?
Thanks
The entry point function is special: you can't just add a method called "main" to the assembly. Instead you must add an instance of the CodeEntryPointMethod type to one of your classes.
See http://blogs.msdn.com/bclteam/archive/2005/10/01/475768.aspx for more information on some of the limitations of using the CodeEntryPointMethod.