Related
Even though there are command-line commands to start (most of?) the various Control Panel screens in Windows 10, a specific scenario seems to fail:
If the machine starts with Bluetooth turned off (not disabled), running the command which should open the Bluetooth settings screen, simply does nothing. The command could be either ms-settings:bluetooth, bthprops.cpl or ms-settings:Bluetooth.
I've also tried to directly launch the Bluetooth Devices screen (using the command %windir%\explorer.exe shell:::{28803F59-3A75-4058-995F-4EE5503B023C} as described here), but clicking on the "Bluetooth settings" in this window does nothing as well.
The only way to get directly to the Bluetooth settings screen without going through the main Control Panel window and without turning on Bluetooth first, is by right clicking on the relevant tile in Windows Action Center:
Although this seems like a bug on the operating system level, I was wondering if there's any way to know when the launch fails from within C# code. So I've tried using the following code:
try
{
var process = new Process();
process.StartInfo.FileName = "control";
process.StartInfo.Arguments = "bthprops.cpl";
process.Exited += (s, e) =>
{
if (process.ExitCode != 0)
{
TurnOnBt();
}
};
var res = process.Start();
if (!res)
{
TurnOnBt();
}
}
catch (System.Exception ex)
{
int test = 6; // just for breakpoint
}
Problem is, no exception was ever thrown, and most of the time the Process.Exit event was never called.
Further more, calling Windows.Devices.Radios.Radio.GetRadiosAsync() returns an empty list!
Currently the only solution I've found is to manually turn on Bluetooth - it wouldn't change the Process.Start/Exit behavior, but it does allow to successfully lunch the command to directly open Bluetooth Settings window, and to get the list of the machine's Bluetooth/Radio devices. Still, when turning off Bluetooth and restarting the machine, same problem would happen all over again.
Any ideas for a code-based workaround?
note - all this based only on my debugging research, nothing from this is documented
i look how BT-settings window is open via Action Center (win8.1, win 10):
the IApplicationActivationManager interface created and called ActivateApplication method with:
appUserModelId = L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel"
arguments = L"page=SettingsPagePCSystemBluetooth"
code on c++ can be look like:
if (0 <= CoInitialize(0))
{
IApplicationActivationManager* pAppAct;
if (0 <= CoCreateInstance(__uuidof(ApplicationActivationManager), 0, CLSCTX_ALL, IID_PPV_ARGS(&pAppAct)))
{
ULONG dwProcessId;
pAppAct->ActivateApplication(
L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel",
L"page=SettingsPagePCSystemBluetooth",
AO_NONE ,
&dwProcessId);
pAppAct->Release();
}
CoUninitialize();
}
the processId (if all ok) reference to "X:\Windows\ImmersiveControlPanel\SystemSettings.exe" -ServerName:microsoft.windows.immersivecontrolpanel
for c# - look IApplicationActivationManager::ActivateApplication in C#?
the "windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel" and "page=SettingsPagePCSystemBluetooth" strings not documented anywhere and I not sure are it is "persist" - but currently it used to open manage Bluetooth page in system settings. as is.
in case we run control.exe bthprops.cpl - the process ( control.exe ) launched without any error - as result you and not got any errors when you call this code.
then control.exe bthprops.cpl exec new process rundll32.exe Shell32.dll,Control_RunDLL bthprops.cpl, and exit
the rundll32.dll call Control_RunDLLW(HWND_DESKTOP, (HINSTANCE)&__ImageBase, L"bthprops.cpl", SW_SHOWDEFAULT);
we can and direct call void WINAPI Control_RunDLLW(HWND hwndParent, HINSTANCE hInst, PCWSTR cplName, int nCmdShow ); this api exported from shell32.dll
internally Control_RunDLLW load "bthprops.cpl" (3-rd argument - cplName), find CPlApplet entry point, and send CPL_INIT message. the bthprops.cpl on this message check are bthserv is running via OpenService(L"BTHSERV", ) + QueryServiceStatus (in function BthpIsbluetoothServiceRunning) and if "BTHSERV" not running - it return zero (fail code)
I want to make a C# program that can be run as a CLI or GUI application depending on what flags are passed into it. Can this be done?
I have found these related questions, but they don't exactly cover my situation:
How to write to the console in a GUI application
How do I get console output in C++ with a Windows program?
Jdigital's answer points to Raymond Chen's blog, which explains why you can't have an application that's both a console program and a non-console* program: The OS needs to know before the program starts running which subsystem to use. Once the program has started running, it's too late to go back and request the other mode.
Cade's answer points to an article about running a .Net WinForms application with a console. It uses the technique of calling AttachConsole after the program starts running. This has the effect of allowing the program to write back to the console window of the command prompt that started the program. But the comments in that article point out what I consider to be a fatal flaw: The child process doesn't really control the console. The console continues accepting input on behalf of the parent process, and the parent process is not aware that it should wait for the child to finish running before using the console for other things.
Chen's article points to an article by Junfeng Zhang that explains a couple of other techniques.
The first is what devenv uses. It works by actually having two programs. One is devenv.exe, which is the main GUI program, and the other is devenv.com, which handles console-mode tasks, but if it's used in a non-console-like manner, it forwards its tasks to devenv.exe and exits. The technique relies on the Win32 rule that com files get chosen ahead of exe files when you type a command without the file extension.
There's a simpler variation on this that the Windows Script Host does. It provides two completely separate binaries, wscript.exe and cscript.exe. Likewise, Java provides java.exe for console programs and javaw.exe for non-console programs.
Junfeng's second technique is what ildasm uses. He quotes the process that ildasm's author went through when making it run in both modes. Ultimately, here's what it does:
The program is marked as a console-mode binary, so it always starts out with a console. This allows input and output redirection to work as normal.
If the program has no console-mode command-line parameters, it re-launches itself.
It's not enough to simply call FreeConsole to make the first instance cease to be a console program. That's because the process that started the program, cmd.exe, "knows" that it started a console-mode program and is waiting for the program to stop running. Calling FreeConsole would make ildasm stop using the console, but it wouldn't make the parent process start using the console.
So the first instance restarts itself (with an extra command-line parameter, I suppose). When you call CreateProcess, there are two different flags to try, DETACHED_PROCESS and CREATE_NEW_CONSOLE, either of which will ensure that the second instance will not be attached to the parent console. After that, the first instance can terminate and allow the command prompt to resume processing commands.
The side effect of this technique is that when you start the program from a GUI interface, there will still be a console. It will flash on the screen momentarily and then disappear.
The part in Junfeng's article about using editbin to change the program's console-mode flag is a red herring, I think. Your compiler or development environment should provide a setting or option to control which kind of binary it creates. There should be no need to modify anything afterward.
The bottom line, then, is that you can either have two binaries, or you can have a momentary flicker of a console window. Once you decide which is the lesser evil, you have your choice of implementations.
* I say non-console instead of GUI because otherwise it's a false dichotomy. Just because a program doesn't have a console doesn't mean it has a GUI. A service application is a prime example. Also, a program can have a console and windows.
Check out Raymond's blog on this topic:
https://devblogs.microsoft.com/oldnewthing/20090101-00/?p=19643
His first sentence: "You can't, but you can try to fake it."
http://www.csharp411.com/console-output-from-winforms-application/
Just check the command line arguments before the WinForms Application. stuff.
I should add that in .NET it is RIDICULOUSLY easy to simply make a console and GUI projects in the same solution which share all their assemblies except main. And in this case, you could make the command line version simply launch the GUI version if it is launched with no parameters. You would get a flashing console.
There is an easy way to do what you want. I'm always using it when writing apps that should have both a CLI and a GUI. You have to set your "OutputType" to "ConsoleApplication" for this to work.
class Program {
[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")]
private static extern IntPtr _GetConsoleWindow();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
/*
* This works as following:
* First we look for command line parameters and if there are any of them present, we run the CLI version.
* If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console.
* If there is no console at all, we show the GUI.
* We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part.
* This way we're both a CLI and a GUI.
*/
if (args != null && args.Length > 0) {
// execute CLI - at least this is what I call, passing the given args.
// Change this call to match your program.
CLI.ParseCommandLineArguments(args);
} else {
var consoleHandle = _GetConsoleWindow();
// run GUI
if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost"))
// we either have no console window or we're started from within visual studio
// This is the form I usually run. Change it to match your code.
Application.Run(new MainForm());
else {
// we found a console attached to us, so restart ourselves without one
Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) {
CreateNoWindow = true,
UseShellExecute = false
});
}
}
}
I think the preferred technique is what Rob called the devenv technique of using two executables: a launcher ".com" and the original ".exe". This is not that tricky to use if you have the boilerplate code to work with (see below link).
The technique uses tricks to have that ".com" be a proxy for the stdin/stdout/stderr and launch the same-named .exe file. This give the behavior of allowing the program to preform in a command line mode when called form a console (potentially only when certain command-line arguments are detected) while still being able to launch as a GUI application free of a console.
I hosted a project called dualsubsystem on Google Code that updates an old codeguru solution of this technique and provides the source code and working example binaries.
Here is what I believe to be the simple .NET C# solution to the problem. Just to restate the problem, when you run the console "version" of the app from a command line with a switch, the console keeps waiting (it doesn't return to the command prompt and the process keeps running) even if you have an Environment.Exit(0) at the end of your code. To fix this, just before calling Environment.Exit(0), call this:
SendKeys.SendWait("{ENTER}");
Then the console gets the final Enter key it needs to return to the command prompt and the process ends. Note: Don't call SendKeys.Send(), or the app will crash.
It's still necessary to call AttachConsole() as mentioned in many posts, but with this I get no command window flicker when launching the WinForm version of the app.
Here's the entire code in a sample app I created (without the WinForms code):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ConsoleWriter
{
static class Program
{
[DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[STAThread]
static void Main(string[] args)
{
if(args.Length > 0 && args[0].ToUpperInvariant() == "/NOGUI")
{
AttachConsole(ATTACH_PARENT_PROCESS);
Console.WriteLine(Environment.NewLine + "This line prints on console.");
Console.WriteLine("Exiting...");
SendKeys.SendWait("{ENTER}");
Environment.Exit(0);
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
}
Hope it helps someone from also spending days on this problem. Thanks for the hint go to #dantill.
/*
** dual.c Runs as both CONSOLE and GUI app in Windows.
**
** This solution is based on the "Momentary Flicker" solution that Robert Kennedy
** discusses in the highest-rated answer (as of Jan 2013), i.e. the one drawback
** is that the console window will briefly flash up when run as a GUI. If you
** want to avoid this, you can create a shortcut to the executable and tell the
** short cut to run minimized. That will minimize the console window (which then
** immediately quits), but not the GUI window. If you want the GUI window to
** also run minimized, you have to also put -minimized on the command line.
**
** Tested under MinGW: gcc -o dual.exe dual.c -lgdi32
**
*/
#include <windows.h>
#include <stdio.h>
static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow);
static LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam);
static int win_started_from_console(void);
static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp);
int main(int argc,char *argv[])
{
HINSTANCE hinst;
int i,gui,relaunch,minimized,started_from_console;
/*
** If not run from command-line, or if run with "-gui" option, then GUI mode
** Otherwise, CONSOLE app.
*/
started_from_console = win_started_from_console();
gui = !started_from_console;
relaunch=0;
minimized=0;
/*
** Check command options for forced GUI and/or re-launch
*/
for (i=1;i<argc;i++)
{
if (!strcmp(argv[i],"-minimized"))
minimized=1;
if (!strcmp(argv[i],"-gui"))
gui=1;
if (!strcmp(argv[i],"-gui-"))
gui=0;
if (!strcmp(argv[i],"-relaunch"))
relaunch=1;
}
if (!gui && !relaunch)
{
/* RUN AS CONSOLE APP */
printf("Console app only.\n");
printf("Usage: dual [-gui[-]] [-minimized].\n\n");
if (!started_from_console)
{
char buf[16];
printf("Press <Enter> to exit.\n");
fgets(buf,15,stdin);
}
return(0);
}
/* GUI mode */
/*
** If started from CONSOLE, but want to run in GUI mode, need to re-launch
** application to completely separate it from the console that started it.
**
** Technically, we don't have to re-launch if we are not started from
** a console to begin with, but by re-launching we can avoid the flicker of
** the console window when we start if we start from a shortcut which tells
** us to run minimized.
**
** If the user puts "-minimized" on the command-line, then there's
** no point to re-launching when double-clicked.
*/
if (!relaunch && (started_from_console || !minimized))
{
char exename[256];
char buf[512];
STARTUPINFO si;
PROCESS_INFORMATION pi;
GetStartupInfo(&si);
GetModuleFileNameA(NULL,exename,255);
sprintf(buf,"\"%s\" -relaunch",exename);
for (i=1;i<argc;i++)
{
if (strlen(argv[i])+3+strlen(buf) > 511)
break;
sprintf(&buf[strlen(buf)]," \"%s\"",argv[i]);
}
memset(&pi,0,sizeof(PROCESS_INFORMATION));
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwX = 0; /* Ignored unless si.dwFlags |= STARTF_USEPOSITION */
si.dwY = 0;
si.dwXSize = 0; /* Ignored unless si.dwFlags |= STARTF_USESIZE */
si.dwYSize = 0;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
/*
** Note that launching ourselves from a console will NOT create new console.
*/
CreateProcess(exename,buf,0,0,1,DETACHED_PROCESS,0,NULL,&si,&pi);
return(10); /* Re-launched return code */
}
/*
** GUI code starts here
*/
hinst=GetModuleHandle(NULL);
/* Free the console that we started with */
FreeConsole();
/* GUI call with functionality of WinMain */
return(my_win_main(hinst,argc,argv,minimized ? SW_MINIMIZE : SW_SHOWNORMAL));
}
static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
static char *wintitle="GUI Window";
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = NULL;
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = wintitle;
wndclass.hIconSm = NULL;
RegisterClassEx (&wndclass) ;
hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wintitle,0,
WS_VISIBLE|WS_OVERLAPPEDWINDOW,
100,100,400,200,NULL,NULL,hInstance,NULL);
SetWindowText(hwnd,wintitle);
ShowWindow(hwnd,iCmdShow);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
static LRESULT CALLBACK WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
{
if (iMsg==WM_DESTROY)
{
PostQuitMessage(0);
return(0);
}
return(DefWindowProc(hwnd,iMsg,wParam,lParam));
}
static int fwbp_pid;
static int fwbp_count;
static int win_started_from_console(void)
{
fwbp_pid=GetCurrentProcessId();
if (fwbp_pid==0)
return(0);
fwbp_count=0;
EnumWindows((WNDENUMPROC)find_win_by_procid,0L);
return(fwbp_count==0);
}
static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp)
{
int pid;
GetWindowThreadProcessId(hwnd,(LPDWORD)&pid);
if (pid==fwbp_pid)
fwbp_count++;
return(TRUE);
}
I have written up an alternative approach which avoids the console flash. See How to create a Windows program that works both as a GUI and console application.
Run AllocConsole() in a static constructor works for me
I have a situation where I'm starting a process in my code in order to set up an IPC channel. The process I'm starting is an MFC application with no CLR support. The application from which I am starting this process is a C# module in a WPF application (thought I don't think that that is consequential to my problem). This works with a version of the application that does support CLR, and it works on every computer except the deployment target, a touch screen computer with Windows 7. But for some reason, when I try it with this exact scenario, the Process object never resolves a main window handle (Process.MainWindowHandle). Is there another (perhaps even pinvoke) method of doing this? Is this a security thing? I'm the one staring the process. The process's main window handle does exist. I don't see what could be wrong.
If it helps, here is my code.
_applicationProcess = new Process();
_applicationProcess.StartInfo.FileName = _strProcessPath;
_applicationProcess.StartInfo.Arguments = _strProcessArguments;
_applicationProcess.Start();
long nTicks = Environment.TickCount;
if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
{
try
{
do
{
// Don't let total processing take more than 1 minute(s).
if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");
_applicationProcess.Refresh();
}
while (_applicationProcess.MainWindowHandle.ToInt32() == 0);
_applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
else
{
// Do exception handling.
}
The ApplicationException is hit after a minute of trying to get a main window handle other than zero.
The value you get out of Process.MainWindowHandle is unfortunately a guess. There is no API function available to a program that lets it tell Windows "this is my main window". The rule it uses is documented, it is the first window that's created by a process when it gets started. That causes trouble if that first window is, say, a login window or a splash screen.
Not much you can do about this, you have to know more about how the program behaves to find that real main window. Enumerating windows with EnumThreadWindows() could help you find it, as long as the first window was created on the same thread as the main window. A more elaborate EnumWindows() will be necessary if that is not the case.
My habit is to call EnumWindows in a loop combined with GetWindowThreadProcessId to find the window handle.
C Code, adapt to your language
DWORD TargetHWND;
//...
while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
Sleep(100);
}
//...
BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
DWORD pid = (DWORD)-1;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == (DWORD)lParam) {
TargetHWND = hWnd;
return FALSE;
}
return TRUE;
}
In order to get MainWindowHandle by means of your process, please make sure your WPF application is shown on the taskbar i.e ShowInTaskbar="True" and set Application.Current.MainWindow property to the window that you'd like to set as your main window.
If I execute code below in my WPF main window without setting ShowInTaskbar="True" I always got 0 as the MainWindowHandle because my WPF window was full screen and not displayed on the taskbar.
Application.Current.MainWindow = this;
var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");
if (Query.Any())
{
Query.FirstOrDefault().Refresh();
MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
}
I don't know why it could be different, but after you create the process, try doing:
Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");
and see if any of the processes returned have a MainWindowHandle.
i have been trying the code using mutex but im unable to open my exe after button click
im successful in not making the multiple entries of the application on the taskbar at button click but my application is launched only when i close my form..
i want to launch my application on button click and if the application is already launched then i need to focus on the previous running application..
how could i able to resolve my need to launch as well as focusin and reopening that application again..
im sending u my code that im using on button click event and plz modify my errors...
coding at program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
System.Diagnostics.Process.Start("filename.exe");
}
:
coding done at form1.cs
private void button1_Click(object sender, EventArgs e)
{
try
{
bool createdNew;
Mutex m = new Mutex(true, "e-Recording", out createdNew);
System.Diagnostics.ProcessStartInfo f = new System.Diagnostics.ProcessStartInfo("C:\\windows\\system32\\rundll32.exe", "C:\\windows\\system32\\shimgvw.dll,ImageView_Fullscreen " + "filename.exe".TrimEnd(null));
if (createdNew) Launch();
else
{
MessageBox.Show("e-Recording is already running!", "Multiple Instances");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
This is be able to find and switch to an already running process that matches what you are trying to start.
[DllImport( "user32.dll" )]
public static extern bool ShowWindowAsync( HandleRef hWnd, int nCmdShow );
public const int SW_RESTORE = 9;
public void SwitchToCurrent() {
IntPtr hWnd = IntPtr.Zero;
Process process = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName( process.ProcessName );
foreach ( Process _process in processes ) {
// Get the first instance that is not this instance, has the
// same process name and was started from the same file name
// and location. Also check that the process has a valid
// window handle in this session to filter out other user's
// processes.
if ( _process.Id != process.Id &&
_process.MainModule.FileName == process.MainModule.FileName &&
_process.MainWindowHandle != IntPtr.Zero ) {
hWnd = _process.MainWindowHandle;
ShowWindowAsync( NativeMethods.HRef( hWnd ), SW_RESTORE );
break;
}
}
}
I posted an answer a while back to a question about Delphi. I explained that I didn't have a background in Delphi but I described at a high level what I did in C# to build a component that uses InterProcess Communication (IPC) with .NET remoting to not only activate a running instance, but also forward the command line parameters from the second instance into the first instance. I linked to a pretty simple to use component that wraps all this functionality up. It may be useful to you.
Hers's my answer from the other question:
The best way to do this is actually in
the the startup code of your exe. In
other words, let Explorer launch a
second copy of the exe which then
proceeds to detect that it is already
running and have it send a message to
the running instance.
Personally, I have practically no
experience with Delphi, but the way I
did this in a .NET application was
using a mutex and an interprocess
communication channel.
The general idea was that the first
instance of the application would
start, and begin listening on an IPC
channel. It would also create a named
interprocess mutex. When the second
instance launched, it would be unable
to create the mutex of the same name
which meant that a previous instance
was running and listening for calls on
the IPC channel. The second instance
then sent the command line arguments
to the first instance over IPC and the
first instance took action on them.
The second instance then exits without
showing any UI.
I've uploaded the code for this
component (C#) and the link is below.
I don't believe it has any external
dependencies and I don't know what the
equivalent communication mechanism in
Delphi would be - but hopefully this
gives you some ideas.
InstanceManager Component (C#)
Note that usage of named mutexes is discouraged for security reasons. Any process (even one running under guest account) can create a mutex with the same name before your process was started. Solving these security problems is usually harder than just not using named mutex at all.
To solve your problem, you just need to store process handler or process ID and then look for a window with that process ID. This is similar to the way task manager works.
I want to make a C# program that can be run as a CLI or GUI application depending on what flags are passed into it. Can this be done?
I have found these related questions, but they don't exactly cover my situation:
How to write to the console in a GUI application
How do I get console output in C++ with a Windows program?
Jdigital's answer points to Raymond Chen's blog, which explains why you can't have an application that's both a console program and a non-console* program: The OS needs to know before the program starts running which subsystem to use. Once the program has started running, it's too late to go back and request the other mode.
Cade's answer points to an article about running a .Net WinForms application with a console. It uses the technique of calling AttachConsole after the program starts running. This has the effect of allowing the program to write back to the console window of the command prompt that started the program. But the comments in that article point out what I consider to be a fatal flaw: The child process doesn't really control the console. The console continues accepting input on behalf of the parent process, and the parent process is not aware that it should wait for the child to finish running before using the console for other things.
Chen's article points to an article by Junfeng Zhang that explains a couple of other techniques.
The first is what devenv uses. It works by actually having two programs. One is devenv.exe, which is the main GUI program, and the other is devenv.com, which handles console-mode tasks, but if it's used in a non-console-like manner, it forwards its tasks to devenv.exe and exits. The technique relies on the Win32 rule that com files get chosen ahead of exe files when you type a command without the file extension.
There's a simpler variation on this that the Windows Script Host does. It provides two completely separate binaries, wscript.exe and cscript.exe. Likewise, Java provides java.exe for console programs and javaw.exe for non-console programs.
Junfeng's second technique is what ildasm uses. He quotes the process that ildasm's author went through when making it run in both modes. Ultimately, here's what it does:
The program is marked as a console-mode binary, so it always starts out with a console. This allows input and output redirection to work as normal.
If the program has no console-mode command-line parameters, it re-launches itself.
It's not enough to simply call FreeConsole to make the first instance cease to be a console program. That's because the process that started the program, cmd.exe, "knows" that it started a console-mode program and is waiting for the program to stop running. Calling FreeConsole would make ildasm stop using the console, but it wouldn't make the parent process start using the console.
So the first instance restarts itself (with an extra command-line parameter, I suppose). When you call CreateProcess, there are two different flags to try, DETACHED_PROCESS and CREATE_NEW_CONSOLE, either of which will ensure that the second instance will not be attached to the parent console. After that, the first instance can terminate and allow the command prompt to resume processing commands.
The side effect of this technique is that when you start the program from a GUI interface, there will still be a console. It will flash on the screen momentarily and then disappear.
The part in Junfeng's article about using editbin to change the program's console-mode flag is a red herring, I think. Your compiler or development environment should provide a setting or option to control which kind of binary it creates. There should be no need to modify anything afterward.
The bottom line, then, is that you can either have two binaries, or you can have a momentary flicker of a console window. Once you decide which is the lesser evil, you have your choice of implementations.
* I say non-console instead of GUI because otherwise it's a false dichotomy. Just because a program doesn't have a console doesn't mean it has a GUI. A service application is a prime example. Also, a program can have a console and windows.
Check out Raymond's blog on this topic:
https://devblogs.microsoft.com/oldnewthing/20090101-00/?p=19643
His first sentence: "You can't, but you can try to fake it."
http://www.csharp411.com/console-output-from-winforms-application/
Just check the command line arguments before the WinForms Application. stuff.
I should add that in .NET it is RIDICULOUSLY easy to simply make a console and GUI projects in the same solution which share all their assemblies except main. And in this case, you could make the command line version simply launch the GUI version if it is launched with no parameters. You would get a flashing console.
There is an easy way to do what you want. I'm always using it when writing apps that should have both a CLI and a GUI. You have to set your "OutputType" to "ConsoleApplication" for this to work.
class Program {
[DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")]
private static extern IntPtr _GetConsoleWindow();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
/*
* This works as following:
* First we look for command line parameters and if there are any of them present, we run the CLI version.
* If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console.
* If there is no console at all, we show the GUI.
* We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part.
* This way we're both a CLI and a GUI.
*/
if (args != null && args.Length > 0) {
// execute CLI - at least this is what I call, passing the given args.
// Change this call to match your program.
CLI.ParseCommandLineArguments(args);
} else {
var consoleHandle = _GetConsoleWindow();
// run GUI
if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost"))
// we either have no console window or we're started from within visual studio
// This is the form I usually run. Change it to match your code.
Application.Run(new MainForm());
else {
// we found a console attached to us, so restart ourselves without one
Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) {
CreateNoWindow = true,
UseShellExecute = false
});
}
}
}
I think the preferred technique is what Rob called the devenv technique of using two executables: a launcher ".com" and the original ".exe". This is not that tricky to use if you have the boilerplate code to work with (see below link).
The technique uses tricks to have that ".com" be a proxy for the stdin/stdout/stderr and launch the same-named .exe file. This give the behavior of allowing the program to preform in a command line mode when called form a console (potentially only when certain command-line arguments are detected) while still being able to launch as a GUI application free of a console.
I hosted a project called dualsubsystem on Google Code that updates an old codeguru solution of this technique and provides the source code and working example binaries.
Here is what I believe to be the simple .NET C# solution to the problem. Just to restate the problem, when you run the console "version" of the app from a command line with a switch, the console keeps waiting (it doesn't return to the command prompt and the process keeps running) even if you have an Environment.Exit(0) at the end of your code. To fix this, just before calling Environment.Exit(0), call this:
SendKeys.SendWait("{ENTER}");
Then the console gets the final Enter key it needs to return to the command prompt and the process ends. Note: Don't call SendKeys.Send(), or the app will crash.
It's still necessary to call AttachConsole() as mentioned in many posts, but with this I get no command window flicker when launching the WinForm version of the app.
Here's the entire code in a sample app I created (without the WinForms code):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ConsoleWriter
{
static class Program
{
[DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[STAThread]
static void Main(string[] args)
{
if(args.Length > 0 && args[0].ToUpperInvariant() == "/NOGUI")
{
AttachConsole(ATTACH_PARENT_PROCESS);
Console.WriteLine(Environment.NewLine + "This line prints on console.");
Console.WriteLine("Exiting...");
SendKeys.SendWait("{ENTER}");
Environment.Exit(0);
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
}
Hope it helps someone from also spending days on this problem. Thanks for the hint go to #dantill.
/*
** dual.c Runs as both CONSOLE and GUI app in Windows.
**
** This solution is based on the "Momentary Flicker" solution that Robert Kennedy
** discusses in the highest-rated answer (as of Jan 2013), i.e. the one drawback
** is that the console window will briefly flash up when run as a GUI. If you
** want to avoid this, you can create a shortcut to the executable and tell the
** short cut to run minimized. That will minimize the console window (which then
** immediately quits), but not the GUI window. If you want the GUI window to
** also run minimized, you have to also put -minimized on the command line.
**
** Tested under MinGW: gcc -o dual.exe dual.c -lgdi32
**
*/
#include <windows.h>
#include <stdio.h>
static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow);
static LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam);
static int win_started_from_console(void);
static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp);
int main(int argc,char *argv[])
{
HINSTANCE hinst;
int i,gui,relaunch,minimized,started_from_console;
/*
** If not run from command-line, or if run with "-gui" option, then GUI mode
** Otherwise, CONSOLE app.
*/
started_from_console = win_started_from_console();
gui = !started_from_console;
relaunch=0;
minimized=0;
/*
** Check command options for forced GUI and/or re-launch
*/
for (i=1;i<argc;i++)
{
if (!strcmp(argv[i],"-minimized"))
minimized=1;
if (!strcmp(argv[i],"-gui"))
gui=1;
if (!strcmp(argv[i],"-gui-"))
gui=0;
if (!strcmp(argv[i],"-relaunch"))
relaunch=1;
}
if (!gui && !relaunch)
{
/* RUN AS CONSOLE APP */
printf("Console app only.\n");
printf("Usage: dual [-gui[-]] [-minimized].\n\n");
if (!started_from_console)
{
char buf[16];
printf("Press <Enter> to exit.\n");
fgets(buf,15,stdin);
}
return(0);
}
/* GUI mode */
/*
** If started from CONSOLE, but want to run in GUI mode, need to re-launch
** application to completely separate it from the console that started it.
**
** Technically, we don't have to re-launch if we are not started from
** a console to begin with, but by re-launching we can avoid the flicker of
** the console window when we start if we start from a shortcut which tells
** us to run minimized.
**
** If the user puts "-minimized" on the command-line, then there's
** no point to re-launching when double-clicked.
*/
if (!relaunch && (started_from_console || !minimized))
{
char exename[256];
char buf[512];
STARTUPINFO si;
PROCESS_INFORMATION pi;
GetStartupInfo(&si);
GetModuleFileNameA(NULL,exename,255);
sprintf(buf,"\"%s\" -relaunch",exename);
for (i=1;i<argc;i++)
{
if (strlen(argv[i])+3+strlen(buf) > 511)
break;
sprintf(&buf[strlen(buf)]," \"%s\"",argv[i]);
}
memset(&pi,0,sizeof(PROCESS_INFORMATION));
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwX = 0; /* Ignored unless si.dwFlags |= STARTF_USEPOSITION */
si.dwY = 0;
si.dwXSize = 0; /* Ignored unless si.dwFlags |= STARTF_USESIZE */
si.dwYSize = 0;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
/*
** Note that launching ourselves from a console will NOT create new console.
*/
CreateProcess(exename,buf,0,0,1,DETACHED_PROCESS,0,NULL,&si,&pi);
return(10); /* Re-launched return code */
}
/*
** GUI code starts here
*/
hinst=GetModuleHandle(NULL);
/* Free the console that we started with */
FreeConsole();
/* GUI call with functionality of WinMain */
return(my_win_main(hinst,argc,argv,minimized ? SW_MINIMIZE : SW_SHOWNORMAL));
}
static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
static char *wintitle="GUI Window";
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = NULL;
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = wintitle;
wndclass.hIconSm = NULL;
RegisterClassEx (&wndclass) ;
hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wintitle,0,
WS_VISIBLE|WS_OVERLAPPEDWINDOW,
100,100,400,200,NULL,NULL,hInstance,NULL);
SetWindowText(hwnd,wintitle);
ShowWindow(hwnd,iCmdShow);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
static LRESULT CALLBACK WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)
{
if (iMsg==WM_DESTROY)
{
PostQuitMessage(0);
return(0);
}
return(DefWindowProc(hwnd,iMsg,wParam,lParam));
}
static int fwbp_pid;
static int fwbp_count;
static int win_started_from_console(void)
{
fwbp_pid=GetCurrentProcessId();
if (fwbp_pid==0)
return(0);
fwbp_count=0;
EnumWindows((WNDENUMPROC)find_win_by_procid,0L);
return(fwbp_count==0);
}
static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp)
{
int pid;
GetWindowThreadProcessId(hwnd,(LPDWORD)&pid);
if (pid==fwbp_pid)
fwbp_count++;
return(TRUE);
}
I have written up an alternative approach which avoids the console flash. See How to create a Windows program that works both as a GUI and console application.
Run AllocConsole() in a static constructor works for me