Сделал вот такое(вроде на костыль не похоже).
Gui Color, 111111
Gui Font, s7 cFFFFFF, Georgia
Gui Add, Edit, w200 h150 hwndhConsole ReadOnly
Gui Show
i:=0, line := 0
Loop {
Sleep 100
value := "text" ++i
len := 0
ScrPos := SB_GetPos(hConsole)
EM_GETSEL(hConsole, start, end)
if(line = 50) {
len := GetFirstLineLength(msg)
msg := RegExReplace(msg, "U).*`n",,,1), msg .= value "`n"
}
else msg .= value "`n", line++
GuiControl,, % hConsole, % msg
EM_SETSEL(hConsole, start-len, end-len)
SB_SetPos(hConsole,, ScrPos)
SetScrollPos(hConsole, true, ScrPos)
if(line = 50)
PostMessage, WM_VSCROLL := 0x115, SB_LINEUP := 0,,, ahk_id %hConsole%
}
GuiClose:
ExitApp
f5::reload
GetFirstLineLength(line) {
return Strlen(RegExReplace(line, "U)`n.*",,,1))
}
; ======================================================================================================================
; Gets the starting and ending character positions of the current selection in an edit control.
; Start - receives the start of the current selection
; End - receives the end of the current selection
; ======================================================================================================================
EM_GETSEL(HWND, ByRef Start, ByRef End) {
; EM_GETSEL = 0x00B0 -> msdn.microsoft.com/en-us/library/bb761598(v=vs.85).aspx
Start := End := 0
DllCall("User32.dll\SendMessage", "Ptr", HWND, "UInt", 0x00B0, "UIntP", Start, "UIntP", End, "Ptr")
++Start, ++End
Return True
}
; ======================================================================================================================
; Selects a range of characters in an edit control.
; Start - the character index of the start of the selection.
; End - the character index of the end of the selection.
; ======================================================================================================================
EM_SETSEL(HWND, Start, End) {
; EM_SETSEL = 0x00B1 -> msdn.microsoft.com/en-us/library/bb761661(v=vs.85).aspx
Return DllCall("User32.dll\SendMessage", "Ptr", HWND, "UInt", 0x00B1, "Ptr", Start - 1, "Ptr", End - 1, "Ptr")
}
SB_GetPos(hwnd, Which:="V"){
Which := (Which="V" || Which=1) ? 1 : (Which="H" || Which=0) ? 0 : 1
Return DllCall("GetScrollPos", "UInt", Hwnd, "Int", Which)
}
SB_SetPos(hwnd, Which:="V", To:="0"){
Which := (Which="V" || Which=1) ? 1 : (Which="H" || Which=0) ? 0 : 1
Return DllCall("SetScrollPos", "UInt", Hwnd, "Int", Which, "Int", To, "UInt", 1)
}
SetScrollPos(hwnd, fnBar, nPos)
{
VarSetCapacity(si, 28, 0) ; SCROLLINFO si
NumPut(28 , si, 0) ; si.cbSize := sizeof(SCROLLINFO)
NumPut(0x4 , si, 4) ; si.fMask := SIF_POS
NumPut(nPos, si, 20) ; si.nPos := nPos
; Use SetScrollInfo first because it supports 32-bit positions.
; If an application supports positions < 0 or > 65535, it most likely
; ignores WM_#SCROLL's wParam and uses GetScrollPos or GetScrollInfo
; to get the actual scroll position.
DllCall("SetScrollInfo", "uint", hwnd, "int", fnBar, "uint", &si, "int", 0)
; WM_HSCROLL or WM_VSCROLL must be sent for the window to update it's contents.
msg := (fnBar=0) ? 0x114 : 0x115
wParam := 4 ; SB_THUMBPOSITION
| ((nPos&0xFFFF)<<16)
SendMessage, msg, wParam,,, ahk_id %hwnd%
return (ErrorLevel != "FAIL")
}
GetScrollInfo(hwnd, fnBar, ByRef nPos=0, ByRef nTrackPos=0, ByRef nMin=0, ByRef nMax=0, ByRef nPage=0)
{
WinGet, Style, Style, ahk_id %hwnd%
if !(Style & (fnBar ? 0x200000 : 0x100000))
return false ; Fix for some controls which report {min:0,max:100} even though scroll bar doesn't exist.
VarSetCapacity(si, 28, 0) ; SCROLLINFO si
NumPut(28 , si, 0) ; si.cbSize := sizeof(SCROLLINFO)
NumPut(0x17, si, 4) ; si.fMask := SIF_ALL
if ! DllCall("GetScrollInfo", "uint", hwnd, "int", fnBar, "uint", &si)
return false
nPos := NumGet(si, 20)
nTrackPos := NumGet(si, 24)
nMin := NumGet(si, 8)
nMax := NumGet(si, 12)
nPage := NumGet(si, 16)
return true
}
Только, если выделять текст во время добавление строчки то он будет прыгать. Но то не существенно.