Do not show console window in release, but show in debug

I would like to create a program that will run in the background as the final product. For debugging purpose, I want it to display the console.

I found out there is a ShowWindow function (hWnd, SW_HIDE) , but if I use it in a standard function, the console window still appears momentarily. I tried to do this (yes, I know it's shitty):

#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <tchar.h>


#define DEBUG
//#undef DEBUG

#ifndef DEBUG
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    HWND hWnd = GetConsoleWindow();
    ShowWindow( hWnd, SW_HIDE );    

    while(1);

    return 0;
}
#else
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
    int main(int argc, int **argv)
    {
        HWND hWnd = GetConsoleWindow();

        while(1);

        return 0;
    }
#endif

      

Here I managed to prevent the window form from appearing, but I cannot pass parameters to the program.

I believe there is a much better solution for this. Can you share?

PS

I don't want to use .NET.

+3


source to share


4 answers


This is the answer to the first part of the question: "Here I was able to prevent the appearance of the window form", that is, how to install the Windows subsystem for an application in Visual C ++.

I will answer the second part of the question, about command line arguments, separately.

// How to create a Windows GUI or console subsystem app with a standard `main`.

#ifndef _MSC_VER
#   error Hey, this is Visual C++ specific source code!
#endif

// Better set this in the project settings, so that it more easily configured.
#ifdef  NDEBUG
#   pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
#else
#   pragma comment( linker, "/subsystem:console" )
#endif

#undef  UNICODE
#define UNICODE
#undef  NOMINMAX
#define NOMINAX
#undef  STRICT
#define STRICT
#include <windows.h>

int main()
{
    MessageBox( 0, L"Hi!", L"This is the app!", MB_SETFOREGROUND );
}

      

The C ++ standard standard macro is designed to suppress the effect of the standard , so it shouldn't be globally significant. However, in practice, this is globally significant. And then it provides little portability compared to using a Visual C ++ macro like . NDEBUG

assert

DEBUG



Anyway, to make it easier to configure the subsystem, if you don't want these debug builds to be console-based and the release builds need to be graphical, then I recommend doing this in the project settings, not via #pragma

(note also that, for example, the compiler g ++ does not support linkers, so using project options is more portable).

If you want, you can check the subsystem programmatically, rather than just checking (i.e. instead of noting whether the above program creates a console or not):

// How to create a Windows GUI or console subsystem app with a standard `main`.

#ifndef _MSC_VER
#   error Hey, this is Visual C++ specific source code!
#endif

// Better set this in the project settings, so that it more easily configured.
#ifdef  NDEBUG
#   pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
#else
#   pragma comment( linker, "/subsystem:console" )
#endif

#undef  UNICODE
#define UNICODE
#undef  NOMINMAX
#define NOMINAX
#undef  STRICT
#define STRICT
#include <windows.h>

#include <assert.h>         // assert
#include <string>           // std::wstring
#include <sstream>          // std::wostringstream
using namespace std;

template< class Type >
wstring stringFrom( Type const& v )
{
    wostringstream  stream;

    stream << v;
    return stream.str();
}

class S
{
private:
    wstring     s_;

public:
    template< class Type >
    S& operator<<( Type const& v )
    {
        s_ += stringFrom( v );
        return *this;
    }

    operator wstring const& () const { return s_; }
    operator wchar_t const* () const { return s_.c_str(); }
};

IMAGE_NT_HEADERS const& imageHeaderRef()
{
    HMODULE const                   hInstance   =
        GetModuleHandle( nullptr );

    IMAGE_DOS_HEADER const* const   pImageHeader    =
        reinterpret_cast< IMAGE_DOS_HEADER const* >( hInstance );
    assert( pImageHeader->e_magic == IMAGE_DOS_SIGNATURE );     // "MZ"

    IMAGE_NT_HEADERS const* const   pNTHeaders      = reinterpret_cast<IMAGE_NT_HEADERS const*>(
            reinterpret_cast< char const* >( pImageHeader ) + pImageHeader->e_lfanew
            );
    assert( pNTHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC );    // "PE"

    return *pNTHeaders;
}

