0

I have a winapi program that I wish to not open any windows if executed with command line arguments. I can attach to the parent console perfectly and WriteConsoleA() works, but when I try to redirect C I/O, std::cout, and std::cin to the console (following the methodology of several StackOverflow posts about this subject), these will not write to the attached console as expected.

main.c -

#include <windows.h>
#include <stdio.h>
#include "cmd_line.h"
#include "dialog.h"
#include "resource.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    if(__argc > 1)
    {
        RedirectIOToConsole(); // Attaches and redirects

        // Temporary tests
        HANDLE consoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
        char a[] = "hi";
        WriteConsoleA(consoleOut, &a, 2, NULL, NULL); // Prints
        printf("hi\n"); // Does not print

        // External C++ function which performs my command line option via lots of std::cout and cin
        CmdLine(__argc, __argv); // Does not print
    }
    else
        // External C++ function to handle my winapi dialog
        return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DlgProc);
}

part of cmd_line.cpp which implements RedirectIOToConsole() -

void RedirectIOToConsole()
{
    int hConHandle;
    long lStdHandle;
    FILE *fp;

    AttachConsole(ATTACH_PARENT_PROCESS);

    // STDOUT to the console
    lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );
    *stdout = *fp;
    setvbuf( stdout, NULL, _IONBF, 0 );

     // STDIN to the console
    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "r" );
    *stdin = *fp;
    setvbuf( stdin, NULL, _IONBF, 0 );

    // STDERR to the console
    lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "w" );
    *stderr = *fp;
    setvbuf( stderr, NULL, _IONBF, 0 );

    // C++ streams to console
    std::ios_base::sync_with_stdio();
}

Could someone please help me with a proper, working way to redirect stdio and iostream to this console? Thank you!

OCDkirby
  • 190
  • 9

2 Answers2

2

This should work, but I didn't test it:

