ChangeLog
- Adapted kernel32 so that it no longer (directly) manages console handles as wineserver handles
+ console input handle object is no longer waitable (input record
synchronisation is now implemented as a simple semaphore), and
remove FD_TYPE_CONSOLE from fd types in wineserver
+ console handles now always have their two lower bit set so one can
distinguish a console handle from a kernel object handle
+ implemented some undocumented kernel32 console related APIs
(CloseConsoleHandle, GetConsoleInputWaitHandle, OpenConsoleW,
VerifyConsoleIoHandle, DuplicateConsoleHandle)
+ allowed a few kernel32 APIs to take console pseudo-handles
(FlushFileBuffer, GetFileType, WaitFor*Object*,
+ simplified the console inheritance at process creation
+ in console tests, no longer create a console if one already exists
Notes:
- some ugly include directives (../kernel/kernel_private.h) were needed because some functions are not yet in dlls/kernel. We'll be able to get rid of those ugly directives as soon as files/functions are moved
A+ -- Eric Pouech
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel26/console.c dlls/kernel/console.c --- dlls/kernel26/console.c 2003-05-13 19:29:57.000000000 +0200 +++ dlls/kernel/console.c 2003-06-18 20:57:25.000000000 +0200 @@ -48,12 +48,14 @@ #include "wine/debug.h" #include "excpt.h" #include "console_private.h" +#include "kernel_private.h" WINE_DEFAULT_DEBUG_CHANNEL(console); static UINT console_input_codepage; static UINT console_output_codepage; + /* map input records to ASCII */ static void input_records_WtoA( INPUT_RECORD *buffer, int count ) { @@ -195,11 +197,12 @@ if (written) *written = 0; SERVER_START_REQ( write_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); wine_server_add_data( req, buffer, count * sizeof(INPUT_RECORD) ); if ((ret = !wine_server_call_err( req ))) { if (written) *written = reply->written; + ReleaseSemaphore( GetConsoleInputWaitHandle(), reply->written, NULL ); } } SERVER_END_REQ; @@ -266,7 +269,7 @@ { SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; @@ -348,7 +351,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -412,7 +415,7 @@ SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -453,7 +456,7 @@ SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -507,7 +510,7 @@ SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -536,7 +539,7 @@ SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_ATTR; @@ -596,7 +599,7 @@ { SERVER_START_REQ( read_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = region->Left; req->y = region->Top + y; req->mode = CHAR_INFO_MODE_TEXTATTR; @@ -667,7 +670,7 @@ BOOL ret; SERVER_START_REQ( read_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); req->flush = FALSE; wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) ); if ((ret = !wine_server_call_err( req ))) @@ -688,7 +691,7 @@ BOOL ret; SERVER_START_REQ( read_console_input ) { - req->handle = handle; + req->handle = console_handle_unmap(handle); req->flush = FALSE; if ((ret = !wine_server_call_err( req ))) { @@ -700,24 +703,50 @@ } -/*********************************************************************** - * FlushConsoleInputBuffer (KERNEL32.@) +/****************************************************************************** + * read_console_input + * + * Helper function for ReadConsole, ReadConsoleInput and FlushConsoleInputBuffer + * + * Returns + * 0 for error, 1 for no INPUT_RECORD ready, 2 with INPUT_RECORD ready */ -BOOL WINAPI FlushConsoleInputBuffer( HANDLE handle ) +enum read_console_input_return {rci_error = 0, rci_timeout = 1, rci_gotone = 2}; +static enum read_console_input_return read_console_input(HANDLE handle, LPINPUT_RECORD ir, DWORD timeout) { - BOOL ret; + enum read_console_input_return ret; + + if (WaitForSingleObject(GetConsoleInputWaitHandle(), timeout) != WAIT_OBJECT_0) + return rci_timeout; SERVER_START_REQ( read_console_input ) { - req->handle = handle; - req->flush = TRUE; - ret = !wine_server_call_err( req ); + req->handle = console_handle_unmap(handle); + req->flush = TRUE; + wine_server_set_reply( req, ir, sizeof(INPUT_RECORD) ); + if (wine_server_call_err( req ) || !reply->read) ret = rci_error; + else ret = rci_gotone; } SERVER_END_REQ; + return ret; } /*********************************************************************** + * FlushConsoleInputBuffer (KERNEL32.@) + */ +BOOL WINAPI FlushConsoleInputBuffer( HANDLE handle ) +{ + enum read_console_input_return last; + INPUT_RECORD ir; + + while ((last = read_console_input(handle, &ir, 0)) == rci_gotone); + + return last == rci_timeout; +} + + +/*********************************************************************** * SetConsoleTitleA (KERNEL32.@) */ BOOL WINAPI SetConsoleTitleA( LPCSTR title ) @@ -996,30 +1025,6 @@ } -/****************************************************************************** - * read_console_input - * - * Helper function for ReadConsole, ReadConsoleInput and PeekConsoleInput - */ -static BOOL read_console_input(HANDLE handle, LPINPUT_RECORD buffer, DWORD count, - LPDWORD pRead, BOOL flush) -{ - BOOL ret; - unsigned read = 0; - - SERVER_START_REQ( read_console_input ) - { - req->handle = handle; - req->flush = flush; - wine_server_set_reply( req, buffer, count * sizeof(INPUT_RECORD) ); - if ((ret = !wine_server_call_err( req ))) read = reply->read; - } - SERVER_END_REQ; - if (pRead) *pRead = read; - return ret; -} - - /*********************************************************************** * ReadConsoleA (KERNEL32.@) */ @@ -1072,23 +1077,26 @@ else { INPUT_RECORD ir; - DWORD count; + DWORD timeout = INFINITE; /* FIXME: should we read at least 1 char? The SDK does not say */ /* wait for at least one available input record (it doesn't mean we'll have - * chars stored in xbuf... + * chars stored in xbuf...) */ - WaitForSingleObject(hConsoleInput, INFINITE); - for (charsread = 0; charsread < nNumberOfCharsToRead;) + charsread = 0; + do { - if (!read_console_input(hConsoleInput, &ir, 1, &count, TRUE)) return FALSE; - if (count && ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && + if (read_console_input(hConsoleInput, &ir, timeout) != rci_gotone) break; + timeout = 0; + if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown && ir.Event.KeyEvent.uChar.UnicodeChar && !(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) { xbuf[charsread++] = ir.Event.KeyEvent.uChar.UnicodeChar; } - } + } while (charsread < nNumberOfCharsToRead); + /* nothing has been read */ + if (timeout == INFINITE) return FALSE; } if (lpNumberOfCharsRead) *lpNumberOfCharsRead = charsread; @@ -1103,7 +1111,8 @@ BOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput, LPINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead) { - DWORD count; + DWORD idx = 0; + DWORD timeout = INFINITE; if (!nLength) { @@ -1112,17 +1121,12 @@ } /* loop until we get at least one event */ - for (;;) - { - WaitForSingleObject(hConsoleInput, INFINITE); - if (!read_console_input(hConsoleInput, lpBuffer, nLength, &count, TRUE)) - return FALSE; - if (count) - { - if (lpNumberOfEventsRead) *lpNumberOfEventsRead = count; - return TRUE; - } - } + while (read_console_input(hConsoleInput, &lpBuffer[idx], timeout) == rci_gotone && + ++idx < nLength) + timeout = 0; + + if (lpNumberOfEventsRead) *lpNumberOfEventsRead = idx; + return idx != 0; } @@ -1152,7 +1156,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x = coord.X; req->y = coord.Y; req->mode = CHAR_INFO_MODE_TEXT; @@ -1480,7 +1484,7 @@ SERVER_START_REQ(get_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); if ((ret = !wine_server_call_err( req ))) { csbi->dwSize.X = reply->width; @@ -1536,7 +1540,7 @@ SERVER_START_REQ(get_console_mode) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); ret = !wine_server_call_err( req ); if (ret && mode) *mode = reply->mode; } @@ -1569,7 +1573,7 @@ SERVER_START_REQ(set_console_mode) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); req->mode = mode; ret = !wine_server_call_err( req ); } @@ -1598,7 +1602,7 @@ SERVER_START_REQ( write_console_output ) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->x = pos->X; req->y = pos->Y; req->mode = CHAR_INFO_MODE_TEXTSTDATTR; @@ -1828,7 +1832,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); req->cursor_x = pos.X; req->cursor_y = pos.Y; req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_POS; @@ -1888,7 +1892,7 @@ SERVER_START_REQ(get_console_output_info) { - req->handle = hcon; + req->handle = console_handle_unmap(hcon); ret = !wine_server_call_err( req ); if (ret && cinfo) { @@ -1917,7 +1921,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->cursor_size = cinfo->dwSize; req->cursor_visible = cinfo->bVisible; req->mask = SET_CONSOLE_OUTPUT_INFO_CURSOR_GEOM; @@ -1956,7 +1960,7 @@ } SERVER_START_REQ(set_console_output_info) { - req->handle = hCon; + req->handle = console_handle_unmap(hCon); req->win_left = p.Left; req->win_top = p.Top; req->win_right = p.Right; @@ -1986,7 +1990,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->attr = wAttr; req->mask = SET_CONSOLE_OUTPUT_INFO_ATTR; ret = !wine_server_call_err( req ); @@ -2013,7 +2017,7 @@ SERVER_START_REQ(set_console_output_info) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->width = dwSize.X; req->height = dwSize.Y; req->mask = SET_CONSOLE_OUTPUT_INFO_SIZE; @@ -2051,7 +2055,7 @@ { SERVER_START_REQ( fill_console_output ) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->mode = CHAR_INFO_MODE_TEXTATTR; req->x = i; req->y = j; @@ -2129,7 +2133,7 @@ /* step 3: transfer the bits */ SERVER_START_REQ(move_console_output) { - req->handle = hConsoleOutput; + req->handle = console_handle_unmap(hConsoleOutput); req->x_src = lpScrollRect->Left; req->y_src = lpScrollRect->Top; req->x_dst = dst.Left; @@ -2260,7 +2264,7 @@ unsigned ret = FALSE; SERVER_START_REQ(get_console_input_info) { - req->handle = hConIn; + req->handle = console_handle_unmap(hConIn); if ((ret = !wine_server_call_err( req ))) *mode = reply->edition_mode; } diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel26/editline.c dlls/kernel/editline.c --- dlls/kernel26/editline.c 2003-02-19 21:10:45.000000000 +0100 +++ dlls/kernel/editline.c 2003-05-23 22:46:44.000000000 +0200 @@ -88,29 +88,10 @@ static BOOL WCEL_Get(WCEL_Context* ctx, INPUT_RECORD* ir) { - DWORD retv; - - for (;;) - { - /* data available ? */ - if (ReadConsoleInputW(ctx->hConIn, ir, 1, &retv) && retv == 1) - return TRUE; - /* then wait... */ - switch (WaitForSingleObject(ctx->hConIn, INFINITE)) - { - case WAIT_OBJECT_0: - break; - default: - /* we have checked that hConIn was a console handle (could be sb) */ - ERR("Shouldn't happen\n"); - /* fall thru */ - case WAIT_ABANDONED: - case WAIT_TIMEOUT: - ctx->error = 1; - ERR("hmm bad situation\n"); - return FALSE; - } - } + if (ReadConsoleInputW(ctx->hConIn, ir, 1, NULL)) return TRUE; + ERR("hmm bad situation\n"); + ctx->error = 1; + return FALSE; } static inline void WCEL_Beep(WCEL_Context* ctx) @@ -633,7 +614,7 @@ static void WCEL_RepeatCount(WCEL_Context* ctx) { #if 0 -/* FIXME: wait untill all console code is in kernel32 */ +/* FIXME: wait until all console code is in kernel32 */ INPUT_RECORD ir; unsigned repeat = 0; @@ -822,11 +803,12 @@ while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) { - if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue; + if (ir.EventType != KEY_EVENT) continue; TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08lx\n", ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); + if (!ir.Event.KeyEvent.bKeyDown) continue; /* EPP WCEL_Dump(&ctx, "before func"); */ ofs = ctx.ofs; diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel26/kernel32.spec dlls/kernel/kernel32.spec --- dlls/kernel26/kernel32.spec 2003-06-14 09:30:10.000000000 +0200 +++ dlls/kernel/kernel32.spec 2003-06-18 20:28:01.000000000 +0200 @@ -913,7 +913,7 @@ @ stub AddConsoleAliasW @ stub BaseAttachCompleteThunk @ stub BasepDebugDump -@ stub CloseConsoleHandle +@ stdcall CloseConsoleHandle(long) @ stub CmdBatNotification @ stub ConsoleMenuControl @ stub ConsoleSubst @@ -941,7 +941,7 @@ @ stub GetConsoleFontInfo @ stub GetConsoleFontSize @ stub GetConsoleHardwareState -@ stub GetConsoleInputWaitHandle +@ stdcall GetConsoleInputWaitHandle() @ stub GetCurrentConsoleFont @ stub GetNextVDMCommand @ stub GetNumberOfConsoleFonts @@ -953,7 +953,7 @@ @ stub HeapUsage @ stub InvalidateConsoleDIBits @ stdcall IsDebuggerPresent() -@ stub OpenConsoleW +@ stdcall OpenConsoleW(wstr long ptr long) @ stub QueryWin31IniFilesMappedToRegistry @ stub RegisterConsoleVDM @ stub RegisterWaitForInputIdle @@ -976,7 +976,7 @@ @ stub TrimVirtualBuffer @ stub VDMConsoleOperation @ stub VDMOperationStarted -@ stub VerifyConsoleIoHandle +@ stdcall VerifyConsoleIoHandle(long) @ stub VirtualBufferExceptionHandler @ stub WriteConsoleInputVDMA @ stub WriteConsoleInputVDMW @@ -991,7 +991,7 @@ @ stdcall CreateWaitableTimerA(ptr long str) @ stdcall CreateWaitableTimerW(ptr long wstr) @ stdcall DeleteFiber(ptr) -@ stub DuplicateConsoleHandle +@ stdcall DuplicateConsoleHandle(long long long long) @ stdcall FindFirstFileExA(str long ptr long ptr long) @ stdcall FindFirstFileExW(wstr long ptr long ptr long) @ stub GetConsoleInputExeNameA diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel26/kernel_private.h dlls/kernel/kernel_private.h --- dlls/kernel26/kernel_private.h 1970-01-01 01:00:00.000000000 +0100 +++ dlls/kernel/kernel_private.h 2003-06-18 21:33:39.000000000 +0200 @@ -0,0 +1,47 @@ +/* + * Kernel32 undocumented and private functions definition + * + * Copyright 2003 Eric Pouech + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_KERNEL_PRIVATE_H +#define __WINE_KERNEL_PRIVATE_H + +HANDLE WINAPI OpenConsoleW(LPCWSTR, DWORD, LPSECURITY_ATTRIBUTES, DWORD); +BOOL WINAPI VerifyConsoleIoHandle(HANDLE); +HANDLE WINAPI DuplicateConsoleHandle(HANDLE, DWORD, BOOL, DWORD); +BOOL WINAPI CloseConsoleHandle(HANDLE handle); +HANDLE WINAPI GetConsoleInputWaitHandle(void); + +static inline BOOL is_console_handle(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE && ((DWORD)h & 3) == 3; +} + +/* map a real wineserver handle onto a kernel32 console handle */ +static inline HANDLE console_handle_map(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE ? (HANDLE)((DWORD)h ^ 3) : INVALID_HANDLE_VALUE; +} + +/* map a kernel32 console handle onto a real wineserver handle */ +static inline HANDLE console_handle_unmap(HANDLE h) +{ + return h != INVALID_HANDLE_VALUE ? (HANDLE)((DWORD)h ^ 3) : INVALID_HANDLE_VALUE; +} + +#endif diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel26/tests/console.c dlls/kernel/tests/console.c --- dlls/kernel26/tests/console.c 2003-05-13 06:46:54.000000000 +0200 +++ dlls/kernel/tests/console.c 2003-06-18 21:12:55.000000000 +0200 @@ -538,7 +538,7 @@ START_TEST(console) { - HANDLE hCon; + HANDLE hConIn, hConOut; BOOL ret; CONSOLE_SCREEN_BUFFER_INFO sbi; @@ -548,16 +548,29 @@ * Another solution would be to rerun the test under wineconsole with * the curses backend */ - FreeConsole(); - AllocConsole(); - hCon = GetStdHandle(STD_OUTPUT_HANDLE); - ok(ret = GetConsoleScreenBufferInfo(hCon, &sbi), "Getting sb info"); + + hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + + /* first, we need to be sure we're attached to a console */ + if (hConIn == INVALID_HANDLE_VALUE || hConOut == INVALID_HANDLE_VALUE) + { + /* we're not attached to a console, let's do it */ + AllocConsole(); + hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + } + /* now verify everything's ok */ + ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn"); + ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut"); + + ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info"); if (!ret) return; /* Non interactive tests */ - testCursor(hCon, sbi.dwSize); + testCursor(hConOut, sbi.dwSize); /* will test wrapped (on/off) & processed (on/off) strings output */ - testWrite(hCon, sbi.dwSize); + testWrite(hConOut, sbi.dwSize); /* will test line scrolling at the bottom of the screen */ /* testBottomScroll(); */ /* will test all the scrolling operations */ diff -u -N -r -x '*~' -x '.#*' -x CVS files26/file.c files/file.c --- files26/file.c 2003-06-14 09:30:11.000000000 +0200 +++ files/file.c 2003-06-18 21:11:05.000000000 +0200 @@ -70,6 +70,7 @@ #include "heap.h" #include "msdos.h" #include "wincon.h" +#include "../kernel/kernel_private.h" /* FIXME: to be changed when moved to kernel32 */ #include "smb.h" #include "wine/unicode.h" @@ -351,30 +352,124 @@ return FILE_GetUnixHandleType( handle, access, NULL, NULL ); } -/************************************************************************* - * FILE_OpenConsole +/****************************************************************** + * OpenConsoleW (KERNEL32.@) * - * Open a handle to the current process console. - * Returns 0 on failure. + * Undocumented + * Open a handle to the current process console. + * Returns INVALID_HANDLE_VALUE on failure. */ -static HANDLE FILE_OpenConsole( BOOL output, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa ) +HANDLE WINAPI OpenConsoleW(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa, + DWORD creation) { + static const WCHAR coninW[] = {'C','O','N','I','N','$',0}; + static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0}; + BOOL output; HANDLE ret; + if (strcmpW(coninW, name) == 0) + output = FALSE; + else if (strcmpW(conoutW, name) == 0) + output = TRUE; + else + { + SetLastError(ERROR_INVALID_NAME); + return INVALID_HANDLE_VALUE; + } + if (creation != OPEN_EXISTING) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + SERVER_START_REQ( open_console ) { req->from = output; req->access = access; - req->share = sharing; + req->share = FILE_SHARE_READ | FILE_SHARE_WRITE; req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle); SetLastError(0); wine_server_call_err( req ); ret = reply->handle; } SERVER_END_REQ; + return ret ? console_handle_map(ret) : INVALID_HANDLE_VALUE; +} + +/****************************************************************** + * VerifyConsoleIoHandle (KERNEL32.@) + * + * Undocumented + */ +BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle) +{ + BOOL ret; + + if (!is_console_handle(handle)) return FALSE; + SERVER_START_REQ(get_console_mode) + { + req->handle = console_handle_unmap(handle); + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; return ret; } +/****************************************************************** + * DuplicateConsoleHandle (KERNEL32.@) + * + * Undocumented + */ +HANDLE WINAPI DuplicateConsoleHandle(HANDLE handle, DWORD access, BOOL inherit, + DWORD options) +{ + HANDLE ret; + + if (!is_console_handle(handle) || + !DuplicateHandle(GetCurrentProcess(), console_handle_unmap(handle), + GetCurrentProcess(), &ret, access, inherit, options)) + return INVALID_HANDLE_VALUE; + return console_handle_map(ret); +} + +/****************************************************************** + * CloseConsoleHandle (KERNEL32.@) + * + * Undocumented + */ +BOOL WINAPI CloseConsoleHandle(HANDLE handle) +{ + if (!is_console_handle(handle)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + return CloseHandle(console_handle_unmap(handle)); +} + +/****************************************************************** + * GetConsoleInputWaitHandle (KERNEL32.@) + * + * Undocumented + */ +HANDLE WINAPI GetConsoleInputWaitHandle(void) +{ + static HANDLE console_wait_event; + + /* FIXME: this is not thread safe */ + if (!console_wait_event) + { + SERVER_START_REQ(get_console_wait_event) + { + if (!wine_server_call_err( req )) console_wait_event = reply->handle; + } + SERVER_END_REQ; + } + return console_wait_event; +} +/* end of FIXME */ + + /* FIXME: those routines defined as pointers are needed, because this file is * currently compiled into NTDLL whereas it belongs to kernel32. * this shall go away once all the DLL separation process is done @@ -630,14 +725,9 @@ } /* Open a console for CONIN$ or CONOUT$ */ - if (!strcmpiW(filename, coninW)) + if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW)) { - ret = FILE_OpenConsole( FALSE, access, sharing, sa ); - goto done; - } - if (!strcmpiW(filename, conoutW)) - { - ret = FILE_OpenConsole( TRUE, access, sharing, sa ); + ret = OpenConsoleW(filename, access, sa, creation); goto done; } @@ -1807,6 +1897,9 @@ if (bytesRead) *bytesRead = 0; /* Do this before anything else */ if (!bytesToRead) return TRUE; + if (is_console_handle(hFile)) + return FILE_ReadConsole(hFile, buffer, bytesToRead, bytesRead, NULL); + unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_READ, &type, &flags ); if (flags & FD_FLAG_OVERLAPPED) @@ -1845,9 +1938,6 @@ case FD_TYPE_SMB: return SMB_ReadFile(hFile, buffer, bytesToRead, bytesRead, NULL); - case FD_TYPE_CONSOLE: - return FILE_ReadConsole(hFile, buffer, bytesToRead, bytesRead, NULL); - case FD_TYPE_DEFAULT: /* normal unix files */ if (unix_handle == -1) return FALSE; @@ -2031,6 +2121,9 @@ if (bytesWritten) *bytesWritten = 0; /* Do this before anything else */ if (!bytesToWrite) return TRUE; + if (is_console_handle(hFile)) + return FILE_WriteConsole(hFile, buffer, bytesToWrite, bytesWritten, NULL); + unix_handle = FILE_GetUnixHandleType( hFile, GENERIC_WRITE, &type, &flags ); if (flags & FD_FLAG_OVERLAPPED) @@ -2062,11 +2155,6 @@ switch(type) { - case FD_TYPE_CONSOLE: - TRACE("%p %s %ld %p %p\n", hFile, debugstr_an(buffer, bytesToWrite), bytesToWrite, - bytesWritten, overlapped ); - return FILE_WriteConsole(hFile, buffer, bytesToWrite, bytesWritten, NULL); - case FD_TYPE_DEFAULT: if (unix_handle == -1) return FALSE; @@ -2366,6 +2454,13 @@ NTSTATUS nts; IO_STATUS_BLOCK ioblk; + if (is_console_handle( hFile )) + { + /* this will fail (as expected) for an output handle */ + /* FIXME: wait until FlushFileBuffers is moved to dll/kernel */ + /* return FlushConsoleInputBuffer( hFile ); */ + return TRUE; + } nts = NtFlushBuffersFile( hFile, &ioblk ); if (nts != STATUS_SUCCESS) { @@ -2473,6 +2568,10 @@ DWORD WINAPI GetFileType( HANDLE hFile ) { DWORD ret = FILE_TYPE_UNKNOWN; + + if (is_console_handle( hFile )) + return FILE_TYPE_CHAR; + SERVER_START_REQ( get_file_info ) { req->handle = hFile; diff -u -N -r -x '*~' -x '.#*' -x CVS programs/wineconsole26/wineconsole.c programs/wineconsole/wineconsole.c --- programs/wineconsole26/wineconsole.c 2003-06-18 08:01:57.000000000 +0200 +++ programs/wineconsole/wineconsole.c 2003-06-18 20:28:27.000000000 +0200 @@ -535,6 +535,7 @@ DWORD ret; struct config_data cfg; STARTUPINFOW si; + HANDLE sem; data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data)); if (!data) return 0; @@ -563,6 +564,7 @@ /* should always be defined */ } + sem = CreateSemaphore(NULL, 0, 65536, NULL); /* the handles here are created without the whistles and bells required by console * (mainly because wineconsole doesn't need it) * - they are not inheritable @@ -570,9 +572,11 @@ */ SERVER_START_REQ(alloc_console) { - req->access = GENERIC_READ | GENERIC_WRITE; - req->inherit = FALSE; - req->pid = pid; + req->access = GENERIC_READ | GENERIC_WRITE; + req->inherit = FALSE; + req->pid = pid; + req->wait_event = sem; + ret = !wine_server_call_err( req ); data->hConIn = (HANDLE)reply->handle_in; data->hSynchro = (HANDLE)reply->event; diff -u -N -r -x '*~' -x '.#*' -x CVS scheduler26/handle.c scheduler/handle.c --- scheduler26/handle.c 2003-05-13 22:49:11.000000000 +0200 +++ scheduler/handle.c 2003-05-23 22:03:16.000000000 +0200 @@ -32,6 +32,7 @@ #include "wine/server.h" #include "winerror.h" #include "wine/debug.h" +#include "../kernel/kernel_private.h" /* FIXME: to be changed when moving file to dlls/kernel */ WINE_DEFAULT_DEBUG_CHANNEL(win32); @@ -49,6 +50,9 @@ (handle == (HANDLE)STD_ERROR_HANDLE)) handle = GetStdHandle( (DWORD)handle ); + if (is_console_handle(handle)) + return CloseConsoleHandle(handle); + status = NtClose( handle ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; @@ -118,8 +122,22 @@ HANDLE dest_process, HANDLE *dest, DWORD access, BOOL inherit, DWORD options ) { - NTSTATUS status = NtDuplicateObject( source_process, source, dest_process, dest, - access, inherit ? OBJ_INHERIT : 0, options ); + NTSTATUS status; + + if (is_console_handle(source)) + { + /* FIXME: this test is not sufficient, we need to test process ids, not handles */ + if (source_process != dest_process || + source_process != GetCurrentProcess()) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + *dest = DuplicateConsoleHandle( source, access, inherit, options ); + return (*dest != INVALID_HANDLE_VALUE); + } + status = NtDuplicateObject( source_process, source, dest_process, dest, + access, inherit ? OBJ_INHERIT : 0, options ); if (status) SetLastError( RtlNtStatusToDosError(status) ); return !status; } diff -u -N -r -x '*~' -x '.#*' -x CVS scheduler26/process.c scheduler/process.c --- scheduler26/process.c 2003-06-18 21:51:18.000000000 +0200 +++ scheduler/process.c 2003-06-18 21:34:51.000000000 +0200 @@ -47,6 +47,7 @@ #include "wine/server.h" #include "options.h" #include "wine/debug.h" +#include "../kernel/kernel_private.h" WINE_DEFAULT_DEBUG_CHANNEL(process); WINE_DECLARE_DEBUG_CHANNEL(server); @@ -330,19 +329,25 @@ /* Create the process heap */ current_process.heap = HeapCreate( HEAP_GROWABLE, 0, 0 ); - if (main_create_flags == 0 && - process_pmts.hStdInput == 0 && - process_pmts.hStdOutput == 0 && - process_pmts.hStdError == 0) - { - /* This is wine specific: - * no parent, and no new console requested, create a simple console with bare handles to - * unix stdio input & output streams (aka simple console) + if (info_size == 0) + { + /* This is wine specific: we have no parent (we're started from unix) + * so, create a simple console with bare handles to unix stdio + * input & output streams (aka simple console) */ wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE, TRUE, &process_pmts.hStdInput ); wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdOutput ); wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdError ); } + else + { + if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdInput))) + process_pmts.hStdInput = console_handle_map(process_pmts.hStdInput); + if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdOutput))) + process_pmts.hStdOutput = console_handle_map(process_pmts.hStdOutput); + if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdError))) + process_pmts.hStdError = console_handle_map(process_pmts.hStdError); + } /* Now we can use the pthreads routines */ PTHREAD_init_done(); @@ -955,6 +958,20 @@ req->hstderr = GetStdHandle( STD_ERROR_HANDLE ); } + if ((flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)) != 0) + { + /* this is temporary (for console handles). We have no way to control that the handle is invalid in child process otherwise */ + if (is_console_handle(req->hstdin)) req->hstdin = INVALID_HANDLE_VALUE; + if (is_console_handle(req->hstdout)) req->hstdout = INVALID_HANDLE_VALUE; + if (is_console_handle(req->hstderr)) req->hstderr = INVALID_HANDLE_VALUE; + } + else + { + if (is_console_handle(req->hstdin)) req->hstdin = console_handle_unmap(req->hstdin); + if (is_console_handle(req->hstdout)) req->hstdout = console_handle_unmap(req->hstdout); + if (is_console_handle(req->hstderr)) req->hstderr = console_handle_unmap(req->hstderr); + } + if (GetLongPathNameA( filename, buf, MAX_PATH )) nameptr = buf; else diff -u -N -r -x '*~' -x '.#*' -x CVS scheduler26/synchro.c scheduler/synchro.c --- scheduler26/synchro.c 2003-03-31 21:34:14.000000000 +0200 +++ scheduler/synchro.c 2003-05-23 22:11:09.000000000 +0200 @@ -22,6 +22,7 @@ #include "winbase.h" #include "winternl.h" +#include "../kernel/kernel_private.h" /* FIXME: to be changed when moving file to dlls/kernel */ /*********************************************************************** @@ -80,10 +81,39 @@ BOOL alertable ) { NTSTATUS status; + HANDLE hloc[MAXIMUM_WAIT_OBJECTS]; + int i; + + if (count >= MAXIMUM_WAIT_OBJECTS) + { + SetLastError(ERROR_INVALID_PARAMETER); + return WAIT_FAILED; + } + for (i = 0; i < count; i++) + { + if ((handles[i] == (HANDLE)STD_INPUT_HANDLE) || + (handles[i] == (HANDLE)STD_OUTPUT_HANDLE) || + (handles[i] == (HANDLE)STD_ERROR_HANDLE)) + hloc[i] = GetStdHandle( (DWORD)handles[i] ); + else + hloc[i] = handles[i]; + + /* yes, even screen buffer console handles are waitable, and are + * handled as a handle to the console itself !! + */ + if (is_console_handle(hloc[i])) + { + if (!VerifyConsoleIoHandle(hloc[i])) + { + return FALSE; + } + hloc[i] = GetConsoleInputWaitHandle(); + } + } if (timeout == INFINITE) { - status = NtWaitForMultipleObjects( count, handles, wait_all, alertable, NULL ); + status = NtWaitForMultipleObjects( count, hloc, wait_all, alertable, NULL ); } else { @@ -91,7 +121,7 @@ time.QuadPart = timeout * (ULONGLONG)10000; time.QuadPart = -time.QuadPart; - status = NtWaitForMultipleObjects( count, handles, wait_all, alertable, &time ); + status = NtWaitForMultipleObjects( count, hloc, wait_all, alertable, &time ); } if (HIWORD(status)) /* is it an error code? */ diff -u -N -r -x '*~' -x '.#*' -x CVS server26/console.c server/console.c --- server26/console.c 2003-05-10 22:40:04.000000000 +0200 +++ server/console.c 2003-06-18 20:47:15.000000000 +0200 @@ -35,18 +35,16 @@ #include "unicode.h" #include "console.h" - static void console_input_dump( struct object *obj, int verbose ); static void console_input_destroy( struct object *obj ); -static int console_input_signaled( struct object *obj, struct thread *thread ); static const struct object_ops console_input_ops = { sizeof(struct console_input), /* size */ console_input_dump, /* dump */ - add_queue, /* add_queue */ - remove_queue, /* remove_queue */ - console_input_signaled, /* signaled */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ no_satisfied, /* satisfied */ no_get_fd, /* get_fd */ console_input_destroy /* destroy */ @@ -205,7 +203,7 @@ return evt; } -static struct object *create_console_input( struct thread* renderer ) +static struct object *create_console_input( struct thread* renderer, struct object* wait_obj ) { struct console_input *console_input; @@ -224,6 +222,7 @@ console_input->history_index = 0; console_input->history_mode = 0; console_input->edition_mode = 0; + console_input->wait_obj = wait_obj; if (!console_input->history || !console_input->evt) { @@ -368,11 +367,6 @@ } } -int is_console_object( struct object *obj ) -{ - return (obj->ops == &console_input_ops || obj->ops == &screen_buffer_ops); -} - static struct console_input* console_input_get( obj_handle_t handle, unsigned access ) { struct console_input* console = 0; @@ -390,14 +384,6 @@ return console; } -/* check if a console input is signaled: yes if non read input records */ -static int console_input_signaled( struct object *obj, struct thread *thread ) -{ - struct console_input *console = (struct console_input *)obj; - assert( obj->ops == &console_input_ops ); - return console->recnum ? 1 : 0; -} - struct console_signal_info { struct console_input *console; process_id_t group; @@ -957,6 +943,7 @@ release_object( console_in->evt ); console_in->evt = NULL; + release_object( console_in->wait_obj ); for (i = 0; i < console_in->history_size; i++) if (console_in->history[i]) free( console_in->history[i] ); @@ -1222,6 +1209,7 @@ struct process *process; struct process *renderer = current->process; struct console_input *console; + struct object *wait_event; process = (req->pid) ? get_process_from_id( req->pid ) : (struct process *)grab_object( renderer->parent ); @@ -1234,8 +1222,13 @@ set_error( STATUS_ACCESS_DENIED ); goto the_end; } - - if ((console = (struct console_input*)create_console_input( current ))) + wait_event = get_handle_obj( renderer, req->wait_event, 0, NULL); + if (!wait_event) + { + set_error( STATUS_INVALID_PARAMETER ); + goto the_end; + } + if ((console = (struct console_input*)create_console_input( current, wait_event ))) { if ((in = alloc_handle( renderer, console, req->access, req->inherit ))) { @@ -1512,7 +1505,36 @@ group = req->group_id ? req->group_id : current->process->group_id; if (!group) - set_error( STATUS_INVALID_PARAMETER); + set_error( STATUS_INVALID_PARAMETER ); else propagate_console_signal( current->process->console, req->signal, group ); } + +/* get console which renderer is 'current' */ +static int cgwe_enum( struct process* process, void* user) +{ + if (process->console && process->console->renderer == current) + { + *(struct console_input**)user = process->console; + return 1; + } + return 0; +} + +DECL_HANDLER(get_console_wait_event) +{ + struct console_input* console = NULL; + + if (current->process->console && current->process->console->renderer) + console = (struct console_input*)grab_object( (struct object*)current->process->console ); + else enum_processes(cgwe_enum, &console); + + if (console) + { + reply->handle = alloc_handle( current->process, console->wait_obj, + SEMAPHORE_ALL_ACCESS, FALSE); + release_object( console ); + } + else set_error( STATUS_INVALID_PARAMETER ); +} + diff -u -N -r -x '*~' -x '.#*' -x CVS server26/console.h server/console.h --- server26/console.h 2003-05-10 22:40:04.000000000 +0200 +++ server/console.h 2003-05-23 15:43:03.000000000 +0200 @@ -42,12 +42,12 @@ int history_index; /* number of used entries in history array */ int history_mode; /* mode of history (non zero means remove doubled strings */ int edition_mode; /* index to edition mode flavors */ + struct object *wait_obj; /* object to wait on for input queue */ }; /* console functions */ extern void inherit_console(struct thread *parent_thread, struct process *process, obj_handle_t hconin); extern int free_console( struct process *process ); -extern int is_console_object( struct object *obj ); #endif /* __WINE_SERVER_CONSOLE_H */ diff -u -N -r -x '*~' -x '.#*' -x CVS server26/fd.c server/fd.c --- server26/fd.c 2003-05-15 20:34:06.000000000 +0200 +++ server/fd.c 2003-05-23 15:43:26.000000000 +0200 @@ -42,7 +42,6 @@ #include "handle.h" #include "process.h" #include "request.h" -#include "console.h" /* Because of the stupid Posix locking semantics, we need to keep * track of all file descriptors referencing a given file, and not @@ -1035,16 +1034,6 @@ reply->type = fd->fd_ops->get_file_info( fd, NULL, &reply->flags ); release_object( fd ); } - else /* check for console handle (FIXME: should be done in the client) */ - { - struct object *obj; - - if ((obj = get_handle_obj( current->process, req->handle, req->access, NULL ))) - { - if (is_console_object( obj )) reply->type = FD_TYPE_CONSOLE; - release_object( obj ); - } - } } /* get a file information */ diff -u -N -r -x '*~' -x '.#*' -x CVS server26/process.c server/process.c --- server26/process.c 2003-03-18 21:42:00.000000000 +0100 +++ server/process.c 2003-05-24 20:12:26.000000000 +0200 @@ -221,40 +221,11 @@ } if (info) { - if (!info->inherit_all && !info->use_handles) - { - /* duplicate the handle from the parent into this process */ - reply->hstdin = duplicate_handle( parent_thread->process, info->hstdin, process, - 0, TRUE, DUPLICATE_SAME_ACCESS ); - reply->hstdout = duplicate_handle( parent_thread->process, info->hstdout, process, - 0, TRUE, DUPLICATE_SAME_ACCESS ); - reply->hstderr = duplicate_handle( parent_thread->process, info->hstderr, process, - 0, TRUE, DUPLICATE_SAME_ACCESS ); - } - else - { - reply->hstdin = info->hstdin; - reply->hstdout = info->hstdout; - reply->hstderr = info->hstderr; - } - } - else - { - if (process->console) - { - reply->hstdin = alloc_handle( process, process->console, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 1 ); - reply->hstdout = alloc_handle( process, process->console->active, - GENERIC_READ | GENERIC_WRITE, 1 ); - reply->hstderr = alloc_handle( process, process->console->active, - GENERIC_READ | GENERIC_WRITE, 1 ); - } - else - { - /* no parent, let the caller decide what to do */ - reply->hstdin = reply->hstdout = reply->hstderr = 0; - } + reply->hstdin = info->hstdin; + reply->hstdout = info->hstdout; + reply->hstderr = info->hstderr; } + else reply->hstdin = reply->hstdout = reply->hstderr = INVALID_HANDLE_VALUE; /* some handles above may have been invalid; this is not an error */ if (get_error() == STATUS_INVALID_HANDLE) clear_error(); return 1; diff -u -N -r -x '*~' -x '.#*' -x CVS server26/protocol.def server/protocol.def --- server26/protocol.def 2003-06-18 21:46:42.000000000 +0200 +++ server/protocol.def 2003-06-18 20:28:29.000000000 +0200 @@ -627,7 +627,6 @@ { FD_TYPE_INVALID, FD_TYPE_DEFAULT, - FD_TYPE_CONSOLE, FD_TYPE_SOCKET, FD_TYPE_SMB }; @@ -776,6 +775,7 @@ unsigned int access; /* wanted access rights */ int inherit; /* inherit flag */ process_id_t pid; /* pid of process which shall be attached to the console */ + obj_handle_t wait_event; /* semaphore for number of active input events */ @REPLY obj_handle_t handle_in; /* handle to console input */ obj_handle_t event; /* handle to renderer events change notification */ @@ -851,6 +851,12 @@ @END +/* Get the input queue wait event */ +@REQ(get_console_wait_event) +@REPLY + obj_handle_t handle; +@END + /* Get a console mode (input or output) */ @REQ(get_console_mode) obj_handle_t handle; /* handle to the console */