int main()
{
    IMAGE_NT_HEADERS const& imageHeader = imageHeaderRef();
    WORD const              subsystem   = imageHeader.OptionalHeader.Subsystem;

    MessageBox(
        0,
        S() << L"Subsystem " << subsystem << L" "
            << (0?0
                : subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI?     L"GUI"
                : subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI?     L"Console"
                : L"Other"),
        L"Subsystem info:",
        MB_SETFOREGROUND );
}

      

+4


source


You can still pass parameters to a regular, unconfigured Win32 program: they just appear on a single line lpCmdLine

, they are all concatenated into one large command line. You can use CommandLineToArgvW

to parse this into separate arguments, but note that this function is only available in Unicode style. For example:

int wmain(int argc, wchar_t **argv)
{
    // Common main function (Unicode args)
}

#ifndef DEBUG
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    // Forward invocation to wmain
    int argc;
    LPWSTR *argv = CommandLineToArgvW(pCmdLine, &argc);
    int status = wmain(argc, argv);
    LocalFree(argv);
    return status;
}
#endif

      



I also recommend using your project settings to set the executable (console or Windows) depending on your configuration instead of using #pragma

it to run it.

+2


source


This is the answer to the second part of the question: "but I cannot pass parameters to the program", i.e. How to get command line arguments in a Visual C ++ Windows application.

The easiest but most limited way is to use standard C ++ arguments main

,

int main( int argc, char* argv[] )
{
    // Whatever, e.g.
    vector<string> const args( argv, argv + argc );
}

      

The C ++ Standard strongly suggests that these arguments be encoded with multiple multibyte character sets such as UTF-8.

C ++ 11 Β§3.6.1 / 2 :

"If argc

nonzero, these arguments must be represented in argv[0]

via argv[argc-1]

as pointers to the starting characters of null-terminated multibyte strings (NTMBS) (17.5.2.1.4.2) and there argv[0]

must be a pointer to the starting character NTMBS that represents the name used to invoke programs or ""

. "

However, during the first C ++ standard, in 1998, no * nix world convention nor Windows convention was required to do this. Instead, the convention was to pass arguments with some locale-specific character encoding. In the Linux world, the transition to UTF-8 started almost immediately, while Windows did not, so as of 2012 in Windows, standard arguments are main

not sufficient to pass, for example. arbitrary file names and hellip;

Fortunately, on Windows, the command passed to a process and accessible via an API function GetCommandLine

is UTF-16 encoded, which means any filename (and indeed any text).

On the third hand, an API function that provides standard command line parsing CommandLineToArgvW

, at least one nonsense , and possibly more & hellip; Presumably the C ++ Unicode C ++ custom startup function wmain

has arguments provided by that function. So, for best results until this has been fixed, some correct syntax layout should be used in the home machine, for example. as shown in the program below (I just selected a special "personal tool" program I made last week, it looks like the Windows 2000 resource set: s timethis

):

// A program to measure the execution time of another program.
// Based vaguely on Jeffrey Richter "timep" program in
// the 2nd edition of "Win32 System Programming".
//
// Author: Alf P. Steinbach, 2012. License: Boost license 1.0.

#undef  UNICODE
#define UNICODE
#undef  STRICT
#define STRICT
#undef  NOMINMAX
#define NOMINMAX
#include <windows.h>
#include <shlwapi.h>            // PathGetCharType

#include    <assert.h>          // assert
#include    <functional>        // std::function
#include    <iomanip>           // set::setfill, std::setw
#include    <iostream>          // std::wcout, std::endl
#include    <sstream>           // std::wostringstream
#include    <stddef.h>          // ptrdiff_t
#include    <stdexcept>         // std::runtime_error, std::exception
#include    <stdint.h>          // int64_t
#include    <string>            // std::string
#include    <type_traits>       // std::is_fundamental
#include    <utility>           // std::move
using namespace std;

#if !defined( CPP_STATIC_ASSERT )
#   define CPP_STATIC_ASSERT( e )   static_assert( e, #e )
#endif

#if !defined( CPP_NORETURN )
#   define CPP_NORETURN             [[noreturn]]
#endif
// MSVC  workaround: "#define CPP_NORETURN __declspec( noreturn )"
// clang workaround: "#define CPP_NORETURN __attribute__(( noreturn ))"

