supersedes 227544
As described in bugzilla ticket, when calling CreateProcess: - with "default" console inheritance (no CREATE_NEW_CONSOLE nor DETACHED_PROCESS flags), - when parent is not attached to a console - and when the child's subsystem is CUI , native behaves as if CREATE_NEW_CONSOLE was passed.
builtin doesn't...
this serie: - adds a test case for showing the CUI/GUI discrepancies - fixes kernelbase accordingly - but also requires to fix all usage of CreateProcess in Wine Code that would now generate the creation of a console + a couple of programs moved into the GUI subsystem (to avoid console creation on invocation) + when looking to the other usage of CreateProcess, I only convinced myself to adapt one of them
V1 => V2: - don't mess up with the number of patches in the serie - no longer modify makedep for creating cui vs gui test executable, but copy current *_test.exe executable and adapt its subsystem to the need
V2 => V3: - removed the hacks in wow64 redirect, and let programs/uninstaller and programs/wusa belong to the GUI subsystem instead - force console creation for wine initial process when a) none of the unix fd are attached to a TTY, b) the initial process is in CUI subsystem
V3 => V4: - included Jacek's comments: + removed unneeded casts + move CreateProcess change in programs/services into a separate patch + I didn't change programs/start as you suggested. Testing on W10 shows that 'start /b cui.exe' from a cmd prompt just reuses the current console.
V4 => V5: - removed patches applied in V4 - added tweaks in start.exe not so create a console when detached
And Marvin will generate errors for Chinese and Japanese VM in console testing that are independant of this serie.
Signed-off-by: Eric Pouech eric.pouech@gmail.com ---
Eric Pouech (4): dlls/kernel32/tests: add some console tests about creating cui vs gui processes programs/services: start services detached from console programs/start: force no console creation when using start /exec dlls/kernelbase: handle corner case in CreateProcess
dlls/kernel32/tests/console.c | 85 +++++++++++++++++++++++++++++++++++ dlls/kernelbase/process.c | 6 ++- programs/services/services.c | 2 +- programs/start/start.c | 7 ++- 4 files changed, 96 insertions(+), 4 deletions(-)
adding helper to copy argv[0] into an executable with CUI subsystem
Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- dlls/kernel32/tests/console.c | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+)
diff --git a/dlls/kernel32/tests/console.c b/dlls/kernel32/tests/console.c index b31febe0d56..3622687c6a4 100644 --- a/dlls/kernel32/tests/console.c +++ b/dlls/kernel32/tests/console.c @@ -4662,6 +4662,85 @@ static void test_pseudo_console(void) pClosePseudoConsole(pseudo_console); }
+/* copy an executable, but changing its subsystem */ +static void copy_change_subsystem(const char* in, const char* out, DWORD subsyst) +{ + BOOL ret; + HANDLE hFile, hMap; + void* mapping; + IMAGE_NT_HEADERS *nthdr; + + ret = CopyFileA(in, out, FALSE); + ok(ret, "Failed to copy executable %s in %s (%lu)\n", in, out, GetLastError()); + + hFile = CreateFileA(out, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok(hFile != INVALID_HANDLE_VALUE, "Couldn't open file %s (%lu)\n", out, GetLastError()); + hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); + ok(hMap != NULL, "Couldn't create map (%lu)\n", GetLastError()); + mapping = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); + ok(mapping != NULL, "Couldn't map (%lu)\n", GetLastError()); + nthdr = RtlImageNtHeader(mapping); + ok(nthdr != NULL, "Cannot get NT headers out of %s\n", out); + if (nthdr) nthdr->OptionalHeader.Subsystem = subsyst; + ret = UnmapViewOfFile(mapping); + ok(ret, "Couldn't unmap (%lu)\n", GetLastError()); + CloseHandle(hMap); + CloseHandle(hFile); +} + +static BOOL check_whether_child_attached(const char* exec, DWORD flags) +{ + STARTUPINFOA si = { sizeof(si) }; + PROCESS_INFORMATION info; + char buf[MAX_PATH]; + DWORD exit_code; + BOOL res; + DWORD ret; + + sprintf(buf, ""%s" console check_console", exec); + res = CreateProcessA(NULL, buf, NULL, NULL, FALSE, flags, NULL, NULL, &si, &info); + ok(res, "CreateProcess failed: %lu %s\n", GetLastError(), buf); + CloseHandle(info.hThread); + ret = WaitForSingleObject(info.hProcess, 30000); + ok(ret == WAIT_OBJECT_0, "Could not wait for the child process: %ld le=%lu\n", + ret, GetLastError()); + ret = GetExitCodeProcess(info.hProcess, &exit_code); + ok(ret && exit_code <= 255, "Couldn't get exit_code\n"); + CloseHandle(info.hProcess); + return exit_code != 0; +} + +static void test_CreateProcessCUI(void) +{ + char guiexec[MAX_PATH]; + char cuiexec[MAX_PATH]; + char **argv; + BOOL res; + + winetest_get_mainargs(&argv); + GetTempPathA(ARRAY_SIZE(guiexec), guiexec); + strcat(guiexec, "console_gui.exe"); + copy_change_subsystem(argv[0], guiexec, IMAGE_SUBSYSTEM_WINDOWS_GUI); + GetTempPathA(ARRAY_SIZE(cuiexec), cuiexec); + strcat(cuiexec, "console_cui.exe"); + copy_change_subsystem(argv[0], cuiexec, IMAGE_SUBSYSTEM_WINDOWS_CUI); + + FreeConsole(); + + res = check_whether_child_attached(guiexec, DETACHED_PROCESS); + ok(!res, "Don't expecting child to be attached to a console\n"); + res = check_whether_child_attached(guiexec, 0); + ok(!res, "Don't expecting child to be attached to a console\n"); + res = check_whether_child_attached(cuiexec, DETACHED_PROCESS); + ok(!res, "Don't expecting child to be attached to a console\n"); + res = check_whether_child_attached(cuiexec, 0); + todo_wine ok(res, "Expecting child to be attached to a console\n"); + + DeleteFileA(guiexec); + DeleteFileA(cuiexec); +} + START_TEST(console) { HANDLE hConIn, hConOut, revert_output = NULL, unbound_output; @@ -4690,6 +4769,11 @@ START_TEST(console) return; }
+ if (argc == 3 && !strcmp(argv[2], "check_console")) + { + ExitProcess(GetConsoleCP() != 0); + } + test_current = argc >= 3 && !strcmp(argv[2], "--current"); using_pseudo_console = argc >= 3 && !strcmp(argv[2], "--pseudo-console");
@@ -4880,6 +4964,7 @@ START_TEST(console) test_AttachConsole(hConOut); test_AllocConsole(); test_FreeConsole(); + test_CreateProcessCUI(); } else if (revert_output) SetConsoleActiveScreenBuffer(revert_output);
Signed-off-by: Jacek Caban jacek@codeweavers.com
---
This test will not work on non-PE builds, but I guess that's fine. We already have a number of tests assuming that we can create a process using argv[0] (so it's missing .so suffix), which doesn't work any more. This test makes more assumptions than that, but the outcome is the same: failing tests.
Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- programs/services/services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/services/services.c b/programs/services/services.c index 87ab319367b..2edc02d300c 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -1094,7 +1094,7 @@ found: process->use_count++; service_unlock(service_entry);
- r = CreateProcessW(NULL, path, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, environment, NULL, &si, &pi); + r = CreateProcessW(NULL, path, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS, environment, NULL, &si, &pi); HeapFree(GetProcessHeap(), 0, path); if (!r) {
Signed-off-by: Jacek Caban jacek@codeweavers.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- programs/start/start.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/programs/start/start.c b/programs/start/start.c index 998c439423e..7252d54e046 100644 --- a/programs/start/start.c +++ b/programs/start/start.c @@ -516,8 +516,11 @@ int __cdecl wmain (int argc, WCHAR *argv[]) break; } else if (is_option(argv[i], L"/exec")) { - creation_flags = 0; - sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_NO_UI; + /* If start.exe isn't attached to a console, force that no console would be created. + * This is needed when target process belongs to CUI subsystem. + */ + creation_flags = GetConsoleCP() == 0 ? DETACHED_PROCESS : 0; + sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_NO_UI; i++; break; }
Signed-off-by: Jacek Caban jacek@codeweavers.com
in CreateProcess, if: - parent isn't attached to a console - CreateProcess's flag isn't set with DETACHED_PROCESS nor CREATE_NEW_CONSOLE - child is a CUI program then a console must be allocated for the child
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52048 Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- dlls/kernel32/tests/console.c | 2 +- dlls/kernelbase/process.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/tests/console.c b/dlls/kernel32/tests/console.c index 3622687c6a4..98692760acf 100644 --- a/dlls/kernel32/tests/console.c +++ b/dlls/kernel32/tests/console.c @@ -4735,7 +4735,7 @@ static void test_CreateProcessCUI(void) res = check_whether_child_attached(cuiexec, DETACHED_PROCESS); ok(!res, "Don't expecting child to be attached to a console\n"); res = check_whether_child_attached(cuiexec, 0); - todo_wine ok(res, "Expecting child to be attached to a console\n"); + ok(res, "Expecting child to be attached to a console\n");
DeleteFileA(guiexec); DeleteFileA(cuiexec); diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 35381f409e9..1cecbce9321 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -191,7 +191,11 @@ static RTL_USER_PROCESS_PARAMETERS *create_process_params( const WCHAR *filename
if (flags & CREATE_NEW_PROCESS_GROUP) params->ConsoleFlags = 1; if (flags & CREATE_NEW_CONSOLE) params->ConsoleHandle = CONSOLE_HANDLE_ALLOC; - else if (!(flags & DETACHED_PROCESS)) params->ConsoleHandle = NtCurrentTeb()->Peb->ProcessParameters->ConsoleHandle; + else if (!(flags & DETACHED_PROCESS)) + { + params->ConsoleHandle = NtCurrentTeb()->Peb->ProcessParameters->ConsoleHandle; + if (!params->ConsoleHandle) params->ConsoleHandle = CONSOLE_HANDLE_ALLOC; + }
if (startup->dwFlags & STARTF_USESTDHANDLES) {
Signed-off-by: Jacek Caban jacek@codeweavers.com