A while ago we played with console I/O here (http://www.masmforum.com/board/index.php?topic=16920.msg140977#msg140977).
The core code is this, and it works fine to prefill the console buffer:
@@: dec ebx ; write the prefill string to the console input buffer
js @F
movzx eax, byte ptr [ecx+ebx] ; read backwards, start with Len-1
inc edi ; count the records
push 0 ; dwControlKeyState
push ax ; UnicodeChar
push ax ; wVirtualKeyCode
push 0 ; looks unprofessional but it works better without MapVirtualKey
push 1 ; bKeyDown
push KEY_EVENT
jmp @B
@@: invoke GetStdHandle, STD_INPUT_HANDLE ; Google GetStdHandle WriteConsoleInput FILE_TYPE_PIPE
xchg eax, ebx
invoke SetLastError, 1
mov ebx, hConin ; Conin$ throws no errors for WriteConsoleInputA but no effect...
; call GetStInfo
; xchg eax, ebx
mov edx, esp ; buffer start
push edx ; create a slot for chars written
invoke WriteConsoleInputA, ebx, edx, edi, esp ; ml chokes without the A
Except for pipes... no effect. Googling didn't help, it seems most people use STD_INPUT_HANDLE successfully, but in my code it says invalid handle. Using CONIN$ does not throw an error, but it doesn't fill the buffer either. Any ideas?
Thanks, jj
maybe the problem is that you only push the keys (bKeyDown=1), but never release them?
deleted
Thanks, qWord. However, it seems not important. The code works perfectly in non-pipe mode.
WriteConsoleInputA returns invalid handle when used with GetStdHandle(STD_INPUT_HANDLE), but only in piped mode. The other console functions work just fine.
WriteConsoleInputA returns no error when used with CreateFile(CONIN$), and it works fine in non-pipe mode with that handle (which is different from std input handle). But the console input buffer doesn't get filled in pipe mode.
@nidud: No, the order is correct. hConin is obtained through CreateFile, not shown here. If I comment it out, WriteConsoleInputA uses std input handle and throws invalid handle error. Both work fine in non-pipe mode.
I attach the code and executables. Latest MasmBasic of today (http://masm32.com/board/index.php?topic=94.msg264#msg264) required if you want to assemble it.
Are you using named pipes?
No, just CreatePipe. The normal case for passing handles to child processes.
Just for testing purpose, I would try it with releasing the keys...
No change, exactly as before...
Line 37ff of SendStringsOnDemand.asc:
@@: dec ebx ; write the prefill string to the console input buffer
js @F
movzx eax, byte ptr [ecx+ebx] ; read backwards, start with Len-1
inc edi ; count the records
push 0 ; dwControlKeyState
push ax ; UnicodeChar
push ax ; wVirtualKeyCode
push 0 ; looks unprofessional but it works better without MapVirtualKey
push 0 ; bKeyUp
push KEY_EVENT
inc edi ; count the records
push 0 ; dwControlKeyState
push ax ; UnicodeChar
push ax ; wVirtualKeyCode
push 0 ; looks unprofessional but it works better without MapVirtualKey
push 1 ; bKeyDown
push KEY_EVENT
jmp @B
can you make a short description how I can reproduce the described bug with the two EXEs you've upload.
SendStringsOnDemand.exe "standalone" shows
Enter x, Date or Time: >date
(the stuff before is debug info that I forgot to take out - new attachment below has switched that off)
When launched from CmdGUI_full.exe, it shows
D:\Masm32\CmdGUI>SendStringsOnDemand
..
Ready
Enter x, Date or Time: >[NO DATE HERE...]
The attachment above lacked the *.dat files, here is a complete one.
did you try it with SetConsoleMode->ENABLE_ECHO_INPUT for the pipe handle?
Good idea, I tried in various places but the answer is always "invalid handle"...
invoke SetHandleInformation, hWriteOut, hflags, hflags ; we need only hWriteOut
invoke SetHandleInformation, hReadIn, hflags, hflags ; and hReadIn
invoke SetConsoleMode, hWriteOut, ENABLE_ECHO_INPUT
deb 4, "SW2", eax, $Err$()
invoke SetConsoleMode, hReadIn, ENABLE_ECHO_INPUT
deb 4, "SW2", eax, $Err$()
Well, I can't compile your source (SendStringsOnDemand) with MASM. Also, when using jWasm, I get an executable that behaviors differently than the EXE you supplied - I get directly an error "invalid handle".
Also, I've seen some "strange" comments in your code:
LOCAL PipeBytes, buffer[8000]:BYTE ; 8102 works, 8104 crashes, slow for small buffers (e.g. 100 bytes)
How should one interpret that?
"push KEY_EVENT" is that pushing a word?
WORD EventType;
Quote from: sinsi on January 03, 2013, 09:16:41 PM
"push KEY_EVENT" is that pushing a word?
WORD EventType;
the union must be aligned to 4 (size of the greatest member that is not lager than 8 ) so it should be right.
Quote from: qWord on January 03, 2013, 09:02:13 PM
Well, I can't compile your source (SendStringsOnDemand) with MASM. Also, when using jWasm, I get an executable that behaviors differently than the EXE you supplied - I get directly an error "invalid handle".
That is odd. The library \Masm32\MasmBasic\MasmBasic.lib should have time stamp 2 Jan 13, 18:33, 68126 bytes. It doesn't work with ML.exe 6.14 (no SSE2), but 6.15 upwards works fine for me.
Sorry to impose MB on you, but re-writing the whole thing in plain Masm32 would be tedious. I am not proficient in C to write a corresponding example, and I haven't found any full example on the web...
It might even help to find an executable written in another language that does prefilling of an Input$() type prompt.
QuoteAlso, I've seen some "strange" comments in your code:
LOCAL PipeBytes, buffer[8000]:BYTE ; 8102 works, 8104 crashes, slow for small buffers (e.g. 100 bytes)
How should one interpret that?
A few lines further down:
invoke ReadFile, hPipe, edi,
sizeof buffer, addr PipeBytes, NULL
I have played with several values, of course if you take 2 bytes, you need many ReadFile calls, and that slows it down. Somewhere around 8102 for XP it starts crashing, the usual problem with stack & guard pages, so I chose 8000 as a compromise. This is for XP - Win7 seems to allow higher values.
In any case, thanks a lot for your willingness to help, much appreciated :icon14:
@sinsi: yes, that works perfectly in "direct", i.e. non-piped mode.
pushw KEY_EVENT
...
imul edx, edi, 18does not crash, but the prefill stops working even in direct mode.
I'v download the library this morning. I get this error for all version of MASM
SendStringsOnDemand.asm(134) : error A2046:missing single or double quotation mark in string
cStyle$(2): Macro Called From
Input$(3): Macro Called From
Let(1): Macro Called From
the problem is the angle brackets in the literal "Enter x, Date or Time: !>"
Sorry, I use JWasm by default and didn't stumble over that one. Here is a workaround for ML 6.15:
Let esi=Input$(Chr$("Enter x, Date or Time: ", 62), esi) ; let's simulate a prompt...
I do not really understand what you are trying to do, but if it's some inter-process communication with "key-strokes" being sent from a parent process to a child process, you should probably forget using pipes and look at DuplicateHandle() instead.
Quote from: japheth on January 03, 2013, 10:55:02 PMI do not really understand what you are trying to do, but if it's some inter-process communication with "key-strokes" being sent from a parent process to a child process, you should probably forget using pipes and look at DuplicateHandle() instead.
The CmdGUI app (CmdGUI_full.exe) launches cmd.exe, displays its output in a standard edit control, and sends lines of text to cmd.exe via a pipe. Cursor up/down works like doskey /history. In short, a standard window instead of a console for cmd.exe. Right-click in CmdGUI to see one advantage of having that.
Then there is a console prog (SendStringsOnDemand.exe) that prompts for input, providing a prefilled string:
Enter x, Date or Time: >dateEverything works fine except prefilling via the pipe.
DuplicateHandle assumes that you have source code control over the child process - not a universal solution.
Quote from: jj2007 on January 03, 2013, 11:15:59 PM
DuplicateHandle assumes that you have source code control over the child process - not a universal solution.
Yes, I see. Additionally, according to MS, DuplicateHandle() will only copy console handles if target process and source process are identical.
Are you aware of any plain C or C++ solution for the prompt with prefill? I have searched a lot, but all I found is examples in Python, Linux C++ (https://bbs.archlinux.org/viewtopic.php?pid=1092160) and Ruby using the readline library (http://stackoverflow.com/questions/11023713/pre-filled-prompt-in-ruby) plus a hook (code by Casper (http://stackoverflow.com/users/823617/casper)):
require 'rubygems'
require 'rb-readline'
module RbReadline
def self.prefill_prompt(str)
@rl_prefill = str
@rl_startup_hook = :rl_prefill_hook
end
def self.rl_prefill_hook
rl_insert_text @rl_prefill if @rl_prefill
@rl_startup_hook = nil
end
end
RbReadline.prefill_prompt("Previous query")
str = Readline.readline("Enter query: ", true)
puts "You entered: #{str}"
See The GNU Readline Library (http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html), 2.2MB to download, but it seems there is no Windows version.
The functionality is not that exotic; most travel agents still have a terminal-like screen where they can edit client data via prefill. Very old technology, it's amazing that they don't switch to Windows.
> Are you aware of any plain C or C++ solution for the prompt with prefill?
Hm, I guess the best solution is some kind of KI ( deutsch: Koud Intschäggtschen ).
A small routine that reads a pipe and writes to console via WriteConsoleInput(). It has to be copied to the "other" address space and run - may be using CreateRemoteThread().
'Son cannot speak for himself, so daddy puts the words in his mouth' - interesting idea, thanks Japheth.
Edit: There is one problem, though: In the CmdGUI case, it's the 'son' who knows what to say, i.e. the child process only knows what the prefilled string should contain...
Still, if anybody knows a console proggie that asks for input and offers a prefilled string, let me know. I had hoped to find something among the DOS command, but those which prompt the user do so without offering a string in the readline, it seems...
I've put an update into the QikPad thread in the campus (http://masm32.com/board/index.php?topic=1152.msg11969#msg11969). Until now, I have not found a solution for the piped WriteConsoleInput problem.
What I did find, though, is that inkey (Masm32, using crt_getch) and Inkey (MasmBasic, using ReadFile) both behave badly when piped through cmd.exe: the proggies hang and must be killed via Task Manager. Workaround is to launch them with start myproggie. In contrast, Input$() does not hang the child process. However, the underlying ReadFile cannot be used as a substitute for inkey; SetConsoleMode 0 works in standalone mode, i.e. one keypress is enough, but in pipes it requires a Return. Confusing ::)
invoke SetLastError, 0
mov ebx, rv(GetStdHandle, STD_INPUT_HANDLE)
invoke SetConsoleMode, ebx, 0 ; enable return after single key press (works standalone but not piped)
push ecx ; create a slot for chars read
mov edx, esp
invoke ReadFile, ebx, addr buffer, 1000, edx, 0 ; works fine, doesn't block, but needs Return in pipes
deb 4, "ret", eax, $Err$()
pop ecx ; chars read
I've put an update into the QikPad thread in the campus (http://masm32.com/board/index.php?topic=1152.msg11969#msg11969). Until now, I have not found a solution for the piped WriteConsoleInput problem.
What I did find, though, is that inkey (Masm32, using crt_getch) and Inkey (MasmBasic, using ReadFile) both behave badly when piped through cmd.exe: the proggies hang and must be killed via Task Manager. Workaround is to launch them with start myproggie. In contrast, Input$() does not hang the child process. However, the underlying ReadFile cannot be used as a substitute for inkey; SetConsoleMode 0 works in standalone mode, i.e. one keypress is enough, but in pipes it requires a Return. Confusing ::)
invoke SetLastError, 0
mov ebx, rv(GetStdHandle, STD_INPUT_HANDLE)
invoke SetConsoleMode, ebx, 0 ; enable return after single key press (works standalone but not piped)
push ecx ; create a slot for chars read
mov edx, esp
invoke ReadFile, ebx, addr buffer, 1000, edx, 0 ; works fine, doesn't block, but needs Return in pipes
deb 4, "ret", eax, $Err$()
pop ecx ; chars read