0

I launch my application in cmd. I compiled it by Cmake without WIN32_EXECUTABLE, so it hangs in cmd (i.e. is launched as not detached). Now I want to close the console and try to achieve this by calling FreeConsole().

This works in case I double-click the application -- the black console is flashes quickly and gets closed. But it does not work when I launch it in cmd. The cmd is still attached to the launched application and FreeConsole() does not help.

Is there any way to detach it from cmd programmatically? I have found the opportunity to run start /b myapp, but I would like to do it in a programmatical way.

UPDATE

A rough implementation of the answer of Anders for those who are interested -- see below. In this implementation I pass all the arguments, supplied to main.com, to the child process main.exe.

// main.cpp
// cl main.cpp
// link main.obj /SUBSYSTEM:WINDOWS /ENTRY:"mainCRTStartup"
//
#include <stdio.h>
#include <Windows.h>

int main(int argc, char** argv)
{
    if (AttachConsole(ATTACH_PARENT_PROCESS)) {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);
        freopen("CONIN$", "r", stdin);
    }

    for (int i = 0; i < argc; i++)
        printf("--%s", argv[i]);

    printf("Hello\n");
    int k = 0;
    scanf("%d", &k);
    printf("%d\n", k);

    return 0;
}
// helper.cpp
// cl helper.cpp
// link helper.obj /OUT:main.com
//
#include <windows.h>
#include <stdio.h>
#include <string.h>

void main( int argc, char **argv )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Reconstructing the command line args for main.exe:

    char cmdLine[32767] = { 0 };
    strcpy(cmdLine, "main.exe ");
    int shift = strlen("main.exe ");

    for (int i = 1 ; i < argc; i++)
    {
        strcpy(cmdLine + shift, argv[i]);
        const int argLength = strlen(argv[i]);
        cmdLine[shift + argLength] = ' ';
        shift += (argLength + 1);
    }

    printf("\n!!!!%s!!!!!%s\n", cmdLine, GetCommandLine());
    // Start the child process.
    // https://learn.microsoft.com/en-us/windows/win32/procthread/creating-processes
    //
    if( !CreateProcess(NULL, // No module name (use command line)
        cmdLine,             // Command line
        NULL,                // Process handle not inheritable
        NULL,                // Thread handle not inheritable
        FALSE,               // Set handle inheritance to FALSE
        0,                   // No creation flags
        NULL,                // Use parent's environment block
        NULL,                // Use parent's starting directory
        &si,                 // Pointer to STARTUPINFO structure
        &pi )                // Pointer to PROCESS_INFORMATION structure
    )
    {
        printf( "CreateProcess failed (%d).\n", GetLastError() );
        return;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles.
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}
JenyaKh
  • 2,040
  • 17
  • 25
  • What are you *really* trying to accomplish here? This reads a lot like the [XY Problem](https://xyproblem.info). – IInspectable Oct 06 '22 at 08:25
  • I want to have an application that when you click two times or launch it in cmd, you get it detached and without console. But when you launch it in console with -nogui option, it would be a console application. – JenyaKh Oct 06 '22 at 08:26
  • 3
    Then target the `/SUBSYSTEM:WINDOWS` (which solves the first two), and call `AttachConsole` and/or `AllocConsole` when the `-nogui` option is passed (which addresses the final one). – IInspectable Oct 06 '22 at 08:28
  • If I understand right, it will create a new console, won't it? – JenyaKh Oct 06 '22 at 08:33
  • 1
    [`AttachConsole`](https://learn.microsoft.com/en-us/windows/console/attachconsole) doesn't create a console. I mentioned [`AllocConsole`](https://learn.microsoft.com/en-us/windows/console/allocconsole) (which does create a console) for the case where `AttachConsole` fails (e.g. if you are creating a short cut with the `-nogui` option, and double-click it). – IInspectable Oct 06 '22 at 08:37
  • Could you, please, provide some code? Because I tried to put `AttachConsole(GetCurrentProcessId());` in my main(), but it did not help. – JenyaKh Oct 06 '22 at 08:41
  • *Your* process doesn't have a console. Attaching your process to a console that doesn't exist is meaningless. You'll want to attach to your process' parent's console. The documentation explains how to do that. – IInspectable Oct 06 '22 at 08:46
  • Ah, yes, I tried this way -- with `freopen("CON", "w", stdout)` and `freopen("CON", "r", stdin)`. But this way, I cannot make input to stdin properly: https://stackoverflow.com/questions/61544872/using-istream-std-cin-prevent-input-is-not-recognized-as-on-windows – JenyaKh Oct 06 '22 at 08:50
  • Getting console I/O connected to C's *stdio.h* is a different problem. Still, the fact that you claim to have made changes to your `main()` function implies that you aren't actually targeting the WINDOWS subsystem. It uses the `WinMain`/`wWinMain` entry points by default. – IInspectable Oct 06 '22 at 09:32
  • Programs like Java and Python solve this by having two executable hosts, `java.exe/javaw.exe` and `python.exe/pythonw.exe` which works very well. – David Heffernan Oct 06 '22 at 11:12

1 Answers1

3

Creating a "perfect" application that can be both GUI or console is not possible on Windows.

When a .exe has its subsystem set to console, two things happen:

  • CreateProcess will attach it to the parents console. If the parent does not have one, a new console is created. This can only be suppressed by passing flags to CreateProcess.
  • cmd.exe will wait for the process to finish.

The trick of setting the subsystem to Windows and using AttachConsole is sub-optimal because cmd.exe will not wait.

The best solution is to create your main .exe as a Windows application. Create another console helper application that you rename from .exe to .com. The helper application will just launch the main app in GUI mode and then quit. In console mode it has to tell the main app that it needs to attach and you need to WaitForSingleObject on it. Example application available here .

Visual Studio uses this .com trick. The trick works because .com is listed before .exe in %pathext%.

Anders
  • 97,548
  • 12
  • 110
  • 164
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/248683/discussion-on-answer-by-anders-freeconsole-does-not-detach-application-from-cm). – Samuel Liew Oct 10 '22 at 01:49
  • Sorry, I have not seen any notification on your last message, because the discussion was moved to the chat. I am a bit not sure, why do you say that this is not related to the task? The task, as I understand it, is to have an app that is both gui and console, i.e. both gui and the one where you can have stdout/stdin to/from console. – JenyaKh Oct 12 '22 at 05:52
  • The question is about Win32 console. You did not add any details about which C library you are using. The main task of the interaction between GUI app and console is pure Win32. After that is done, the follow up question of how to connect your C library to these new handles are 1) dependent on which C library you are using and 2) it would be a duplicate question. Just search SO for `freopen stdout`, many many answers. Use `freopen("CON...` or `_dup2` or whatever your library provides. – Anders Oct 12 '22 at 09:44
  • ...for `_dup2` you might have to `DuplicateHandle`+`_open_osfhandle` first for example. – Anders Oct 12 '22 at 09:52