Add new version of WINAPI code for attaching console
This commit is contained in:
parent
3a6107b267
commit
04e6ebcc12
5 changed files with 271 additions and 226 deletions
|
@ -354,7 +354,7 @@ set(video_SRCS
|
|||
source_group(video FILES ${video_SRCS})
|
||||
|
||||
set(win32_SRCS
|
||||
src/win32/attachconsole.cpp
|
||||
src/win32/SetupConsole_win32.cpp
|
||||
src/win32/stratagus.rc
|
||||
)
|
||||
source_group(win32 FILES ${win32_SRCS})
|
||||
|
@ -584,6 +584,7 @@ set(stratagus_generic_HDRS
|
|||
src/include/viewport.h
|
||||
src/include/wav.h
|
||||
src/include/widgets.h
|
||||
src/include/SetupConsole_win32.h
|
||||
)
|
||||
|
||||
|
||||
|
|
32
src/include/SetupConsole_win32.h
Normal file
32
src/include/SetupConsole_win32.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (C) 2015 Pali Rohár <pali.rohar@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SETUPCONSOLE_WIN32_H
|
||||
#define SETUPCONSOLE_WIN32_H
|
||||
|
||||
/* Returns true if standard output points to console */
|
||||
bool IsStdoutConsole();
|
||||
|
||||
/* Returns true if standard error points to console */
|
||||
bool IsStderrConsole();
|
||||
|
||||
/* Attach console from parent process and setup console standard input, output and error */
|
||||
/* Must be called before any console I/O, ideally as first call in main() */
|
||||
void SetupConsole();
|
||||
|
||||
#endif
|
|
@ -227,6 +227,10 @@ extern void beos_init(int argc, char **argv);
|
|||
#define REDIRECT_OUTPUT
|
||||
#endif
|
||||
|
||||
#if defined(USE_WIN32) && ! defined(REDIRECT_OUTPUT)
|
||||
#include "SetupConsole_win32.h"
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Variables
|
||||
----------------------------------------------------------------------------*/
|
||||
|
@ -696,6 +700,9 @@ int stratagusMain(int argc, char **argv)
|
|||
#endif
|
||||
#ifdef USE_WIN32
|
||||
SetUnhandledExceptionFilter(CreateDumpFile);
|
||||
#endif
|
||||
#if defined(USE_WIN32) && ! defined(REDIRECT_OUTPUT)
|
||||
SetupConsole();
|
||||
#endif
|
||||
// Setup some defaults.
|
||||
#ifndef MAC_BUNDLE
|
||||
|
|
230
src/win32/SetupConsole_win32.cpp
Normal file
230
src/win32/SetupConsole_win32.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
Copyright (C) 2015 Pali Rohár <pali.rohar@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
/* _WIN32_WINNT must be set to at least 0x0501 */
|
||||
#ifdef _WIN32_WINNT
|
||||
#if _WIN32_WINNT < 0x0501
|
||||
#undef _WIN32_WINNT
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#include "SetupConsole_win32.h"
|
||||
|
||||
/*
|
||||
* Microsoft systems were and always are one big mess where even standard output does not work :-(
|
||||
* There are at least 4 different I/O API levels and each WINAPI function initilize correctly just one...
|
||||
* For example standard output information is stored in: DWORD STD_OUTPUT_HANDLE, std::istream std::cout, FILE* stdout, int fd (value 1)
|
||||
* This code tries to use console window of parent process and properly initialize all I/O API levels and make sure they are not out of sync
|
||||
*/
|
||||
|
||||
bool IsHandleConsole(HANDLE handle) {
|
||||
|
||||
if (!handle || handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
/* GetConsoleMode() returns false on non console handlers */
|
||||
DWORD mode;
|
||||
return GetConsoleMode(handle, &mode);
|
||||
|
||||
}
|
||||
|
||||
bool IsStdoutConsole() {
|
||||
|
||||
return IsHandleConsole(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
|
||||
}
|
||||
|
||||
bool IsStderrConsole() {
|
||||
|
||||
return IsHandleConsole(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
}
|
||||
|
||||
bool IsHandleValid(HANDLE handle) {
|
||||
|
||||
if (!handle || handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
DWORD type = GetFileType(handle);
|
||||
DWORD error = GetLastError();
|
||||
|
||||
/* When error occure GetFileType() returns FILE_TYPE_UNKNOWN and GetLastError() returns error code */
|
||||
return (type != FILE_TYPE_UNKNOWN || error == NO_ERROR);
|
||||
|
||||
}
|
||||
|
||||
bool IsStreamValid(FILE *stream) {
|
||||
|
||||
return _fileno(stream) >= 0;
|
||||
|
||||
}
|
||||
|
||||
bool IsFdValid(int fd) {
|
||||
|
||||
/* Function _isatty returns non zero value when parameter is a terminal, console, printer, or serial port */
|
||||
/* When parameter is bad file descriptor, then zero is returned and errno is set to EBADF */
|
||||
errno = 0;
|
||||
return (_isatty(fd) != 0 || errno != EBADF);
|
||||
|
||||
}
|
||||
|
||||
bool SetStreamAndFdFromStdConsole(FILE *stream, int fd, DWORD std, bool input) {
|
||||
|
||||
/* Retrieves a handle to the standard device of active console screen */
|
||||
HANDLE handle = GetStdHandle(std);
|
||||
if (!handle || handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
/* Allocates new file descriptor and associates it with handle */
|
||||
int fd_new = _open_osfhandle((intptr_t)handle, _O_TEXT | (input ? _O_RDONLY : _O_APPEND));
|
||||
if (fd_new < 0)
|
||||
return false;
|
||||
|
||||
/* Retrieves existing file descriptor from FILE* stream */
|
||||
int fd_old = _fileno(stream);
|
||||
if (fd_old >= 0 && fd_old != fd_new) {
|
||||
/* Reassigns new file descriptor to existing one from FILE* stream */
|
||||
if (_dup2(fd_new, fd_old) < 0) {
|
||||
_close(fd_new);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd_new != fd) {
|
||||
/* Reassigns new file descriptor to one provided by function arg */
|
||||
if (_dup2(fd_new, fd) < 0) {
|
||||
_close(fd_new);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
if (fd_old < 0 || fd_old != fd_new)
|
||||
_close(fd_new);
|
||||
|
||||
/* Allocates new FILE* stream and associates it with file descriptor */
|
||||
FILE *stream_new = _fdopen(fd, (input ? "r" : "a"));
|
||||
if (!stream_new) {
|
||||
_close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Close old FILE* stream */
|
||||
fclose(stream);
|
||||
|
||||
/* Set new FILE* stream */
|
||||
*stream = *stream_new;
|
||||
|
||||
/* Disable bufferring */
|
||||
setvbuf(stream, NULL, _IONBF, 0);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void SetupConsole() {
|
||||
|
||||
/* Get current handlers */
|
||||
HANDLE inputHandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
HANDLE errorHandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
bool stdin_ok = IsHandleValid(inputHandle);
|
||||
bool stdout_ok = IsHandleValid(outputHandle);
|
||||
bool stderr_ok = IsHandleValid(errorHandle);
|
||||
|
||||
/* If stdin, stdout and stderr is valid we do not need to do anything */
|
||||
if (stdin_ok && stdout_ok && stderr_ok)
|
||||
return;
|
||||
|
||||
/* Attaches the calling process to the console of parent process */
|
||||
/* After successfull call STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE are changed to new console screen */
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
return;
|
||||
|
||||
/* If console handlers were valid before AttachConsole() call, set handlers back (we do not want to overwrite them) */
|
||||
if (stdin_ok)
|
||||
SetStdHandle(STD_INPUT_HANDLE, inputHandle);
|
||||
if (stdout_ok)
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, outputHandle);
|
||||
if (stderr_ok)
|
||||
SetStdHandle(STD_ERROR_HANDLE, errorHandle);
|
||||
|
||||
/* And now STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE are valid and set correctly */
|
||||
|
||||
stdin_ok = (IsStreamValid(stdin) && IsFdValid(0));
|
||||
stdout_ok = (IsStreamValid(stdout) && IsFdValid(1));
|
||||
stderr_ok = (IsStreamValid(stderr) && IsFdValid(2));
|
||||
|
||||
bool stdin_console = false;
|
||||
bool stdout_console = false;
|
||||
bool stderr_console = false;
|
||||
|
||||
/* Try to set standard C streams (stdin/stdout/stderr) and fds (0/1/2) from active console screen (STD_INPUT_HANDLE/STD_OUTPUT_HANDLE/STD_ERROR_HANDLE) */
|
||||
if (!stdin_ok)
|
||||
stdin_console = SetStreamAndFdFromStdConsole(stdin, 0, STD_INPUT_HANDLE, true);
|
||||
if (!stdout_ok)
|
||||
stdout_console = SetStreamAndFdFromStdConsole(stdout, 1, STD_OUTPUT_HANDLE, false);
|
||||
if (!stderr_ok)
|
||||
stderr_console = SetStreamAndFdFromStdConsole(stderr, 2, STD_ERROR_HANDLE, false);
|
||||
|
||||
/* If everything failed then detach console and try to restore original console handlers... we cannot do more */
|
||||
if (!stdin_console && !stdout_console && !stderr_console) {
|
||||
FreeConsole();
|
||||
SetStdHandle(STD_INPUT_HANDLE, inputHandle);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, outputHandle);
|
||||
SetStdHandle(STD_ERROR_HANDLE, errorHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* Clear C++ std buffers */
|
||||
if (stdin_console)
|
||||
std::cin.clear();
|
||||
if (stdout_console)
|
||||
std::cout.clear();
|
||||
if (stderr_console)
|
||||
std::cerr.clear();
|
||||
|
||||
/* Synchronise C streams with C++ iostream */
|
||||
std::ios::sync_with_stdio();
|
||||
#endif
|
||||
|
||||
/* Write two empty lines to prevent overwriting command prompt in cmd.exe */
|
||||
if (stdout_console) {
|
||||
fprintf(stdout, "\n\n");
|
||||
fflush(stdout);
|
||||
} else if (stderr_console) {
|
||||
fprintf(stderr, "\n\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
attachconsole.cpp - WINAPI AttachConsole
|
||||
Copyright (C) 2009-2011 Pali Rohár <pali.rohar@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#if ( defined(WIN32) || defined(_MSC_VER) ) && ( defined(NO_STDIO_REDIRECT) || ! defined(REDIRECT_OUTPUT) )
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32_WINNT_WIN2K
|
||||
#define _WIN32_WINNT_WIN2K 0x0500
|
||||
#endif
|
||||
|
||||
static int fixmode = 0;
|
||||
|
||||
static HINSTANCE lib_kernel32 = NULL;
|
||||
static HINSTANCE lib_ntdll = NULL;
|
||||
|
||||
typedef int FAR WINAPI proto_AttachConsole(int);
|
||||
typedef int FAR WINAPI proto_NtQueryObject(HANDLE, int, void *, unsigned long int, unsigned long int *);
|
||||
|
||||
static proto_AttachConsole *func_AttachConsole = NULL;
|
||||
static proto_NtQueryObject *func_NtQueryObject = NULL;
|
||||
|
||||
/// Check if HANDLE is attached to console
|
||||
static int WINAPI_CheckIfConsoleHandle(HANDLE handle)
|
||||
{
|
||||
|
||||
wchar_t filename[MAX_PATH];
|
||||
unsigned long int length = 0;
|
||||
|
||||
// Try to get filename of HANDLE
|
||||
if (func_NtQueryObject) {
|
||||
func_NtQueryObject(handle, 1, filename, MAX_PATH, &length);
|
||||
}
|
||||
|
||||
// Filename start at position 8
|
||||
if (length > 8) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Try to reopen FILE* from WINAPI HANDLE
|
||||
static void WINAPI_ReopenFileFromHandle(HANDLE handle, FILE *file, const char *mode)
|
||||
{
|
||||
|
||||
int fd;
|
||||
FILE *newfile;
|
||||
|
||||
if (! handle || handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get file descriptor from HANDLE
|
||||
fd = _open_osfhandle((intptr_t)handle, O_TEXT);
|
||||
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get C structure FILE* from file descriptior
|
||||
newfile = _fdopen(fd, mode);
|
||||
|
||||
if (! newfile) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close current file
|
||||
fclose(file);
|
||||
|
||||
// Set new file from HANDLE
|
||||
*file = *newfile;
|
||||
|
||||
setvbuf(file, NULL, _IONBF, 0);
|
||||
|
||||
// If stdout/stderr write 2 empty lines to cmd console
|
||||
if (! fixmode && strcmp(mode, "w") == 0) {
|
||||
|
||||
fprintf(file, "\n\n");
|
||||
fixmode = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Try to set std HANDLE from FILE*
|
||||
static void WINAPI_SetStdHandleFromFile(int type, FILE *file)
|
||||
{
|
||||
|
||||
int fd;
|
||||
HANDLE handle;
|
||||
|
||||
fd = _fileno(file);
|
||||
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
handle = (HANDLE)_get_osfhandle(fd);
|
||||
|
||||
if (! handle || handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetStdHandle(type, handle);
|
||||
|
||||
}
|
||||
|
||||
/// Try attach console of parent process for std input/output in Windows NT, 2000, XP or new
|
||||
static void WINAPI_AttachConsole(void)
|
||||
{
|
||||
|
||||
OSVERSIONINFO osvi;
|
||||
int hasVersion;
|
||||
int version;
|
||||
int attached;
|
||||
int reopen_stdin;
|
||||
int reopen_stdout;
|
||||
int reopen_stderr;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
|
||||
hasVersion = GetVersionEx(&osvi);
|
||||
|
||||
if (! hasVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
version = (osvi.dwMajorVersion << 8) | osvi.dwMinorVersion;
|
||||
|
||||
// We need Windows 2000 or new
|
||||
if (version < _WIN32_WINNT_WIN2K) {
|
||||
return;
|
||||
}
|
||||
|
||||
lib_kernel32 = LoadLibrary("kernel32.dll");
|
||||
|
||||
if (! lib_kernel32) {
|
||||
return;
|
||||
}
|
||||
|
||||
func_AttachConsole = (proto_AttachConsole *)GetProcAddress(lib_kernel32, "AttachConsole");
|
||||
|
||||
if (! func_AttachConsole) {
|
||||
return;
|
||||
}
|
||||
|
||||
lib_ntdll = LoadLibrary("ntdll.dll");
|
||||
|
||||
if (lib_ntdll) {
|
||||
func_NtQueryObject = (proto_NtQueryObject *)GetProcAddress(lib_ntdll, "NtQueryObject");
|
||||
}
|
||||
|
||||
// Ignore if HANDLE is not attached console
|
||||
reopen_stdin = WINAPI_CheckIfConsoleHandle(GetStdHandle(STD_INPUT_HANDLE));
|
||||
reopen_stdout = WINAPI_CheckIfConsoleHandle(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
reopen_stderr = WINAPI_CheckIfConsoleHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
attached = func_AttachConsole(-1);
|
||||
|
||||
if (! attached) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reopen_stdin) {
|
||||
WINAPI_ReopenFileFromHandle(GetStdHandle(STD_INPUT_HANDLE), stdin, "r");
|
||||
} else {
|
||||
WINAPI_SetStdHandleFromFile(STD_INPUT_HANDLE, stdin);
|
||||
}
|
||||
|
||||
if (reopen_stdout) {
|
||||
WINAPI_ReopenFileFromHandle(GetStdHandle(STD_OUTPUT_HANDLE), stdout, "w");
|
||||
} else {
|
||||
WINAPI_SetStdHandleFromFile(STD_OUTPUT_HANDLE, stdout);
|
||||
}
|
||||
|
||||
if (reopen_stderr) {
|
||||
WINAPI_ReopenFileFromHandle(GetStdHandle(STD_ERROR_HANDLE), stderr, "w");
|
||||
} else {
|
||||
WINAPI_SetStdHandleFromFile(STD_ERROR_HANDLE, stderr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
std::cin.clear();
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
std::ios::sync_with_stdio();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// This section set that WINAPI_AttachConsole() will be called at application startup before main()
|
||||
#ifdef _MSC_VER
|
||||
#pragma section(".CRT$XCU", long, read)
|
||||
__declspec(allocate(".CRT$XCU")) void (*initialize)(void) = WINAPI_AttachConsole;
|
||||
#else
|
||||
__attribute__((constructor)) static void initialize(void) { WINAPI_AttachConsole(); }
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue