Last active
March 15, 2022 22:02
-
-
Save SCP002/1b7fd91a519a2dc60fc5b179f90472b6 to your computer and use it in GitHub Desktop.
Golang: Write a message to the current (see comments for a method for any console) console's input on Windows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Used in https://github.com/SCP002/terminator. | |
// For POSIX, see: https://gist.github.com/SCP002/c7c3bf4aafd3e32e0dc0aa65dda2bf14. | |
package main | |
import ( | |
"errors" | |
"os" | |
"unsafe" | |
"golang.org/x/sys/windows" | |
) | |
// Windows types. | |
// | |
// Inspired by https://github.com/Azure/go-ansiterm/blob/master/winterm/api.go. | |
type ( | |
// https://docs.microsoft.com/en-us/windows/console/input-record-str. | |
inputRecord struct { | |
eventType uint16 | |
keyEvent keyEventRecord | |
} | |
// https://docs.microsoft.com/en-us/windows/console/key-event-record-str. | |
keyEventRecord struct { | |
keyDown int32 | |
repeatCount uint16 | |
virtualKeyCode uint16 | |
virtualScanCode uint16 | |
unicodeChar uint16 | |
controlKeyState uint32 | |
} | |
) | |
// Windows constants. | |
const ( | |
// https://docs.microsoft.com/en-us/windows/console/input-record-str#members. | |
keyEvent uint16 = 0x0001 | |
) | |
func main() { | |
err := Write("Hello, World!\r\n") | |
if err != nil { | |
panic(err) | |
} | |
} | |
// Write writes a message to the current console's input. | |
// | |
// To write to other console, see the implementation in: | |
// | |
// https://github.com/SCP002/terminator/blob/main/internal/proxy/answer/send.go. | |
func Write(msg string) error { | |
k32 := windows.NewLazyDLL("kernel32.dll") | |
inpRecList, err := StrToInputRecords(msg) | |
if err != nil { | |
return errors.New("Failed to convert string message to an array of inputRecord.") | |
} | |
k32Proc := k32.NewProc("WriteConsoleInputW") | |
var written uint32 = 0 | |
var toWrite uint32 = uint32(len(inpRecList)) | |
r1, _, _ := k32Proc.Call( | |
os.Stdin.Fd(), | |
// Actually passing the whole slice. Must be [0] due the way syscall works. | |
uintptr(unsafe.Pointer(&inpRecList[0])), | |
uintptr(toWrite), | |
// A pointer to the number of input records actually written. Not using it (placeholder). | |
uintptr(unsafe.Pointer(&written)), | |
) | |
if r1 == 0 { | |
return errors.New("Failed to write an array of inputRecord to the current console's input.") | |
} | |
return nil | |
} | |
// StrToInputRecords converts a string into a slice of inputRecord, see: | |
// | |
// https://docs.microsoft.com/en-us/windows/console/input-record-str. | |
func StrToInputRecords(msg string) ([]inputRecord, error) { | |
records := []inputRecord{} | |
utf16chars, err := windows.UTF16FromString(msg) | |
if err != nil { | |
return records, err | |
} | |
for _, char := range utf16chars { | |
record := inputRecord{ | |
eventType: keyEvent, | |
keyEvent: keyEventRecord{ | |
// 1 = TRUE, the key is pressed. Can omit key release events. | |
keyDown: 1, | |
repeatCount: 1, | |
virtualKeyCode: 0, | |
virtualScanCode: 0, | |
unicodeChar: char, | |
controlKeyState: 0, | |
}, | |
} | |
records = append(records, record) | |
} | |
return records, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment