I'm trying to create a custom console, but I'm having issues with the cloning, for starters, the console is not accepting an Enter.
`n creates a new line,
ControlSend, {Enter} doesn't work either. It works if I enter the command in the console myself. I have to FreeConsole/AttachConsole each time too, which is not good. Anybody know how to solve this? And better, how to do the same, but with a console in the gui window, like demonstrated by Lexicos?
I want to be able to simulate keystrokes, mouseclicks/moves, setcursor and have a streaming update in the Gui version of the console.
Code:
#SingleInstance force
Gui, Color, , Black
Gui, Add, Edit, x0 y0 w600 h400 cFFFFFF vRepConsole,
Gui, Color
Gui, Add, Edit, x0 y400 w520 h20 cFFFFFF vConsoleInput, cls
Gui, Add, Button, x528 y400 w60 h20 gNewConsoleInput, Enter
Gui, Show, , ConsoleTest
Run, %ComSpec% /k dir, %A_WinDir%,, cmdPid
WinWait, ahk_pid %cmdPid%, , 3
if ErrorLevel
{
MsgBox, WinWait timed out.
ExitApp
}
AttachConsole(cmdPid)
Sleep 500 ; is needed because otherwise the timer fails
SetTimer, UpdateGui, 50
Return
Write(txt) {
; do I really need to do it this way?
global cmdPid
FileAppend, % txt, CONOUT$
DllCall("CloseHandle", "uint", hConIn)
DllCall("CloseHandle", "uint", hConOut)
DllCall("FreeConsole")
Sleep 500
ControlSend, , {Enter}, ahk_class ConsoleWindowClass
Sleep 500
AttachConsole(cmdPid)
}
NewConsoleInput:
Gui, Submit, NoHide
SetTimer, UpdateGui, Off
Write(ConsoleInput)
;ConsoleSend(ConsoleInput, cmdPid)
Sleep 500
SetTimer, UpdateGui, On
Return
UpdateGui:
; do really I need a timer, or is there an OnMessage option?
text := GetConsoleText()
If text = %prevText%
Return
GuiControl, , RepConsole, % text
prevText := text
Return
GuiClose:
DllCall("FreeConsole")
WinClose, ahk_pid %cmdPid%
ExitApp
; -----------------------------------
AttachConsole(pid)
{
global hConOut, hConIn
if !DllCall("AttachConsole","uint",pid)
{
MsgBox AttachConsole failed - error %A_LastError%.
ExitApp
}
; If it succeeded, console functions now operate on the target console window.
; Use CreateFile to retrieve a handle to the active console screen buffer.
hConOut:=DllCall("CreateFile","str","CONOUT$","uint",0xC0000000
,"uint",7,"uint",0,"uint",3,"uint",0,"uint",0)
if hConOut = -1 ; INVALID_HANDLE_VALUE
{
MsgBox CreateFile failed - error %A_LastError%.
ExitApp
}
hConIn := DllCall("CreateFile", "str", "CONIN$", "uint", 0xC0000000
, "uint", 0x3, "uint", 0, "uint", 0x3, "uint", 0, "uint", 0)
if hConIn = -1
{
MsgBox CreateFile failed - error %A_LastError%.
ExitApp
}
}
GetConsoleText()
{
global hConOut
; Allocate memory for a CONSOLE_SCREEN_BUFFER_INFO structure.
VarSetCapacity(info, 24, 0)
; Get info about the active console screen buffer.
if !DllCall("GetConsoleScreenBufferInfo","uint",hConOut,"uint",&info)
{
MsgBox GetConsoleScreenBufferInfo failed - error %A_LastError%.
ExitApp
}
; Determine which section of the buffer is on display.
ConWinLeft := NumGet(info, 10, "Short") ; info.srWindow.Left
ConWinTop := NumGet(info, 12, "Short") ; info.srWindow.Top
ConWinRight := NumGet(info, 14, "Short") ; info.srWindow.Right
ConWinBottom := NumGet(info, 16, "Short") ; info.srWindow.Bottom
ConWinWidth := ConWinRight-ConWinLeft+1
ConWinHeight := ConWinBottom-ConWinTop+1
; Allocate memory to read into.
VarSetCapacity(text, ConWinWidth*ConWinHeight, 0)
; Read text.
if !DllCall("ReadConsoleOutputCharacter","uint",hConOut,"str",text
,"uint",ConWinWidth*ConWinHeight,"uint",0,"uint*",numCharsRead)
{
MsgBox ReadConsoleOutputCharacter failed - error %A_LastError%.
ExitApp
}
/* Alternate, slower method:
; Allocate memory to read into.
VarSetCapacity(buf, ConWinWidth*ConWinHeight*4, 0)
; Read an array of CHAR_INFO structures, containing text and attributes.
; Note: &info+10 is the address of a SMALL_RECT containing the coords we
; wish to read from. On success, it will receive the actual coords used.
if !DllCall("ReadConsoleOutput","uint",hConOut,"uint",&buf
,"uint",ConWinWidth|ConWinHeight<<16,"uint",0
,"uint",&info+10)
{
MsgBox ReadConsoleOutput failed - error %A_LastError%.
ExitApp
}
; buf should now contain an array of CHAR_INFO structures.
; We must decode this to retrieve readable text.
VarSetCapacity(text, ConWinWidth*ConWinHeight)
Loop % ConWinWidth*ConWinHeight
text .= Chr(NumGet(buf, 4*(A_Index-1), "Char"))
*/
; Optional: insert line breaks every %ConWinWidth% characters.
text := RegExReplace(text, "`a).{" ConWinWidth "}(?=.)", "$0`n")
; Finally, display the text.
Return text
}
; http://www.autohotkey.com/forum/topic27854.html
; ConsoleSend("Hooray, it works!", "ahk_class ConsoleWindowClass")
; Sends text to a console's input stream. WinTitle may specify any window in
; the target process. Since each process may be attached to only one console,
; ConsoleSend fails if the script is already attached to a console.
ConsoleSend(text, ConsoleID) ;WinTitle="", WinText="", ExcludeTitle="", ExcludeText="")
{
;pid := ConsoleID
;WinGet, pid, PID, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
/*
if !pid
return false, ErrorLevel:="window"
; Attach to the console belonging to %WinTitle%'s process.
if !DllCall("AttachConsole", "uint", pid)
return false, ErrorLevel:="AttachConsole"
hConIn := DllCall("CreateFile", "str", "CONIN$", "uint", 0xC0000000
, "uint", 0x3, "uint", 0, "uint", 0x3, "uint", 0, "uint", 0)
if hConIn = -1
return false, ErrorLevel:="CreateFile"
*/
Global hConIn ; moved to AttachConsole()
VarSetCapacity(ir, 24, 0) ; ir := new INPUT_RECORD
NumPut(1, ir, 0, "UShort") ; ir.EventType := KEY_EVENT
NumPut(1, ir, 8, "UShort") ; ir.KeyEvent.wRepeatCount := 1
; wVirtualKeyCode, wVirtualScanCode and dwControlKeyState are not needed,
; so are left at the default value of zero.
Loop, Parse, text ; for each character in text
{
NumPut(Asc(A_LoopField), ir, 14, "UShort")
NumPut(true, ir, 4, "Int") ; ir.KeyEvent.bKeyDown := true
gosub ConsoleSendWrite
NumPut(false, ir, 4, "Int") ; ir.KeyEvent.bKeyDown := false
gosub ConsoleSendWrite
}
gosub ConsoleSendCleanup
return true
ConsoleSendWrite:
if ! DllCall("WriteConsoleInput", "uint", hconin, "uint", &ir, "uint", 1, "uint*", 0)
{
gosub ConsoleSendCleanup
return false, ErrorLevel:="WriteConsoleInput"
}
return
ConsoleSendCleanup:
if (hConIn!="" && hConIn!=-1)
DllCall("CloseHandle", "uint", hConIn)
; Detach from %WinTitle%'s console.
DllCall("FreeConsole")
return
}