namespace cpp {
    namespace detail {
        template< class Destination, class Source >
        class ImplicitCast
        {
        public:
            static Destination value( Source const v )
            {
                return static_cast<Destination>( v );
            }
        };

        template< class Source >
        class ImplicitCast< bool, Source >
        {
        public:
            static bool value( Source const v )
            {
                return !!v;     // Shuts up Visual C++ sillywarning about performance.
            }
        };
    };

    template< class Destination, class Source >
    Destination implicitCast( Source const v )
    {
        CPP_STATIC_ASSERT( is_fundamental< Destination >::value );
        CPP_STATIC_ASSERT( is_fundamental< Source >::value );

        return detail::ImplicitCast< Destination, Source >::value( v );
    }

    typedef ptrdiff_t       Size;

    inline bool hopefully( bool const c ) { return c; }

    inline CPP_NORETURN bool throwX( string const& s )
    {
        throw runtime_error( s );
    }

    inline CPP_NORETURN bool throwX( string const& s, exception const& reasonX )
    {
        throwX( s + "\n!Because - " + reasonX.what() );
    }

    class ScopeGuard
    {
    private:
        function<void()>  cleanup_;

        ScopeGuard( ScopeGuard const& );                // No such.
        ScopeGuard& operator=( ScopeGuard const& );     // No such.

    public:
        ~ScopeGuard() { cleanup_(); }

        ScopeGuard( function<void()> const cleanup )
            : cleanup_( cleanup )
        {}
    };

    class SubstringRef
    {
    private:
        wchar_t const*  start_;
        wchar_t const*  end_;

    public:
        Size length() const             { return end_ - start_; }
        wchar_t const* start() const    { return start_; }
        wchar_t const* end() const      { return end_; }

        SubstringRef( wchar_t const* start, wchar_t const* end )
            : start_( start )
            , end_( end )
        {}
    };

    inline void skipWhitespace( wchar_t const*& p )
    {
        while( *p != L'\0' && iswspace( *p ) ) { ++p; }
    }

    inline wchar_t const* theAfterWhitespacePart( wchar_t const* p )
    {
        skipWhitespace( p );
        return p;
    }

    inline void invert( bool& b ) { b = !b; }
}  // namespace cpp

namespace winapi {
    using cpp::hopefully;
    using cpp::invert;
    using cpp::Size;
    using cpp::skipWhitespace;
    using cpp::SubstringRef;
    using cpp::theAfterWhitespacePart;
    using cpp::throwX;

    namespace raw {
        typedef DWORD                   DWord;
        typedef FILETIME                FileTime;
        typedef HANDLE                  Handle;
        typedef PROCESS_INFORMATION     ProcessInformation;
        typedef SYSTEMTIME              SystemTime;
        typedef WORD                    Word;
    }  // namespace raw

    // The following logic is mainly a workaround for a bug in CommandLineToArgvW.
    // See [http://preview.tinyurl.com/CommandLineToArgvWBug].
    inline SubstringRef nextArgumentIn( wchar_t const* const commandLine )
    {
        wchar_t const*  p   = commandLine;

        skipWhitespace( p );
        wchar_t const* const    start   = p;

        bool isInQuotedPart = false;
        while( *p != L'\0' && (isInQuotedPart || !iswspace( *p ) ) )
        {
            if( *p == L'\"' ) { invert( isInQuotedPart ); }
            ++p;
        }
        return SubstringRef( start, p );
    }

    // This corresponds essentially to the argument of wWinMain(...).
    inline wchar_t const* commandLineArgPart()
    {
        SubstringRef const programSpec = nextArgumentIn( GetCommandLine() );
        return theAfterWhitespacePart( programSpec.end() );
    }

    class ProcessInfo
    {
    private:
        raw::ProcessInformation info_;

        ProcessInfo( ProcessInfo const& );              // No such.
        ProcessInfo& operator=( ProcessInfo const& );   // No such.

    public:
        raw::ProcessInformation& raw()      { return info_; }
        raw::Handle handle() const          { return info_.hProcess; }

        ~ProcessInfo()
        {
            ::CloseHandle( info_.hThread );
            ::CloseHandle( info_.hProcess );
        }

        ProcessInfo(): info_() {}