void RedirectIOConsole()
{
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONERR$", "w", stderr);
}
DarkAtom
  • 2,589
  • 1
  • 11
  • 27
  • This works if I define the `_CRT_SECURE_NO_WARNINGS` symbol, but how would I port this to `freopen_s()` for security? If I can get that working, then this is the answer! – OCDkirby May 09 '21 at 20:26
  • `freopen_s()` is not standard C++. Please don't let this MSVC bulls**t bother you. – DarkAtom May 09 '21 at 20:30
  • Check this: https://stackoverflow.com/a/50921096/10505778 – DarkAtom May 09 '21 at 20:32
  • There is a proposal for the secure functions to be added to the C++ standard, so I'm going to try and future-proof my code as much as possible because it really doesn't cause me that much trouble. Found an example of _s in the Microsoft docs, thank you so much for getting me started on finding the answer! EDIT: Thanks for the info on the _s functions, but I'm going to still use what Microsoft's toolchain recommends. – OCDkirby May 09 '21 at 20:34
  • Don't blame Microsoft for moving the language forward, when the C Committee won't, because it is too busy with itself ([Why the C Language Will Never Stop You from Making Mistakes](https://thephd.github.io/your-c-compiler-and-standard-library-will-not-help-you)). Your options are either `1` opt in to non-portable extensions and benefit from advancements to make your C just a *tiny* bit safer, or, `2` stick with portable C and allow your broken code to compile on as many platforms as humanly possible. A responsible developer shouldn't have much trouble choosing. – IInspectable May 10 '21 at 09:50
  • @IInspectable I literally linked to an annex k removal proposal which explains why they are not that safe. Also, Microsoft proposed these functions to the C standard which they don't even implement, and they don't even have a conforming implementation of the `_s` functions. They simply wanted to show their power as a company. All the other reputable compilers that are TRULY portable, like GCC and Clang, did not implement the functions. – DarkAtom May 10 '21 at 10:12
  • That's understood. GCC and Clang value portability more than anything else, even if that anything else includes safety. They are testament to the state of the C language, squarely footed on the idea of being, and **staying** as unsafe as possible. Me, personally, I like the *"MSVC bulls\*\*t"*, though, if you really want to make a point, you'd also have to be consistent and spell that *"M$VC"*. Like everyone else that believes the compiler team were the official Microsoft channel for communicating company policies. Or some other construed conspiracy theory. – IInspectable May 10 '21 at 10:22
  • 1
    @IInspectable If you want safety, choose another language. C is about responsibility, freedom and control. In a company, decisions like this (submitting proposals to the C committee) are not made by the developing team, but somewhere higher up the hierarchy. If the MS devs took that decision, they would have at least implemented the standard. And they certainly wouldn't come up with a function as ridiculous as `strnlen_s()`. I don't understand what you mean by other compilers staying as unsfae as possible. GCC, for example, can bound check all memory accesses at runtime for debugging purposes. – DarkAtom May 10 '21 at 10:33
  • 1
    What point are you trying to make? The OP of this question is writing Windows code. They have expressed that they don't care about portability. MSVC is the compiler they use. *"Choose another language"* is not a valid suggestion when the programming language isn't negotiable. And here you are, telling them to stay clear of whatever compiler/library support they have at their disposal. For what reason, specifically? – IInspectable May 10 '21 at 14:12
  • The OP simply expressed their willingness to use MSVC and that was it. You came along and started to throw arguments about how other compilers keep C very unsafe, the C commitee is too busy with itself and the brave MSVC wants to move the language forward, but it doesn't even support it. I felt the need to reply, since your arguments are not true and other people might read this comment section. The OP does no harm by choosing the compiler he wishes, but he also didn't start an argument about it. – DarkAtom May 10 '21 at 15:19
  • @IInspectable Also, in case I didn't make myself clear, I never recommended the OP to not use MSVC. I recommended to stay portable to some extent, given the fact that the _s functions are questionable in their usefulness. – DarkAtom May 10 '21 at 15:22
  • *"I never recommended the OP to not use MSVC"* - *"`freopen_s()` is not standard C++. Please don't let this MSVC bulls\*\*t bother you."* - Care to explain how those two go together? There's nothing *"questionable about the usefulness"* of `freopen_s()`. It is a strictly superior implementation to `freopen()`. Your only substantial point against using `freopen_s()` apparently is, that `strnlen_s()` were ridiculous. I'm not convinced. – IInspectable May 11 '21 at 08:43
  • @IInspectable Strictly superior implementation? The fact that you cannot nest it because it doesn't return FILE* and the fact that it takes an additional pointer which it has to check for NULL make it very inneficient. The fact that the runtime constraint handler is extremely unsafe if used incorrectly makes it unsafe. And this is without taking portability into consideration. Did you read the Annex K removal proposal? All of the points I am making have been explained in detail over there. – DarkAtom May 11 '21 at 09:15
  • I'm not referring to what the TR 24731-1 suggests. I'm referring to Microsoft's implementation of `freopen_s()`. Which, as we have established, **doesn't** implement the Standard. For good reasons, too, like providing the ability to supply a **thread-local** constraint handler. One of the issues with the Standard that's explicitly called out in the Annex K removal proposal. So what we have is a Standard, that's broken, a compiler vendor that provides an implementation that fixes that brokenness, and someone that thinks MS sucks for not implementing a broken Standard. – IInspectable May 11 '21 at 09:48
  • @IInspectable Let's get this clear. The MS compiler is a C++ compiler, which is a different language than C. The C++ standard does not even mention _s functions, but MS implements them as a compiler extension. The C standard has a broken Annex BECAUSE of MS (because they came up with the idea and proposed it, and the used their power as a company to approve the proposal with minimal modification). Using the C library in C++ is generally not the right way, but the OP wants to redirect the C streams as well. – DarkAtom May 11 '21 at 09:59
  • @IInspectable And about the constraint handler: the fact that it's thread local doesn't make it safe. Constraint violations are not regular errors - they are errors on the programmer's side. In normal operation, constraint violations should never happen. If one does, the safest thing to do is to terminate the program ASAP, so that there is less time to abuse any vulnerability. But terminating the program is not always doable - if the constraint handler was called while sensitive information (i.e. passwords) are in memory, then that data could remain in memory after termination. – DarkAtom May 11 '21 at 10:04
  • But you know what? Let's end it here, since I don't want to create more spam. Neither of us will get convinced by the other, so it's pointless. – DarkAtom May 11 '21 at 10:05
  • I'm not trying to convince you. The comments are here so that future visitors have a chance to assess your credibility. – IInspectable May 11 '21 at 11:21
-3

You should use stdout = fp;, not *stdout = *fp. The same in the other two assignments.

Iziminza
  • 374
  • 3
  • 8