FreeConsole() does not detach the process - c#

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);

Related

Console Application to be run as GUI as well

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.

How to be dynamically either console application or Windows Application

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!

C# hybrid cli and win form app

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
}

How to own console for time of program execution?

I'm trying to write a program, that works in console or GUI mode, depending on execution parameters. I've managed to write following sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace wfSketchbook
{
static class Program
{
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AttachConsole(int processId);
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AllocConsole();
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeConsole();
private const int ATTACH_PARENT_PROCESS = -1;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
if (!AttachConsole(ATTACH_PARENT_PROCESS))
AllocConsole();
Console.WriteLine("Welcome to console!");
Console.ReadKey();
FreeConsole();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
}
It generally works, however when program is called from the system command-line, the cmd seems not to be aware, that program works in console mode and exits immediately:
d:\Dokumenty\Dev\C#\Projekty\Win32\Sketchbook\wfSketchbook\bin\Debug>wfSketchbook.exe test
d:\Dokumenty\Dev\C#\Projekty\Win32\Sketchbook\wfSketchbook\bin\Debug>Welcome to console!
d:\Dokumenty\Dev\C#\Projekty\Win32\Sketchbook\wfSketchbook\bin\Debug>
I'd rather expect following output:
d:\Dokumenty\Dev\C#\Projekty\Win32\Sketchbook\wfSketchbook\bin\Debug>wfSketchbook.exe test
Welcome to console!
d:\Dokumenty\Dev\C#\Projekty\Win32\Sketchbook\wfSketchbook\bin\Debug>
How may I correct this problem?
There is no ideal solution for this. Cmd.exe is only going to automatically wait for the program to complete if it can see that the .exe is a console mode app. Which won't be the case for your app. One workaround is to tell it to wait:
start /wait yourapp.exe [arguments]
The other is to always use AllocConsole(). Which the side-effect that it creates a second console window. Changing the application type to Console then calling FreeConsole() is not ideal either, the flash of the window is quite noticeable. Pick your poison.
There isn't any reliable way to make a Windows application a console and a GUI. Your program is a Windows application - so Windows launches you outside of the console window - when your program starts you aren't attached to the console window.
You could change your project output to a Console application in the project's properties. But then you would always get a console window. Windows could see that your application was marked as a console application and create a console even before you ran.
See this blog post for more information and links to some work arounds.

WPF / Console Hybrid Application

I writing an application what can either be run on the command line, or with a WPF UI.
[STAThread]
static void Main(string[] args)
{
// Does magic parse args and sets IsCommandLine to true if flag is present
ParseArgs(args);
if(IsCommandLine)
{
// Write a bunch of things to the console
}
else
{
var app = new App();
app.Run(new Window());
}
}
I set the project's Output type to Console Application, I get a console window that popups if I try to execute it by double-clicking the exe. I don't want to show the console window to the user if the flag is not set (passed in via command args).
However, if I set the project's Output type to Windows Application, the double-click behaviour is fine, but when I run it in the console, I get no console output (Console.Writeline)
Your best bet would be to abstract out the code that actually does the work to a separate class library that has no UI and then create two applications one Console, the other WPF that call this.
A console application and an WPF application have entirely different application models so you can't reuse the same code in both applications.
Having a separate class library allows you do other things like use it in other applications such as a web site or client/server architecture.
Create a WPF app and add the following code to your App class:
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Length > 0)
{
List<string> lowercaseArgs = e.Args.ToList().ConvertAll(x => x.ToLower());
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
// your console app code
Console.Write("\rPress any key to continue...");
Console.ReadKey();
FreeConsole();
}
Shutdown();
}
else
{
base.OnStartup(e);
}
}
private const int ATTACH_PARENT_PROCESS = -1;
[DllImport("kernel32", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool FreeConsole();
}
You can conditionally start your WPF application by performing the following steps with the sample below.
Add another entry point with the 'Main' method declarated with STAThreadAttribute. This is required by WPF.
Under your project's 'Build' properties, choose 'Console Application' as your output and your new 'Main' method as the application's 'Startup Object'.
using System;
public static class Program
{
[STAThreadAttribute]
public static void Main()
{
Console.WriteLine("What now?");
Console.ReadKey(true);
App.Main();
}
}
I know I'm a little late to the party, but figured I could toss in my two cents. You could always keep it as a console application, and then hide the console as per this answer (https://stackoverflow.com/a/3571628/1059953). There is a moment of the console being displayed, then it disappears and the window shows up.

Categories