Before anyone marks this as Duplicate, Please read
I have a process which is Windows Application and is written in C#.
Now I have a requirement where I want to run this from console as well. Like this.
Since I need to show output to console, so I changed the application type to Console Application
Now the problem is that, whenever user launches a process from Windows Explorer (by doubling clicking). Console window also opens in behind.
Is there is way to avoid this ?
What I tried after #PatrickHofman's help.
Followed how to run a winform from console application?
But I still have problems
When I do this https://stackoverflow.com/a/279811/3722884, the console opens in new window. I don't want that.
When I do this https://stackoverflow.com/a/11058118/3722884, i.e. pass -1 to AllocConsole there are other problems which occur, as mentioned in the referred link.
OK I thought I'd have a play at this as I was curious.
First I updated the Main method in Program.cs to take arguments so I could specify -cli and get the application to run on the command line.
Second I changed the project's output type to "console application" in the project properties.
Third I added the following methods to Program.cs
private static void HideConsoleWindow()
{
var handle = GetConsoleWindow();
ShowWindow(handle, 0);
}
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
Fourth I call HideConsoleWindow as the first action in non-CLI mode.
After these steps my (basic) application looks like:
[STAThread]
static void Main(string[] args)
{
if (args.Any() && args[0] == "-cli")
{
Console.WriteLine("Console app");
}
else
{
HideConsoleWindow();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Then if I open the program on the command line with the -cli switch it runs as a command line application and prints content out to the command line and if I run it normally it loads a command line window (extremely) briefly and then loads as a normal application as you'd expect.
Related
I have already read this similar quesiton but the solution accepted in that question is not working for me.
I have a WinForm application (called FormPlusConsoleApp) that works as a console application if some arguments are passed by the calling program.
I need to provide status messages from the program FormPlusConsoleApp to the console therefore, I attach to the console at the very beginning of the program.
PROBLEM: After executing the required job, the program should exit with an exit code 0/1 and should completely detach to the console without any user interaction.
I am already calling the FreeConsole() method as suggested in this similar quesiton but at the end, a blinking cursor appears on the command prompt (as shown in the screenshot) and the user must press a button to completely exit.
Sample Working Program:
using System;
using System.Windows.Forms;
using System.IO;
namespace FormPlusConsoleApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
System.Windows.Forms.MessageBox.Show("Attach the debugger now...");
if (args.Length == 0) //form version
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormApp());
}
else //start console version
{
// case args.Length > 0
SampleConsoleExecution();
}
}
static void SampleConsoleExecution()
{
//attach to the console
bool isAttached = AttachConsole(-1);
//Sample Message
Console.WriteLine(Environment.NewLine + "Console process started...");
//do something
Console.WriteLine(Environment.NewLine + "Exit process!");
//Free console
FreeConsole(); //<<<<<<<<<--------blinking cursor appears on the console but the user still have to press the "ENTER" button
}
//To attach to the console (parent)
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
//To free from the attached console
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
}
}
PS: I understand that the best approach is to have separate WinForm and console applications that use the same business logic. Unfortunately, that's not an option for me. The above code is just MWE to test the problem.
As written in this blog post by Raymond Chen and this answer you can't have an app that is both, console and windows app.
There are few workarounds, the approach that worked for me is to add the following code at the end of the main function:
SendKeys.SendWait("{ENTER}");
In order to set the exit code, you could use Environment.Exit:
Environment.Exit(0);
I have a small application who should be executed in two modes: non UI or WPF Window. It should depend on command line arguments.
In each mode, I need to show some feedback log:
In WPF Window mode, WPF is going to take care of visualizing logs,
In no UI mode, I need a console to show logs. If my app have been started from a console (mainly cmd.exe), I'd like to use it without opening a new one. If my app have been started outside of a console (double click on explorer, a CreateProcess, ...), I need to create a new console to output my results and wait for a Readkey to close it.
I have found:
how I can create a new console:
How to open/close console window dynamically from a wpf application?,
how to get current console windows handle to show/hide it:
Show/Hide the console window of a C# console application
And I know I can statically choose between "Windows Application" or "Console Application" in project property.
Choosing "Windows Application", GetConsoleWindow() is always 0 and I don't see how to reuse a previous console.
Choosing "Console Application", I can reuse a previous console but when started from explorer in WPF Window mode, a console is created under my WPF main window.
The question is: how can an application be really dynamic? Either in WPF Window mode, with only a WPF windows (and no console at all) or in non UI, with only one console (starting one or a new created one).
It was a lot easier in Winforms, but its not too hard.
Start off with a WPF Application Project (not a console application project with WPF windows).
Create a new Program.cs class in the root directory, add the following code:
class Program
{
[DllImport("Kernel32")]
public static extern void AllocConsole();
[DllImport("Kernel32")]
public static extern void FreeConsole();
[DllImport("kernel32.dll")]
static extern bool AttachConsole(uint dwProcessId);
[STAThread]
public static void Main(string[] args)
{
bool madeConsole = false;
if (args.Length > 0 && args[0] == "console")
{
if (!AttachToConsole())
{
AllocConsole();
Console.WriteLine("Had to create a console");
madeConsole = true;
}
Console.WriteLine("Now I'm a console app!");
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
if (madeConsole)
FreeConsole();
}
else
{
WpfApplication1.App.Main();
}
}
public static bool AttachToConsole()
{
const uint ParentProcess = 0xFFFFFFFF;
if (!AttachConsole(ParentProcess))
return false;
Console.Clear();
Console.WriteLine("Attached to console!");
return true;
}
}
Now you have a console app or a WPF app. In the Properties, set the start up object as the Program.Main method. In the example above, WpfApplication1.App.Main is the old start up object (defined in the App.xaml.cs file).
Edit this misses one of your requirements about using the existing console and I will edit it as soon as I figure out how to stay in the same console window.
New Edit Now works to use the existing console!
I have a C# Console application project where the output type is set to "Windows application". This is so the console does not flash up at the start of the program.
However i also want to allow a help command line argument which will display details about the program if you run it from the command line with "/?" as an argument.
Is there a way to have the program run as a windows application normally but show a console if the help argument is passed?
EDIT - After reading the answers and a similar one at This question (this question assumes you are running with a console application output type) I am using this solution.
[DllImport(Kernel32_DllName)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
static void Main(string[] args)
{
if(args.Contains("/?"))
{
AllocConsole();
Console.WriteLine("helpText");
Console.ReadLine();
var handle = GetConsoleWindow();
//Hides console
ShowWindow(handle, SW_HIDE);
}
}
I have not found a way to do exactly as you are asking, but you can have a console open up depending on the input. For example:
class Program
{
private const string Kernel32_DllName = "kernel32.dll";
[DllImport(Kernel32_DllName)]
private static extern bool AllocConsole();
static void Main(string[] args)
{
if (args[0] == "/")
{
AllocConsole();
Console.WriteLine("Details");
Console.ReadKey();
//cases and such for your menu options
}
That will open a console that you can use if you follow the run command with a / even though the output type of the project is a Windows Application.
Is there a way to have the program run as a windows application normally but show a console if the help argument is passed?
For what you want, learn about using command line arguments here.
Basically declare Main to accept arguments as an array of string:
static void Main(string[] args)
Use a simple dialog form to display the help message or even a MessageBox which is a very simple dialog form.
This gives you much better control rather than trying to cobble something together that wasn't really meant to be put together.
When I need to do stuff like this, I usually make my app a console app. I then parse the args if any in main and decide if I'm going to hide the console window and launch my UI or write to the console.
Hiding a window is fairly straightforward. See How to hide / unhide a process in C#? for an example.
I have seen many examples of a hybrid gui/cli app for C#. I have implemented such an app, but I am having a hard time figuring out how to prevent the .exe, when run on the command line, from not returning back to the prompt right away.
//defines for commandline output
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[STAThreadAttribute]
static void Main(string[] args)
{
// load cli
// redirect console output to parent process;
// must be before any calls to Console.WriteLine()
AttachConsole(ATTACH_PARENT_PROCESS);
if (args.Length == 0)
{
//loads gui
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
cli_main cli = new cli_main();
cli.start_cli(args);
Console.WriteLine("finished");
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
Application.Exit();
}
}
I get the following output
C:\Users\john\Documents\Visual Studio 2010\Projects\test\test\test\bin\Debug>test.exe -param1 test
-param2 test2
C:\Users\john\Documents\Visual Studio 2010\Projects\test\test\test\bin\Debug>Output was successful. File saved as: c:\test\test.html
finished
The line "finished" is a string I output when I know I have reach end of my main code... this works fine in Winforms, my project is Winforms and I started it as a gui but now I am trying to make it hybrid gui/cli
And it seems to be running my main code and threads I see them in debugging and it creates my final output file...
I just am puzzled as to how to keep the .exe when executed from cmd line with it's parameters, to not return to the command prompt?? And have it wait with a blinking cursor, then output the line about the html file and then the line "finished", then finally go back to the command prompt.
I've tried numerous things like removing
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
Application.Exit();
and instead of using Application.Exit(); use Environment.Exit(0); but it always returns to the command prompt right away, also I tried putting in sleep for 5 secs after the line
cli.start_cli(args);
but that didn't work either, I guess I don't understand how it can return to command prompt right away and it hasn't even it the line
Console.WriteLine("finished");
FWIW, I tried the first approach as well. I ended up just hiding the console window using:
IntPtr handle = GetConsoleWindow();
if (handle != IntPtr.Zero)
{
ShowWindow(handle, 0); // 0=SW_HIDE
}
This completely hides the window, even from the Taskbar. It flashes for a brief second but that's acceptable in my case
I have been researching how to create a hybrid winform and CLI app... I started out my app as Winforms, now I am adding CLI to it... It seems to work but has a few issues I want to figure out how to fix and have not been able to do so , probably due to my lack of experience with C#.
If i have output type in VS set to "windows application", and use the code below i am able to run the winform portion for GUI, and also from command line am able to give it parameters and it works, well at least it outputs consolewrites i have coded in, i have a seperate c# class file that has my main code so it is seperate from winform GUI and my eventual cli code, they both will just feed user input to this other "main" c# class.. anyway here is the code.
[STAThreadAttribute]
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
AttachConsole(ATTACH_PARENT_PROCESS);
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
well it works in gui, i can access my menus and create different win forms, moment i click a button to perform an action i get the following exception:
System.Threading.ThreadStateException was unhandled by user code
Message=Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
if i then change the output type to "console application", it works perfectly in all manners in cli and GUI, BUT when it opens the winform/GUI portion i get this ugly CMD window that will not go away.. here is the code i used, basically just what i started with before i added the above code.
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new form_Main());
}
else
{
cli_main cli = new cli_main();
cli.start_cli(args);
}
}
again with my lack of knowledge on C# i am hoping someone can point me to a solution . I would prefer to keep the app as output "console application" and find a way to hide the console that is opened when i start the winform/gui portion..?? thanks in advance.
Did you do what it said in the error message?
Ensure that your Main function has STAThreadAttribute marked on it
In the code you pasted, the STAThread attribute is not marking the Main method, it is marking the AttachConsole external function. Move that to where it ought to be and you shouldn't have any problems.
If your application is a console application, it will get a console window automatically if there isn't already one attached. That's the point of making it a console application. You can use FindWindow and ShowWindow(SW_HIDE) to hide it at runtime but it will still flash on-screen briefly.
If you plan to start your application from an existing console window most of the time, you should keep it as a console application, since it will inherit the parent process's console window by default. If you plan to start your application from a UI shortcut or from other non-console processes, you should probably make it a Windows application and allocate the console as needed.
Thanks for all, but some changes for previous pattern
It works so:
static void Main(string[] args)
{
// Test pattern
args = new string[] { "-flag1", "value1", "-flag2", "value2", "-flag3", "value3" };
if (args.Length == 0)
MainWinApp();
else
MainCLI(args);
}
[STAThread]
static MainWinApp()
{
// Your code for start GUI application
}
[STAThreadAttribute]
static void MainCLI(string[] args)
{
// Your code for CLI application
}