From 1ba63fccf59070f4c4622e06797ad80dddb1962d Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Mon, 21 Oct 2024 11:00:40 -0400 Subject: [PATCH] windows: add console input api This adds multiple functions to read, count, and discard console input events. Using `ReadConsoleInput` instead of `ReadConsole` or `ReadFile` is necessary to capture extended events such as window resize, focus, and extended key and mouse events. This also define console event types and structs. --- windows/syscall_windows.go | 4 ++ windows/types_windows.go | 87 +++++++++++++++++++++++++++++++++++++ windows/zsyscall_windows.go | 36 +++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 63471b0e4..804fc82dc 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -319,6 +319,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetConsoleOutputCP(cp uint32) (err error) = kernel32.SetConsoleOutputCP //sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW //sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW +//sys ReadConsoleInput(console Handle, buf *InputRecord, toread uint32, read *uint32) (err error) = kernel32.ReadConsoleInputW +//sys PeekConsoleInput(console Handle, buf *InputRecord, toread uint32, read *uint32) (err error) = kernel32.PeekConsoleInputW +//sys GetNumberOfConsoleInputEvents(console Handle, numevents *uint32) (err error) = kernel32.GetNumberOfConsoleInputEvents +//sys FlushConsoleInputBuffer(console Handle) (err error) = kernel32.FlushConsoleInputBuffer //sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole //sys CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot //sys Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW diff --git a/windows/types_windows.go b/windows/types_windows.go index 7b97a154c..99576fa75 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go @@ -3474,3 +3474,90 @@ const ( KLF_NOTELLSHELL = 0x00000080 KLF_SETFORPROCESS = 0x00000100 ) + +// FocusEventRecord corresponds to the FocusEventRecord structure from the +// Windows console API. +// https://docs.microsoft.com/en-us/windows/console/focus-event-record-str +type FocusEventRecord struct { + // SetFocus is reserved and should not be used. + SetFocus bool +} + +// KeyEventRecord corresponds to the KeyEventRecord structure from the Windows +// console API. +// https://docs.microsoft.com/en-us/windows/console/key-event-record-str +type KeyEventRecord struct { + // KeyDown specified whether the key is pressed or released. + KeyDown bool + + // RepeatCount indicates that a key is being held down. For example, when a + // key is held down, five events with RepeatCount equal to 1 may be + // generated, one event with RepeatCount equal to 5, or multiple events + // with RepeatCount greater than or equal to 1. + RepeatCount uint16 + + // VirtualKeyCode identifies the given key in a device-independent manner + // (see + // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes). + VirtualKeyCode uint16 + + // VirtualScanCode represents the device-dependent value generated by the + // keyboard hardware. + VirtualScanCode uint16 + + // Char is the character that corresponds to the pressed key. Char can be + // zero for some keys. + Char rune + + //ControlKeyState holds the state of the control keys. + ControlKeyState uint32 +} + +// MenuEventRecord corresponds to the MenuEventRecord structure from the +// Windows console API. +// https://docs.microsoft.com/en-us/windows/console/menu-event-record-str +type MenuEventRecord struct { + CommandID uint32 +} + +// MouseEventRecord corresponds to the MouseEventRecord structure from the +// Windows console API. +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str +type MouseEventRecord struct { + // MousePosition contains the location of the cursor, in terms of the + // console screen buffer's character-cell coordinates. + MousePositon Coord + + // ButtonState holds the status of the mouse buttons. + ButtonState uint32 + + // ControlKeyState holds the state of the control keys. + ControlKeyState uint32 + + // EventFlags specify the type of mouse event. + EventFlags uint32 +} + +// WindowBufferSizeRecord corresponds to the WindowBufferSizeRecord structure +// from the Windows console API. +// https://docs.microsoft.com/en-us/windows/console/window-buffer-size-record-str +type WindowBufferSizeRecord struct { + // Size contains the size of the console screen buffer, in character cell + // columns and rows. + Size Coord +} + +// InputRecord corresponds to the INPUT_RECORD structure from the Windows +// console API. +// +// https://docs.microsoft.com/en-us/windows/console/input-record-str +type InputRecord struct { + // EventType specifies the type of event that helt in Event. + EventType uint16 + + // Padding of the 16-bit EventType to a whole 32-bit dword. + _ [2]byte + + // Event holds the actual event data. + Event [16]byte +} diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index 63fad80d7..1ab1a12fd 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -233,6 +233,7 @@ var ( procFindResourceW = modkernel32.NewProc("FindResourceW") procFindVolumeClose = modkernel32.NewProc("FindVolumeClose") procFindVolumeMountPointClose = modkernel32.NewProc("FindVolumeMountPointClose") + procFlushConsoleInputBuffer = modkernel32.NewProc("FlushConsoleInputBuffer") procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") procFormatMessageW = modkernel32.NewProc("FormatMessageW") @@ -277,6 +278,7 @@ var ( procGetModuleHandleExW = modkernel32.NewProc("GetModuleHandleExW") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") + procGetNumberOfConsoleInputEvents = modkernel32.NewProc("GetNumberOfConsoleInputEvents") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procGetPriorityClass = modkernel32.NewProc("GetPriorityClass") procGetProcAddress = modkernel32.NewProc("GetProcAddress") @@ -326,6 +328,7 @@ var ( procOpenMutexW = modkernel32.NewProc("OpenMutexW") procOpenProcess = modkernel32.NewProc("OpenProcess") procOpenThread = modkernel32.NewProc("OpenThread") + procPeekConsoleInputW = modkernel32.NewProc("PeekConsoleInputW") procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") procProcess32FirstW = modkernel32.NewProc("Process32FirstW") procProcess32NextW = modkernel32.NewProc("Process32NextW") @@ -335,6 +338,7 @@ var ( procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW") procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW") procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject") + procReadConsoleInputW = modkernel32.NewProc("ReadConsoleInputW") procReadConsoleW = modkernel32.NewProc("ReadConsoleW") procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW") procReadFile = modkernel32.NewProc("ReadFile") @@ -2055,6 +2059,14 @@ func FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error) { return } +func FlushConsoleInputBuffer(console Handle) (err error) { + r1, _, e1 := syscall.SyscallN(procFlushConsoleInputBuffer.Addr(), uintptr(console)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func FlushFileBuffers(handle Handle) (err error) { r1, _, e1 := syscall.SyscallN(procFlushFileBuffers.Addr(), uintptr(handle)) if r1 == 0 { @@ -2409,6 +2421,14 @@ func GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint3 return } +func GetNumberOfConsoleInputEvents(console Handle, numevents *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procGetNumberOfConsoleInputEvents.Addr(), uintptr(console), uintptr(unsafe.Pointer(numevents))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wait bool) (err error) { var _p0 uint32 if wait { @@ -2866,6 +2886,14 @@ func OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (hand return } +func PeekConsoleInput(console Handle, buf *InputRecord, toread uint32, read *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procPeekConsoleInputW.Addr(), uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uintptr, overlapped *Overlapped) (err error) { r1, _, e1 := syscall.SyscallN(procPostQueuedCompletionStatus.Addr(), uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped))) if r1 == 0 { @@ -2939,6 +2967,14 @@ func QueryInformationJobObject(job Handle, JobObjectInformationClass int32, JobO return } +func ReadConsoleInput(console Handle, buf *InputRecord, toread uint32, read *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procReadConsoleInputW.Addr(), uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) { r1, _, e1 := syscall.SyscallN(procReadConsoleW.Addr(), uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl))) if r1 == 0 {