with modifications on CreateProcess and management of CUI subsystem processes, it's handy to provide fine control to users when launching their CUI applications: - kind of windows (console) - which std handles - wait for child termination or not...
Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- programs/wineconsole/wineconsole.c | 118 ++++++++++++++++++++++--------- programs/wineconsole/wineconsole.man.in | 65 ++++++++++++++++- programs/wineconsole/wineconsole.rc | 20 +++++ programs/wineconsole/wineconsole_res.h | 3 + 4 files changed, 167 insertions(+), 39 deletions(-)
diff --git a/programs/wineconsole/wineconsole.c b/programs/wineconsole/wineconsole.c index c703127c238..71fcb1a50d8 100644 --- a/programs/wineconsole/wineconsole.c +++ b/programs/wineconsole/wineconsole.c @@ -33,6 +33,28 @@ WINE_DEFAULT_DEBUG_CHANNEL(console);
static const unsigned int EC_INTERNAL = 255; /* value of exit_code for internal errors */ +static FILE* freport; + +static void message( unsigned id, const WCHAR* pmt ) +{ + WCHAR format[1024], msg[1024]; + DWORD_PTR args[1]; + args[0] = (DWORD_PTR)pmt; + + if (LoadStringW( GetModuleHandleW( NULL ), id, format, ARRAY_SIZE(format) ) && + FormatMessageW( FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING, + format, 0, 0, msg, ARRAY_SIZE(msg), (va_list*)args )) + fwprintf( freport, L"%s\n", msg ); + else + ERR("Failed to format\n"); +} + +static void usage( LPCWSTR option ) +{ + if (option) message( IDS_CMD_UNKNOWN_OPTION, option ); + message( IDS_CMD_USAGE, NULL ); + exit( EC_INTERNAL ); +}
/*********************************************************************** * build_command_line @@ -123,44 +145,72 @@ int wmain( int argc, WCHAR *argv[] ) { STARTUPINFOW startup = { sizeof(startup) }; PROCESS_INFORMATION info; - WCHAR *cmd; - DWORD exit_code; - - cmd = argc > 1 ? build_command_line( &argv[1] ) : wcsdup( L"cmd.exe" ); - - FreeConsole(); /* make sure we're not connected to inherited console */ - if (!AllocConsole()) - { - ERR( "failed to allocate console: %lu\n", GetLastError() ); - return 1; - } + DWORD cpflags = CREATE_NEW_CONSOLE; + BOOL inherit = FALSE, dowait = TRUE; + WCHAR *cmdline; + int i;
- startup.dwFlags = STARTF_USESTDHANDLES; - startup.hStdInput = CreateFileW( L"CONIN$", GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, 0, 0 ); - startup.hStdOutput = CreateFileW( L"CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, 0, 0 ); - startup.hStdError = startup.hStdOutput; - - if (!CreateProcessW( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info )) + freport = stderr; + for (i = 1; i < argc; i++) { - WCHAR format[256], *buf; - INPUT_RECORD ir; - DWORD len; - exit_code = GetLastError(); - WARN( "CreateProcess failed: %lu\n", exit_code ); - LoadStringW( GetModuleHandleW( NULL ), IDS_CMD_LAUNCH_FAILED, format, ARRAY_SIZE(format) ); - len = wcslen( format ) + wcslen( cmd ); - if ((buf = malloc( len * sizeof(WCHAR) ))) + if (argv[i][0] != '-') break; + if (argv[i][1] == '-' && !argv[i][2]) {i++; break;} + if ( !wcscmp(argv[i], L"--detached")) cpflags = DETACHED_PROCESS; + else if (!wcscmp(argv[i], L"--console")) cpflags = CREATE_NEW_CONSOLE; + else if (!wcscmp(argv[i], L"--headless")) cpflags = CREATE_NO_WINDOW; + else if (!wcscmp(argv[i], L"--console-std")) inherit = FALSE; + else if (!wcscmp(argv[i], L"--inherit-std")) inherit = TRUE; + else if (!wcscmp(argv[i], L"--dontwait")) dowait = FALSE; + else if (!wcscmp(argv[i], L"--wait")) dowait = TRUE; + else if (!wcsncmp(argv[i], L"--report=", 9) && argv[i][9]) { - swprintf( buf, len, format, cmd ); - WriteConsoleW( startup.hStdOutput, buf, wcslen(buf), &len, NULL); - while (ReadConsoleInputW( startup.hStdInput, &ir, 1, &len ) && ir.EventType == MOUSE_EVENT); + FILE* f = _wfopen(argv[i] + 9, L"w+"); + if (!f) + { + message( IDS_CMD_REPORT_OPEN, argv[i] + 9 ); + exit( EC_INTERNAL ); + } + freport = f; } - return exit_code; + else if (!wcscmp(argv[i], L"--help") || !wcscmp(argv[i], L"-?")) + usage( NULL ); + else usage( argv[i] ); + } + cmdline = i < argc ? build_command_line( &argv[i] ) : wcsdup( L"cmd.exe" ); + if (inherit) + { + startup.dwFlags |= STARTF_USESTDHANDLES; + startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); + startup.hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); + startup.hStdError = GetStdHandle( STD_ERROR_HANDLE ); + } + if (!CreateProcessW( NULL, cmdline, NULL, NULL, TRUE, cpflags, NULL, NULL, &startup, &info )) + { + WARN( "CreateProcess failed (%ls): %lu\n", cmdline, GetLastError() ); + message( IDS_CMD_LAUNCH_FAILED, cmdline ); + return EC_INTERNAL; } - CloseHandle( info.hThread ); - WaitForSingleObject( info.hProcess, INFINITE ); - return GetExitCodeProcess( info.hProcess, &exit_code ) ? exit_code : GetLastError(); + if (dowait) + { + DWORD exit_code; + HANDLE hJob; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobinfo; + + /* set parent and child into the same job: if parent gets killed (unix kill), + * child will be terminated as well. + */ + SetConsoleCtrlHandler( NULL, TRUE ); + hJob = CreateJobObjectW( NULL, NULL ); + memset( &jobinfo, 0, sizeof(jobinfo) ); + jobinfo.BasicLimitInformation.LimitFlags = + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | + JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; + SetInformationJobObject( hJob, JobObjectExtendedLimitInformation, &jobinfo, sizeof(jobinfo) ); + AssignProcessToJobObject( hJob, info.hProcess ); + + WaitForSingleObject( info.hProcess, INFINITE ); + return GetExitCodeProcess( info.hProcess, &exit_code ) ? exit_code : EC_INTERNAL; + } + return 0; } diff --git a/programs/wineconsole/wineconsole.man.in b/programs/wineconsole/wineconsole.man.in index dac4e62f321..8a71be60564 100644 --- a/programs/wineconsole/wineconsole.man.in +++ b/programs/wineconsole/wineconsole.man.in @@ -1,13 +1,70 @@ .TH WINECONSOLE 1 "November 2010" "@PACKAGE_STRING@" "Wine Programs" .SH NAME -wineconsole - The Wine console +wineconsole - A Wine helper for managing the console used for running applications .SH SYNOPSIS .B wineconsole -.RI [ command "] " +.RI "[ " options " ] [ " command " ] " .SH DESCRIPTION .B wineconsole -is the Wine console manager, used to run console commands and applications. It allows running the -console in a newly made window. + +allows to have fine grain management over console and standard I/O streams used when running an application under Wine. +.SH OPTIONS + +.IP \fB--console\fR +\fBwineconsole\fR will execute the command in a newly created window. + +This is the default when none of the \fB--detached\fR, \fB--headless\fR or \fB--console\fR options is provided. +.IP \fB--detached\fR +\fBwineconsole\fR will execute the \fIcommand\fR without being attached to any console. +.IP \fB--headless\fR +\fBwineconsole\fR will execute the \fIcommand\fR attached to an invisible console. + +.IP \fB--console-std\fR +The \fIcommand\fR's standard I/O streams will be mapped to the console ones. + +This is the default when neither \fB--console-std\fR nor \fB--inherit-std\fR is provided. +.IP \fB--inherit-std\fR +The \fIcommand\fR's standard I/O streams will be mapped to the standard streams \fBwineconsole\fR is run with. + +.IP \fB--wait\fR +Wait for \fIcommand\fR to terminate before \fBwineconsole\fR terminates itself. + +.IP \fB--dontwait\fR +Terminate after starting \fIcommand\fR without waiting for it to terminate. + +.IP \fB--report=filename\fR +Write detailed error information into filename. Can be useful either when \fBwineconsole\fR isn't run attached to a console, or when its output stream is already redirected. + +.IP \fIcommand\fR +The name of the executable to run, potentially followed by its arguments, with same meaning and syntax than using \fBwine\fR. + +If this part is omitted, than \fBcmd.exe\fR is run without arguments. + +When an error occurs inside \fBwineconsole\fR, 255 is returned as exit status. +Upon success, in \fB--dontwait\fR mode the exit status is 0, while in \fB--wait\fR mode, the exit status of the \fBwineconsole\fR is the exit status for the \fIcommand\fR. + +.SH NOTES +Consoles are only of interest when the \fIcommand\fR executable belongs to the CUI subsystem. + +Using \fBwineconsole\fR overrides default Wine console creation when invoked from regular shell or script. + +.SH EXAMPLES + +.IP ./wine +wineconsole [ \fIcommand\fR ] + +Run \fIcommand\fR in a newly created standalone console. + +.IP ./wine +wineconsole --headless --inherit-std [ \fIcommand\fR ] >& log + +Run \fIcommand\fR, in an invisible console, having its standard output and error streams redirected into file 'log'. + +.IP ./wine +wineconsole --detached [ \fIcommand\fR ] + +Run \fIcommand\fR, without any console (meaning no possible input, and all \fIcommand\fR's information sent to standard output and error streams are lost). + .SH BUGS Bugs can be reported on the .UR https://bugs.winehq.org diff --git a/programs/wineconsole/wineconsole.rc b/programs/wineconsole/wineconsole.rc index 6a61dd31ede..8229cc1fdf8 100644 --- a/programs/wineconsole/wineconsole.rc +++ b/programs/wineconsole/wineconsole.rc @@ -25,6 +25,24 @@ LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT STRINGTABLE BEGIN
-IDS_CMD_LAUNCH_FAILED "wineconsole: Starting program %s failed.\nThe command is invalid.\n" +IDS_CMD_UNKNOWN_OPTION "Unknown option %1" +IDS_CMD_USAGE "Usage: wineconsole [options] [command]\n\ +\n\ +options:\n\ + --detached start [command] not being attached to any console\n\ + --console start [command] being attached to a newly created console (this is the default)\n\ + --headless start [command] being attached to a newly created yet not visible console\n\ +\n\ + --console-std ensures standard I/O streams of command are mapped to the console (this is the default)\n\ + --inherit-std ensures standard I/O streams of command are mapped to the ones which wineconsole is run with\n\ +\n\ + --report=filename write error reports into filename instead of stderr\n\ +\n\ + --wait wait for [command] to terminate before returning\n\ + --dontwait terminate as soon as [command] has been started\n\ +\n\ + [command]: executable (and optional arguments) to run" +IDS_CMD_REPORT_OPEN "Couldn't open file '%1' for reporting." +IDS_CMD_LAUNCH_FAILED "wineconsole: Starting program %1 failed.\nThe command is invalid."
END diff --git a/programs/wineconsole/wineconsole_res.h b/programs/wineconsole/wineconsole_res.h index cca60ac5857..213b353cc5e 100644 --- a/programs/wineconsole/wineconsole_res.h +++ b/programs/wineconsole/wineconsole_res.h @@ -22,4 +22,7 @@ #include <winuser.h> #include <commctrl.h>
+#define IDS_CMD_UNKNOWN_OPTION 0x302 +#define IDS_CMD_USAGE 0x303 #define IDS_CMD_LAUNCH_FAILED 0x304 +#define IDS_CMD_REPORT_OPEN 0x305