        ProcessInfo( ProcessInfo&& other )
            : info_( move( other.info_ ) )
        {
            other.info_ = raw::ProcessInformation();      // Zero.
        }
    };

    inline ProcessInfo createProcess( wchar_t const commandLine[] )
    {
        STARTUPINFO         startupInfo     = { sizeof( startupInfo ) };
        ProcessInfo         processInfo;
        wstring             mutableCommandLine( commandLine );

        mutableCommandLine += L'\0';
        GetStartupInfo( &startupInfo );
        bool const  creationSucceeded = !!CreateProcess (
            nullptr,                // LPCTSTR lpApplicationName,
            &mutableCommandLine[0], // LPTSTR lpCommandLine,
            nullptr,                // LPSECURITY_ATTRIBUTES lpProcessAttributes,
            nullptr,                // LPSECURITY_ATTRIBUTES lpThreadAttributes,
            true,                   // BOOL bInheritHandles,
            NORMAL_PRIORITY_CLASS,  // DWORD dwCreationFlags,
            nullptr,                // LPVOID lpEnvironment,
            nullptr,                // LPCTSTR lpCurrentDirectory,
            &startupInfo,           // LPSTARTUPINFO lpStartupInfo,
            &processInfo.raw()      // LPPROCESS_INFORMATION lpProcessInformation
            );
        hopefully( creationSucceeded )
            || throwX( "winapi::createProcess: CreateProcess failed" );
        return processInfo;
    }

    inline raw::Handle dup(
        raw::Handle const       h,
        raw::DWord const        desiredAccess,
        bool                    inheritable = false
        )
    {
        raw::Handle result  = 0;
        bool const wasDuplicated = !!DuplicateHandle(
            GetCurrentProcess(), h,
            GetCurrentProcess(), &result,
            desiredAccess,
            inheritable,
            0               // options
            );
        hopefully( wasDuplicated )
            || throwX( "winapi::dup: DuplicateHandle failed" );
        assert( result != 0 );
        return result;
    }

    inline int64_t mSecsFromRelative( raw::FileTime const t )
    {
        ULARGE_INTEGER  asLargeInt;

        asLargeInt.u.HighPart   = t.dwHighDateTime;
        asLargeInt.u.LowPart    = t.dwLowDateTime;

        return asLargeInt.QuadPart/10000;
    }

    SubstringRef filenamePart( SubstringRef const& path )
    {
        wchar_t const*  p     = path.end();

        while( p != path.start() && PathGetCharType( *p ) != GCT_SEPARATOR )
        {
            --p;
        }
        if( PathGetCharType( *p ) == GCT_SEPARATOR ) { ++p; }
        return SubstringRef( p, path.end() );
    }
}  // namespace winapi

winapi::ProcessInfo createProcess( wchar_t const commandLine[], char const errMsg[] )
{
    try{ return winapi::createProcess( commandLine ); }
    catch( exception const& x ) { cpp::throwX( errMsg, x ); }
}

