I am trying to write an app that makes use of the external tools functionality of SQL Server Management Studio.
To specify an external tool, you can enter a path to the app and specify some arguments to pass to the app via STDIN.
Currently I just have a form that displays the arguments. Every time I run the external tool I get a new instance of the application.
Ideally I would like for the first time I run the tool to load the application, and each subsequent running to take the arguments from STDIN and do something with them WITHOUT creating a new instance of the app.
Is there anything I can do that could allow this, or am I stuck with lots of windows?
Thanks in advance
As horrible as it sounds, you can leverage Microsoft.VisualBasic.ApplicationServices to make this really simple (you can add a reference to Microsoft.VisualBasic in your c# project).
As a quick example, you can create a new C# WinForms project and alter Program.cs to look something like this:
class Program : WindowsFormsApplicationBase
{
static Form1 mainForm = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] commandline)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Program prog = new Program();
prog.MainForm = mainForm = new Form1();
prog.Run(commandline);
}
public Program()
{
this.IsSingleInstance = true;
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
mainForm.Startup(eventArgs.CommandLine.ToArray());
}
}
Then in the Form1 throw a label on there and a little code to show it's working:
public void Startup(string[] commandLine)
{
string output = "";
foreach (string arg in commandLine)
output += arg + "\n";
label1.Text = output;
}
public Form1()
{
InitializeComponent();
Startup(Environment.GetCommandLineArgs());
}
The only gotcha with this little snippet is that the command line arguments you get on first launch include the application name, but it's not included on subsequent launches.
Related
I have 2 winform applications, say form1 and form2.
They are programmed through separate visual studio programs
form1 is programmed in WindowsFormsApplication1.sln
form2 is programmed in WindowsFormsApplication2.sln
I want to open form2(=WindowsFormApplication2.exe) by clicking a button in form1
I created a method in WindowsFormApplication1.sln
private void button3_Click(object sender, EventArgs e)
{
var p = new Process();
p.StartInfo.FileName ="WindowsFormsApplication2.exe";
p.StartInfo.Arguments = "10";
p.Start();
}
This method opens WindowsFormApplication2.exe
Then I need WindowsFormApplication2 MessageBox show the value got from WindowsFormApplication1.exe. Is this clear?
This should be clear... I cannot explain more simply than this
What other people have answered through comment or answerbox, is not what I want
If I want to pass a value from form1 to form2 that are in same .sln (That is, WindowsFormApplication1.sln has form1 and form2), this is so easy
I could just use
Form2 form2 = new Form2(textBox1.Text);
form2.Show();
Constructor Form2:
public Form2(string smth)
{
InitializeComponent();
label1.Text = smth;
}
But this is not what I want
I think everything is clear. Please tell me how to solve the problem
C# programs have a static void Main() method that is the entry point for the application. You should see something like this in your Program.cs file in your winform2 project:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
If you want to enable your winform2 application to accept command-line arguments, you capture them from this method. But first you need to modify the method signature to take in a string[] of arguments:
// Add a string[] argument to the Main entry point method
static void Main(string[] args)
Now any command line arguments passed to this application will be in the args array.
Next, we need to modify your winform2 application's main form constructor to take in one or more string arguments, so we can pass them in the line that says Application.Run(new Form1()).
For example, you can modify your form's constructor to take in a string (I'm using Form1 as an example):
public partial class Form1 : Form
{
// Add a string argument to the form's constructor.
// If it's not empty, we'll use it for the form's title.
public Form1(string input)
{
InitializeComponent();
if (!string.IsNullOrEmpty(input)) this.Text = input;
}
}
After we enable the form to take a string input, we now modify the call to the constructor to pass a string to our form. In this case, I'm expecting a string like: /Title:"Some Form Title", so I'll look at the args array and try to find a match.
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Try to find the 'Title' argument
var titleArg = args?.FirstOrDefault(arg => arg.StartsWith("/Title:",
StringComparison.OrdinalIgnoreCase));
// Now strip off the beginning part of the argument
titleArg = titleArg?.Replace("/Title:", "");
// And now we can pass this to our form constructor
Application.Run(new Form1(titleArg));
}
Now you can launch your WinForm application from the command line and pass in a string, which will become the title. Here I'm running the .exe from the command line and passing /Title:"Custom Title From Command Line", but you could just assign this string to the Arguments property of your ProcessStartInfo instance if you're launching the application programmatically.:
Assume that I have a C# Solution with 3 projects Main, Program1, Program2.
I want to have a "Main form", when I click on button "Program1" the main form will be hidden, Program1 will be showed, and when I close Program1, the Main form will return.
How can I do this?
I tried add Program1 and PRogram2 as Reference to Project Main and code like below in Main, it works for call Program1, but can't handle event Program1.closed() because when I try to reference Main to Program1, it error
---------------------------
Microsoft Visual Studio
---------------------------
A reference to 'Main' could not be added. Adding this project as a reference would cause a circular dependency.
---------------------------
OK
---------------------------
I searched Google and got nothing helpful!
using System;
using System.Windows.Forms;
namespace Switch
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Program1.Form1 pf1 = new Program1.Form1();
pf1.Show();
this.Hide();
}
}
}
As zcui93 commented you can use process to make it work. You can either have all 3 in same folder (when you deploy the app on client machine)
using System.Diagnostics;
...
Process process = new Process();
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = "process.exe";
process.StartInfo.Arguments = "-n";
process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
process.Start();
process.WaitForExit();// Waits here for the process to exit.
In C# you can use the Process.Exited event. This event doesn't work when someone close the app when someone kill the app from task manager.
Circular dependencies use to happen when the project arquitecture is not good.
In your case i think the problem migth be the program1 or program2 have Main as a reference.
Remove de Main reference from the program1 and program2.
The main project must have reference to the program1 and program2.
Thanks everyone for answers!
After confirmed with customer, they don't strictly need the "mainform" to be hidden, so I came with another easier solution:
1. For the "child form", I use ShowDiaglog() instead of Show()
private void btnChildForm1_Click(object sender, EventArgs e)
{
var frm = new ChildForm1();
frm.ShowDialog();
}
For the mainform, I use mutex to force it to be only 1 instance:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
[STAThread]
static void Main()
{
var mutex = new Mutex(true, "MainForm", out var result);
if (!result)
{
MessageBox.Show("Running!");
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
GC.KeepAlive(mutex);
}
}
I'm an amateur at c# and I've been unable to locate the answer to this.
Perhaps I am not aware of the correct terms to use.
When a video file is dragged onto my exe application, I would like the application to know that it was launched with a file and be able to know the path and filename of that file. This way, the user does not have to use the file>open menu.
Hope that makes sense.
Thanks
You can check the command line arguments which were used to launch the application.
If your application was started by dropping a file on the .exe file, there will be a single command line argument with the path of the file.
string[] args = System.Environment.GetCommandLineArgs();
if(args.Length == 1)
{
// make sure it is a file and not some other command-line argument
if(System.IO.File.Exists(args[0])
{
string filePath = args[0];
// open file etc.
}
}
As your question title states, you want the path and the file name. You can get the file name using:
System.IO.Path.GetFileName(filePath); // returns file.ext
When you drag a file into a C# application, it will goes as an command-line argument to that application. Like console applications, you can catch it on the Main method on the Program class.
I'll explain it using Windows Forms application.
Open your Program class on the solution. Your program class should look like this.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
By default, when you create Windows Forms applications, they don't treat command line arguments. You have to make a tiny change on the signature of the Main method, so it can receive arguments:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Now you can handle file name argument passed to the application. By default, Windows will put the file name as the first argument. Just do something like this:
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Before Application.Run method, treat the argument passed.
// I created an overload of the constructor of my Form1, so
// it can receive the File Name and load the file on some TextBox.
string fileName = null;
if (args != null && args.Length > 0)
fileName = args[0];
Application.Run(new Form1(fileName));
}
In case you want to know the constructor overload of my Form1, here it is. Hope it helps!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Form1(string fileName) : this()
{
if (fileName == null)
return;
if (!File.Exists(fileName))
{
MessageBox.Show("Invalid file name.");
return;
}
textBox1.Text = File.ReadAllText(fileName);
}
}
You need your application's command-line arguments. When you drop a file on your application in Explorer, Explorer opens the application with the file you dropped on it. You can select as many files as you want, drop them on your application and using this line of code, files will be an array of those command line arguments.
string[] files = Environment.GetCommandLineArgs();
This question already has answers here:
"does not contain a static 'main' method suitable for an entry point"
(10 answers)
Closed 9 years ago.
I got this Error when I try to compile a sourcecode with CodeDom
Does not contain a static "Main" Method suitable for an entry point!
I already googled it and read other answers here, but I dont know how to fix it.
Can someone please help me?
Here is my source code :
http://picz.to/image/ao5n
^ private void button2_Click(object sender, EventArgs e)
{
SaveFileDialog d = new SaveFileDialog();
d.Filter = "Executable (*.exe)|*.exe";
if (d.ShowDialog() == DialogResult.OK)
{
string source = Properties.Resources.source;
CompilerParameters param = new CompilerParameters();
param.CompilerOptions += "/target:winexe" + " " + "/win32icon:" + "\"" + textBox1.Text + "\"";
param.GenerateExecutable = true;
param.ReferencedAssemblies.Add("System.Windows.Forms.dll");
param.ReferencedAssemblies.Add("System.dll");
param.OutputAssembly = d.FileName;
StringBuilder Temp = new StringBuilder();
String InputCode = String.Empty;
InputCode = "MessageBox.Show((1 + 2 + 3).ToString());";
Temp.AppendLine(#"using System;");
Temp.AppendLine(#"using System.Windows.Forms;");
Temp.AppendLine(#"namespace RunTimeCompiler{");
Temp.AppendLine(#"static void Main(string[] args){");
Temp.AppendLine(#"public class Test{");
Temp.AppendLine(#"public void Ergebnis(){");
Temp.AppendLine(InputCode);
Temp.AppendLine(#"}}}}");
CompilerResults result = new CSharpCodeProvider().CompileAssemblyFromSource(param, Temp.ToString());
if (result.Errors.Count > 0) foreach (CompilerError err in result.Errors) MessageBox.Show(err.ToString());
else MessageBox.Show("Done.");
}
}
All C# programs need to contain the Main() method. Essentially this is where the program starts. The code you posted is just a small part of the entire application. You must have removed the location where main had been residing.
MSDN Article on Main
Updated for comments:
A new Windows Form App has a Program class that instantiates the form that you want.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Try copying that over to a new file called program.cs. Make sure that Form1 now points to the form you created in the applications.
Paste this into your class -- if you still get an error, you need to paste the entire class in question, not just a screen capture of the event handler for a button click.
static void Main(string[] args)
{
//do nothing
}
The code you've posted is the click event for a button. A button is usually on a form, and the form must be initialized. If you create a Windows Forms Application it will create a file Program.cs that contains a Main method that starts your form.
When you start a program, the computer needs to know where to actually start running code, that's what the Main() method is for. It is required to run, and that's the error you are receiving.
I have two C# winform (.NET 4.0) forms that each run separate but similar automated tasks continuously. Separate in that they are distinct processes/workflows, but similar enough in how they operate to share the same resources (methods, data models, assemblies, etc) in the project.
Both forms are complete, but now I'm not sure how to run the program so that each window opens on launch and runs independently. The program will be "always-on" when deployed.
This might seem a little basic, but most of my development experience has been web applications. Threading/etc is still a little foreign to me. I've researched but most of the answers I've found relate to user interaction and sequential use cases -- this will just be one system continuously running two distinct processes, which will need to interact with the world independently.
Potential solutions I've found might involve multi-threading, or maybe some kind of MDI, or a few folks have suggested the DockPanelSuite (although being in a super-corporate environment, downloading third party files is easier said than done).
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Rather than specifying frmOne or frmTwo,
// load both winforms and keep them running.
Application.Run(new frmOne());
}
}
You can create a new ApplicationContext to represent multiple forms:
public class MultiFormContext : ApplicationContext
{
private int openForms;
public MultiFormContext(params Form[] forms)
{
openForms = forms.Length;
foreach (var form in forms)
{
form.FormClosed += (s, args) =>
{
//When we have closed the last of the "starting" forms,
//end the program.
if (Interlocked.Decrement(ref openForms) == 0)
ExitThread();
};
form.Show();
}
}
}
Using that you can now write:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MultiFormContext(new Form1(), new Form2()));
If you really need two windows/forms to run on two separate UI threads, you could do something like this:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var thread = new Thread(ThreadStart);
// allow UI with ApartmentState.STA though [STAThread] above should give that to you
thread.TrySetApartmentState(ApartmentState.STA);
thread.Start();
Application.Run(new frmOne());
}
private static void ThreadStart()
{
Application.Run(new frmTwo()); // <-- other form started on its own UI thread
}
}
Assumption
You do not need the two different processes, you are only using the 2 processes because you want to have the two different forms and want to be able to keep the application running until both forms are exited.
Another solution
Rely on the Form.Closed event mechanism. You can add an eventhandler which allows you to specify what to do when a form closes. E.g. exit the application when both forms are closed.
In terms of some code
public Form1()
{
InitializeComponent();
_form2 = new Form2();
_form2.Show(this);
this.Closed += Form1Closed;
_form2.Closed += Form2Closed;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = true;
Hide();
Form1Closed(this, new EventArgs());
base.OnFormClosing(e);
}
private void Form1Closed(object sender, EventArgs eventArgs)
{
form1IsClosed = true;
TryExitApplication();
}
private void Form2Closed(object sender, EventArgs eventArgs)
{
_form2IsClosed = true;
TryExitApplication();
}
private void TryExitApplication()
{
if (form1IsClosed && _form2IsClosed)
{
Dispose();
Application.Exit();
}
}
Note that this should be refactored to make it a better solution.
UPDATE
The comments provided by Servy made my revise this "supposed to be simple solution", which pointed out that his solution is way better then this solution. Since I am supported to leave the answer I will use this answer I will also address the issues that start arising when going for this solution:
cancelling close events
rerouting from one event to another
force calling Dispose.
as Servy pointed out: maintenance unfriendly (state to check which form is closed)