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.
source to share
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 );
}
source to share
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.
source to share
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 inargv[0]
viaargv[argc-1]
as pointers to the starting characters of null-terminated multibyte strings (NTMBS) (17.5.2.1.4.2) and thereargv[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;
}
source to share
Instead of changing the subsystem mode depending on the build type, consider using AllocConsole to explicitly create a console for your process.
source to share