winapi::raw::Handle run( wchar_t const commandLine[] )
{
    namespace raw = winapi::raw;
    using cpp::hopefully;
    using cpp::throwX;
    using winapi::dup;
    using winapi::ProcessInfo;

    static char const* const createErrMsg = "Failed to create process";
    ProcessInfo const process = createProcess( commandLine, createErrMsg );

    // Early handle duplication ensures that one has the required rights.
    raw::Handle const   accessibleHandle    =
        dup( process.handle(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE );

    raw::DWord const waitResult = WaitForSingleObject( process.handle(), INFINITE );
    hopefully( waitResult == WAIT_OBJECT_0 )
        || throwX( "Failed waiting for process termination." );

    return accessibleHandle;
}

class Interval
{
private:
    int     hours_;
    int     minutes_;
    int     seconds_;
    int     milliseconds_;

public:
    int msecs() const       { return milliseconds_; }
    int seconds() const     { return seconds_; }
    int minutes() const     { return minutes_; }
    int hours() const       { return hours_; }

    Interval( int msecs, int seconds = 0, int minutes = 0, int hours = 0 )
        : milliseconds_( msecs )
        , seconds_( seconds )
        , minutes_( minutes )
        , hours_( hours )
    {
        assert( unsigned( hours ) < 24 );
        assert( unsigned( minutes ) < 60 );
        assert( unsigned( seconds ) < 60 );
        assert( unsigned( msecs ) < 1000 );
    }

    static Interval fromMSecs( int msecs )
    {
        int const   totalSeconds    = msecs / 1000;
        int const   totalMinutes    = totalSeconds / 60;
        int const   totalHours      = totalMinutes / 24;

        return Interval(
            msecs % 1000, totalSeconds % 60, totalMinutes %60, totalHours
            );
    }
};

wostream& operator<<( wostream& stream, Interval const& t )
{
    wostringstream  formatter;

    formatter << setfill( L'0' );
    formatter
        << setw( 2 ) << t.hours() << ":"
        << setw( 2 ) << t.minutes() << ":"
        << setw( 2 ) << t.seconds() << "."
        << setw( 3 ) << t.msecs();
    return (stream << formatter.str());
}

string narrowStringFrom( cpp::SubstringRef const& s )
{
    return string( s.start(), s.end() );    // Non-ANSI characters => garbage.
}

void cppMain()
{
    namespace raw = winapi::raw;
    using cpp::hopefully;
    using cpp::implicitCast;
    using cpp::ScopeGuard;
    using cpp::SubstringRef;
    using cpp::throwX;
    using winapi::commandLineArgPart;
    using winapi::filenamePart;
    using winapi::mSecsFromRelative;
    using winapi::nextArgumentIn;

    SubstringRef const      programSpec         = nextArgumentIn( GetCommandLine() );
    SubstringRef const      programName         = filenamePart( programSpec );
    wchar_t const* const    otherCommandLine    = commandLineArgPart();

    hopefully( nextArgumentIn( otherCommandLine ).length() > 0 )
        || throwX( "Usage: " + narrowStringFrom( programName ) + " command" );

    raw::DWord const    startMSecs          = GetTickCount(); 
    raw::Handle const   finishedProcess     = run( otherCommandLine );
    raw::DWord const    endMSecs            = GetTickCount();
    raw::DWord const    realElapsedMSecs    = endMSecs - startMSecs;
    ScopeGuard const    closingHandle( [=]() { CloseHandle( finishedProcess ); } );

    Interval const      realElapsedTime = Interval::fromMSecs( realElapsedMSecs );

    static char const* const    commandLineLabel    = "Command line: ";
    static char const* const    rElapsedTimeLabel   = "External elapsed time:   ";
    static char const* const    pElapsedTimeLabel   = "In-process elapsed time: ";
    static char const* const    kernelTimeLabel     = "In-process kernel time:  ";
    static char const* const    userTimeLabel       = "In-process user time:    ";

    wclog << endl;
    wclog << commandLineLabel << "[" << otherCommandLine << "]" << endl;
    wclog << rElapsedTimeLabel << realElapsedTime << endl;

    raw::FileTime   creationTime;
    raw::FileTime   exitTime;
    raw::FileTime   kernelTime;
    raw::FileTime   userTime;
    bool const  timesWereObtained = !!GetProcessTimes(
        finishedProcess, &creationTime, &exitTime, &kernelTime, &userTime
        );
    hopefully( timesWereObtained )
        || throwX( "cppMain: GetProcessTimes failed" );

    int const   elapsedTimeMSecs= implicitCast<int>(
        mSecsFromRelative( exitTime ) - mSecsFromRelative( creationTime )
        );
    int const   kernelTimeMSecs = implicitCast<int>( mSecsFromRelative( kernelTime ) );
    int const   userTimeMSecs   = implicitCast<int>( mSecsFromRelative( userTime ) );

    wclog << pElapsedTimeLabel << Interval::fromMSecs( elapsedTimeMSecs ) << endl;
    wclog << kernelTimeLabel << Interval::fromMSecs( kernelTimeMSecs ) << endl;
    wclog << userTimeLabel << Interval::fromMSecs( userTimeMSecs ) << endl;
}

int main()
{
    try
    {
        cppMain();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        wcerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

      

+2


source


Instead of changing the subsystem mode depending on the build type, consider using AllocConsole to explicitly create a console for your process.

0


source







All Articles