Hello,
I've been looking at a patch which sorts out NUL and CON handling in a part of the command shell, and have stumbled upon a problem I am not sure how to fix because I really dont understand the underlying console handling, and would appreciate someone pointing me in a direction.
For simplicity sake I've cut this down to a tiny test program, which works on windows and fails on wine which does the following: Opens the device ("\.\CON") with CreateFile with GENERIC_READ rights (which internally opens a CONIN$ device with the same access rights) Reads from the device with ReadFile - Because its a console device, this drops through to ReadConsoleW, which creates a CONOUT$ and then waits on a keystroke - Once the key is pressed (WCEL_Get) the key is 'inserted' into the input buffer by calling WriteConsoleInputW
The issue is that WriteConsoleInputW requires GENERIC_WRITE access, but the CON device (\.\CON) was opened as GENERIC_READ (and in fact fails if I try to open it with GENERIC_WRITE). CreateFile("CONIN$"...) will let me open in GENERIC_READ/GENERIC_WRITE mode and the program works on both windows and wine, but if you open CONIN$ GENERIC_READ only then it fails on wine and works on windows, with the same issue.
Now on windows, this works... the question is how to make it work on wine...
My gut feeling, with nothing backing this at all, is that WCEL_Get should not use WriteConsoleInputW to inject the values into the input buffer, instead making the server call directly, but passing through to the server call something to indicate that it is ok to add the data to the buffer, but I'm fast getting out of my depth!
Thanks for any help! I'll file a bug with my analysis and testcase, but would rather fix the thing!
Jason
Le 09/11/2012 22:49, Ann and Jason Edmeades a écrit :
Hello,
I've been looking at a patch which sorts out NUL and CON handling in a part of the command shell, and have stumbled upon a problem I am not sure how to fix because I really dont understand the underlying console handling, and would appreciate someone pointing me in a direction.
For simplicity sake I've cut this down to a tiny test program, which works on windows and fails on wine which does the following: Opens the device ("\.\CON") with CreateFile with GENERIC_READ rights (which internally opens a CONIN$ device with the same access rights) Reads from the device with ReadFile
- Because its a console device, this drops through to ReadConsoleW,
which creates a CONOUT$ and then waits on a keystroke
- Once the key is pressed (WCEL_Get) the key is 'inserted' into the
input buffer by calling WriteConsoleInputW
The issue is that WriteConsoleInputW requires GENERIC_WRITE access, but the CON device (\.\CON) was opened as GENERIC_READ (and in fact fails if I try to open it with GENERIC_WRITE). CreateFile("CONIN$"...) will let me open in GENERIC_READ/GENERIC_WRITE mode and the program works on both windows and wine, but if you open CONIN$ GENERIC_READ only then it fails on wine and works on windows, with the same issue.
Now on windows, this works... the question is how to make it work on wine...
My gut feeling, with nothing backing this at all, is that WCEL_Get should not use WriteConsoleInputW to inject the values into the input buffer, instead making the server call directly, but passing through to the server call something to indicate that it is ok to add the data to the buffer, but I'm fast getting out of my depth!
Thanks for any help! I'll file a bug with my analysis and testcase, but would rather fix the thing!
Jason
from your (partial) explanation, I assume that : - you're using a bare console, ie didn't run your app under wineconsole, but simply as wine foo.exe - if so, does running your app under wineconsole provides the same bad effects ? - another item to test would be to see if writeconsoleinput really enforces the generic_write flag as msdn states
but, for a bare handle, we have to convert unix console keystrokes into win32 keycodes, so we need to feed the keycodes into wine server
if we really have to enforce generic_write, then we should wrap for TERM_ functions the calls to writeconsoleinput with a helper that reopens the console with the right mode A+
Hi Eric, Thanks for taking the time to reply
The issue is that WriteConsoleInputW requires GENERIC_WRITE access, but the
CON device (\.\CON) was opened as GENERIC_READ (and in fact fails if I try to open it with GENERIC_WRITE).
You said...
- you're using a bare console, ie didn't run your app under wineconsole,
but simply as wine foo.exe
- if so, does running your app under wineconsole provides the same bad
effects ?
Correct, I was running wine <testpgm> (Program is attached to bug 32183). I had not tried it under wineconsole but just have. It works (passes) for all 3 test cases, ie read only file i/o, read and r/w CONIN$. (It does give a slightly different GetLastError result to windows for the 4th testcase, which I really dont care about as it is supposed to fail!)). So its really only failing in bare console mode.
- another item to test would be to see if writeconsoleinput really enforces
the generic_write flag as msdn states
So some tests seem to show on windows: - WriteConsoleInput does fail if CONIN$ isnt opened for write - WriteConsoleInput does not seem to work at all for a handle opened with WriteFile, which I do not understand (Returns gle 3).
but, for a bare handle, we have to convert unix console keystrokes into win32 keycodes, so we need to feed the keycodes into wine server
if we really have to enforce generic_write, then we should wrap for TERM_
functions the calls to writeconsoleinput with a helper that reopens the console with the right mode
Do you mean TERM_? The only caller to WriteConsoleInput is console.c, and I think the 'guilty' routine is bare_console_fetch_input. I think you are saying that in read_console_input where we determine that it is a bare fd (get_console_bare_fd == -1), we wrap the call to bare_console_fetch_input by reopening the handle with r/w first? If so, is there any obvious way to do this.. ideally we would want to test for write ability first, but I cant see an API to do that, so I could just open CONIN$ r/w, call bare_console_fetch_input and then close CONIN$ afterwards, is that what you mean?
I tested the following HANDLE hConRW = CreateFileA("CONIN$" , GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hConRW == INVALID_HANDLE_VALUE) hConRW = handle; ret = bare_console_fetch_input(hConRW, fd, timeout); if (hConRW!=handle) CloseHandle(hConRW);
It works a charm, although feels inefficient for the times handle was already r/w - (it feels slightly odd that the 'handle' we are working on is unrelated to the handle passed in (I was worried about stdin redirection, but that seems to work fine even after the change).
Was this what you meant or have I gone off on a tangent?
Jason
I tested the following HANDLE hConRW = CreateFileA("CONIN$" , GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hConRW == INVALID_HANDLE_VALUE) hConRW = handle; ret = bare_console_fetch_input(hConRW, fd, timeout); if (hConRW!=handle) CloseHandle(hConRW);
It works a charm, although feels inefficient for the times handle was already r/w - (it feels slightly odd that the 'handle' we are working on is unrelated to the handle passed in (I was worried about stdin redirection, but that seems to work fine even after the change).
Was this what you meant or have I gone off on a tangent?
more or less. I was thinking to add a helper functions for the writeconsoleinput calls in bare_console_fetch_input. and this helper function would : - try to call writeconsoleinput with the current console input handle - if it fails with status_access_denied, then create the right handle as you did - from your code, please use CreateFileW instead of CreateFileA
A+