I made a simple compiler to learn about CodeDom. But it does not work, when I try to open my compiled file it does nothing.
When I run the code and slect an dir to save the exe file the exe file is generated, but when I click the exe file it does nothing.
The builder:
using System;
using System.IO;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Windows.Forms;
namespace TestBuilder
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void build(string output, string title, string msg)
{
CompilerParameters p = new CompilerParameters();
p.GenerateExecutable = true;
p.ReferencedAssemblies.AddRange(new String[] { "System.dll"});
p.OutputAssembly = output;
p.CompilerOptions = "/t:winexe";
string source = File.ReadAllText(#"C:\Users\Gebruiker\Documents\visual studio 2015\Projects\TestCompiler\Test\Program.cs");
string errors = String.Empty;
source = source.Replace("[MESSAGE]", msg);
CompilerResults results = new CSharpCodeProvider().CompileAssemblyFromSource(p, source);
if (results.Errors.Count > 0)
{
foreach (CompilerError err in results.Errors)
{
errors += "Error: " + err.ToString() + "\r\n\r\n";
}
}
else errors = "Successfully built:\n" + output;
MessageBox.Show(errors, "Build", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "EXE files (*.exe)|*.exe";
DialogResult result = sfd.ShowDialog();
if (result == DialogResult.OK)
{
build(sfd.FileName, textBox1.Text, textBox1.Text);
}
}
}
}
The program.cs file:
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("[MESSAGE]");
Console.ReadLine();
}
}
}
How can I fix this problem, so when I compile a file and execute it, it shows the message I put in the textbox?
Change p.CompilerOptions = "/t:winexe"; to p.CompilerOptions = "/t:exe";.
After that the compiled program should output whatever you've put inside your TextBox when you run it.
Source
Use /target:exe to create a console application.
Related
I am developing a small app that accepts two different types of files (*.miz and *.5js). There are two functions that do parse those files, and they already work properly when triggered from button click events (see below). Also, the solution contains this project and a setup project, to create the installer of the app.
public void button1_Click(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
openFileDialog1.InitialDirectory = "";
openFileDialog1.Title = "Load mission file";
openFileDialog1.ShowDialog();
textBox4.Text = openFileDialog1.FileName;
processMizFile(openFileDialog1.FileName);
button1.BackColor = SystemColors.Control;
}
and
private void button6_Click(object sender, EventArgs e)
{
button6.BackColor = Color.Red;
openFileDialog2.InitialDirectory = "";
openFileDialog2.Title = "Load standalone Datacards";
openFileDialog2.ShowDialog();
loadDatacard(openFileDialog2.FileName);
button6.BackColor = SystemColors.Control;
}
I am now trying to call those functions when I start the app by double clicking in one of those file types (associated to my app through Windows properties dialog). For that, I am running the following code:
public Form1()
{
LoadFont();
InitializeComponent();
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();//to display program version in the form
label29.Text += version;
string[] cmdl = Environment.GetCommandLineArgs();
using (StreamWriter sw = File.CreateText(Application.UserAppDataPath + #"\log.txt"))
{
if (cmdl.Length > 1)
{
sw.WriteLine("Argument 0 - Exe path: " + cmdl[0]);
sw.WriteLine("Argument 1: " + cmdl[1]);
sw.WriteLine("File type: " + Path.GetExtension(cmdl[1]).ToUpper());
}
else
{
sw.WriteLine("Argument 0 - Exe path: " + cmdl[0]);
sw.WriteLine("No further arguments provided");
}
}
if (cmdl.Length > 1)
{
if (Path.GetExtension(cmdl[1]).ToUpper() == ".MIZ")
{
processMizFile(cmdl[1]);
using (StreamWriter sw2 = File.CreateText(Application.UserAppDataPath + #"\log2.txt"))
{
sw2.WriteLine("MIZ file loaded!");
}
}
else if (Path.GetExtension(cmdl[1]).ToUpper() == ".5JS")
{
loadDatacard(cmdl[1]);
using (StreamWriter sw2 = File.CreateText(Application.UserAppDataPath + #"\log2.txt"))
{
sw2.WriteLine("5Js file loaded!");
}
}
}
}
The streamwriter parts are in place to have some traces, as I am not sure how can I debug this, otherwise.
Now, the problem is that it all works properly on the same laptop where I have Visual Studio installed. But, it does not work on a different computer:
Release Build
If I just double click one of the associated files(on the Desktop), the splash screen flashes quickly, and goes away. No log file is written
If I double click an associated file in the same path as the installed executable, it works fine
If I launch the app from command line, passing a path as argument, I get:
log.txt:
Argument 0 - Exe path: DatacardGenerator.exe
Argument 1: C:\Users\Username\OneDrive\Desktop\Datacards.5js
File type: .5JS
log3.txt: writes up to "Created path to extract dir: " and the right Appdata folder to temporary extract
using (StreamWriter sw3 = File.AppendText(Application.UserAppDataPath + #"\log3.txt"))
{
sw3.WriteLine("Created path to extract dir: " + extract5Js);
}
if (Directory.Exists(extract5Js))
{
System.IO.Directory.Delete(extract5Js, true);
}
using (StreamWriter sw3 = File.AppendText(Application.UserAppDataPath + #"\log3.txt"))
{
sw3.WriteLine("Proceeding to unzip...");
}
ZipFile.ExtractToDirectory(path5jscard, extract5Js);
So, how could I know what happens during the startup of my app when launched by an external file? I`ve set command line arguments in debug mode to test, but I am not sure if they come with full path or not, given the different results on the target machine (works if files in same directory as exe, does not work otherwise)
Thanks for the support!! :)
P.S: I do not process the command line arguments directly in Main() because I am using 2 different forms: Form2 is a splash screen, and Form1 is the main program. For that I am using an example I found time ago, in this way:
namespace WindowsFormsApp2{
static class Program{
static private List<PrivateFontCollection> _fontCollections;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
CultureInfo culture;
culture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
//Dispose all used PrivateFontCollections when exiting
Application.ApplicationExit += delegate {
if (_fontCollections != null)
{
foreach (var fc in _fontCollections) if (fc != null) fc.Dispose();
_fontCollections = null;
}
string appPath = Application.UserAppDataPath;
string parentPath = System.IO.Directory.GetParent(appPath).ToString();
System.IO.Directory.Delete(parentPath, true);
};
//Application.Run(new Form1());
new MyApp().Run(args);
}
}
public class MyApp : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new Form2();
}
protected override void OnCreateMainForm()
{
// Do your initialization here
//...
// Then create the main form, the splash screen will automatically close
this.MainForm = new Form1();
}
}
}
Solved!
I just had to declare a global var in Main to be accessed from my form, like:
public static string[] cmdlArgs;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
CultureInfo culture;
culture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
cmdlArgs = Environment.GetCommandLineArgs();
And then it can be accessed using the namespace :
Program.cmdlArgs
I have this code in C# that works fine in some users.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace Rename_OST
{
class Program
{
static public void killOutlook()
{
try
{
string process = "OUTLOOK";
foreach (Process outLook in Process.GetProcessesByName(process))
{
outLook.Kill();
}
}
catch (Exception) { }
}
static public void startOutlook()
{
try
{
//busca el path del Outlook
Process.Start("OUTLOOK");
}
catch (Exception)
{
Console.WriteLine("Could'n open Outlook. Please start Outlook and press any key.");
Console.ReadKey();
}
}
static public void replaceOutlook()
{
string ostPath = "C:\\Users\\" + Environment.UserName + "\\AppData\\Local\\Microsoft\\Outlook\\";
string ostFile = "Outlook.ost";
string ostNewFile = "Outlook.ost.txt";
try
{
if (!File.Exists(ostPath + ostNewFile))
{
File.Move(ostPath + ostFile, ostPath + ostNewFile);
}
else
{
File.Delete(ostPath + ostNewFile);
File.Move(ostPath + ostFile, ostPath + ostNewFile);
}
}
catch (FileNotFoundException)
{
Console.WriteLine("The OST file was not found.");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadKey();
}
}
static void Main(string[] args)
{
Console.WriteLine("Closing Outlook client...");
killOutlook();
Console.WriteLine("Replacing OST file name...");
Thread.Sleep(5000);
replaceOutlook();
Thread.Sleep(5000);
Console.WriteLine("Starting Outlook client...");
startOutlook();
}
}
}
The code only works if the file is named outlook.ost. How can I change the code in order that rename the OST file search regardless of the name.
Thanks in advance
Iterate through the files in the directory to check if they're .OST and then rename them.
// GET ALL FILES IN DIRECTORY
string[] fileEntries = Directory.GetFiles(ostPath);
// CHECK EACH FILE
foreach (string fileName in fileEntries)
{
// IS IT AN OST?
if (Path.GetExtension(fileName).ToLower() == ".ost")
{
// RENAME LOGIC HERE, EXAMPLE:
File.Move(fileName, fileName + ".OLD");
}
}
You should be careful hard coding the .OST path like that. Something like the below would be better:
string ostPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), #"Microsoft\Outlook");
Edit
An better example of replaceOutlook(). Still needs work but better illustrates how this works for OP.
static public void replaceOutlook()
{
// OST PATH
string ostPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), #"Microsoft\Outlook");
// LIST OF FILE PATHS IN OST PATH
string[] fileEntries = Directory.GetFiles(ostPath);
try
{
// CHECK EACH FILE PATH
foreach (string fileName in fileEntries)
{
// IS IT AN OST?
if (Path.GetExtension(fileName).ToLower() == ".ost")
{
// TRY AND DELETE OLD FILE, WON'T THROW EXCEPTION IF FILE DOESN'T EXIST
File.Delete(fileName + ".OLD");
// RENAME FILE
File.Move(fileName, fileName + ".OLD");
}
}
}
catch
{
// Blah blah....
}
}
I'm using the code:
SaveFileDialog sfd = new SaveFileDialog();
sfd.ShowDialog();
string source = Properties.Resources.source;
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
string Output = sfd.FileName + ".exe";
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.CompilerOptions = "/target:winexe";
parameters.ReferencedAssemblies.Add("mscorlib.dll");
parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
parameters.ReferencedAssemblies.Add("System.Management.dll");
parameters.ReferencedAssemblies.Add("System.Drawing.dll");
parameters.ReferencedAssemblies.Add("System.Runtime.InteropServices.dll");
parameters.ReferencedAssemblies.Add("System.DirectoryServices.AccountManagement.dll");
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, Properties.Resources.source);
if (results.Errors.Count > 0)
{
foreach (CompilerError CompErr in results.Errors)
{
MessageBox.Show("Error on line #" + CompErr.Line + " " + CompErr.ErrorText);
}
}
else
{
MessageBox.Show("Successfully Compiled.");
}
To compile my source, which is:
using System;
static void Main(string[] args)
{
}
I'm getting the error:
Error on line #0 Program 'c:\Users\Tom\Desktop\s.exe' does not contain a static 'Main' method suitable for an entry point
From Googling and looking here, I am unable to find why this is throwing an error..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
public static void Main(string[] args)
{
}
}
}
This is also not working, I'm getting the same error.
When debugging the code, Visual Studio gives me a "format exception was unhandled" while highlighting this line of code: 'CustObj.d_CustDiscount = Convert.ToDecimal(gs_InPutBuffer.Substring(000, 004));'
I have been googling correct formats and have not come up with any fixes. So my main question is, how do I rewrite this code in order for it to work?
Thanks for any help!
Below is the full program if you need to reference it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using CustFile_DLL;
namespace FileConvertorPA03
{
class Program
{
//add these to handle I/O
//instatiate streamreader
private static StreamReader TextfileIn = new StreamReader("customers.txt");
//instantiate the dll
private static CustFileClass CustObj = new CustFileClass();
//a few vars
private static string gs_InPutBuffer = "";
private static Int32 gi_TotalRec = 0, gi_FirstRecNo = 0;
private static bool gb_FirstRec = true;
static void Main(string[] args)
{
while ((gs_InPutBuffer = TextfileIn.ReadLine()) != null)
{
ParsetoAttributes();
CustObj.AddObject();
}//end while
PopMessageBox();
TextfileIn.Close();
}//end main
//method to parse input buffer to class attributes
private static void ParsetoAttributes()
{
CustObj.s_CustName = gs_InPutBuffer.Substring(000, 033).Trim();
CustObj.s_CustAddress = gs_InPutBuffer.Substring(033, 032).Trim();
CustObj.s_CustZip = gs_InPutBuffer.Substring(065, 005);
CustObj.s_CustPhone = gs_InPutBuffer.Substring(070, 010);
CustObj.d_CustDiscount = Convert.ToDecimal(gs_InPutBuffer.Substring(000, 004));
}//end parse attributes
//method to count records added
static void CountRecs()
{
if (gb_FirstRec == true)
{
gi_FirstRecNo = CustObj.i_CustNumber;
gb_FirstRec = false;
}//end if
gi_TotalRec++;
}//end count recs
public static void PopMessageBox()
{
MessageBox.Show(String.Format("Message: \n\tRecords Added \t{0,6}n\tFirst Rec Added\t {1,6}\n\tLast Rec Added\t{2,6}",
gi_TotalRec, gi_FirstRecNo, CustObj.i_CustNumber),"File Conversion Message:",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}//end class
}
}//end namespace
Replace the ToDecimal call with the following to find out what the issue is:
try
{
CustObj.d_CustDiscount = Convert.ToDecimal(gs_InPutBuffer.Substring(0, 4));
}
catch (FormatException e)
{
Console.WriteLine(gs_InPutBuffer.Substring(0, 4));
Console.WriteLine(e.Message);
}
If this isn't a console application, you can change the code in the catch block to write out to a MessageBox. You can also leave out the leading zeros in the arguments to the calls to Substring.
Why compiling this create an exe which :
open a console
launch the form ?
What can be done for the runtime compiled form open alone, without console ?
//LIST OF USING
using System;
using System.Windows.Forms;
using System.CodeDom.Compiler;
//CODE TO COMPILE
string oSource = #"
using System.Windows.Forms;
using System;
namespace fTest
{
public static class Program
{
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
public class MyForm:Form
{
public MyForm()
{
this.Text=""Generated exe"";
MessageBox.Show(""Generated exe says H3110 W0r1d"");
}
}
}";
string compiledOutput="Generated.exe";
//COMPILATION WORK
String [] referenceAssemblies={"System.dll","System.Drawing.dll","System.Windows.Forms.dll"};
CodeDomProvider _CodeCompiler = CodeDomProvider.CreateProvider("CSharp");
System.CodeDom.Compiler.CompilerParameters _CompilerParameters =
new System.CodeDom.Compiler.CompilerParameters(referenceAssemblies,"");
_CompilerParameters.OutputAssembly = compiledOutput;
_CompilerParameters.GenerateExecutable = true;
_CompilerParameters.GenerateInMemory = false;
_CompilerParameters.WarningLevel = 3;
_CompilerParameters.TreatWarningsAsErrors = true;
_CompilerParameters.CompilerOptions = "/optimize /target:winexe";//!! HERE IS THE SOLUTION !!
string _Errors = null;
try
{
// Invoke compilation
CompilerResults _CompilerResults = null;
_CompilerResults = _CodeCompiler.CompileAssemblyFromSource(_CompilerParameters, oSource);
if (_CompilerResults.Errors.Count > 0)
{
// Return compilation errors
_Errors = "";
foreach (System.CodeDom.Compiler.CompilerError CompErr in _CompilerResults.Errors)
{
_Errors += "Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";\r\n\r\n";
}
}
}catch (Exception _Exception)
{
// Error occurred when trying to compile the code
_Errors = _Exception.Message;
}
//AFTER WORK
if (_Errors==null)
{
// lets run the program
MessageBox.Show(compiledOutput+" Compiled !");
System.Diagnostics.Process.Start(compiledOutput);
}else
{
MessageBox.Show("Error occurred during compilation : \r\n" + _Errors);
}
By default, csc compiles console applications. You need to add /target:winexe to compiler options.
have you tried adding "target:winexe" to the command line parameters?