Skip to content

Instantly share code, notes, and snippets.

@UserExistsError
Created September 5, 2020 18:14

Revisions

  1. UserExistsError created this gist Sep 5, 2020.
    113 changes: 113 additions & 0 deletions winpty.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    package main

    // Windows pty example
    // https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/

    import (
    "io"
    "os"
    "fmt"
    "log"
    "unsafe"
    "syscall"
    "os/exec"
    "path/filepath"
    )

    var kernel32Path = filepath.Join(os.Getenv("windir"), "System32", "kernel32.dll")

    var (
    hKernel32 = syscall.NewLazyDLL(kernel32Path)
    fCreatePseudoConsole = hKernel32.NewProc("CreatePseudoConsole")
    fResizePseudoConsole = hKernel32.NewProc("ResizePseudoConsole")
    fClosePseudoConsole = hKernel32.NewProc("ClosePseudoConsole")
    )


    const (
    S_OK uintptr = 0
    )

    type HandleIO struct {
    handle syscall.Handle
    }

    type COORD struct {
    X, Y int16
    }

    func (c *COORD) Pack() uintptr {
    return uintptr((int32(c.Y) << 16) | int32(c.X))
    }

    type HPCON syscall.Handle

    func (h *HandleIO) Read(p []byte) (int, error) {
    var numRead uint32 = 0
    err := syscall.ReadFile(h.handle, p, &numRead, nil)
    return int(numRead), err
    }

    func (h *HandleIO) Write(p []byte) (int, error) {
    var numWritten uint32 = 0
    err := syscall.WriteFile(h.handle, p, &numWritten, nil)
    return int(numWritten), err
    }

    func (h *HandleIO) Close() error {
    return syscall.CloseHandle(h.handle)
    }

    func ClosePseudoConsole(hPc HPCON) {
    fClosePseudoConsole.Call(uintptr(hPc))
    }

    func ResizePseudoConsole(hPc HPCON, coord *COORD) error {
    ret, _, _ := fResizePseudoConsole.Call(uintptr(hPc), coord.Pack())
    if ret != S_OK {
    return fmt.Errorf("ResizePseudoConsole failed with status 0x%x", ret)
    }
    return nil
    }

    func CreatePseudoConsole(hIn, hOut syscall.Handle) (HPCON, error){
    var hPc HPCON
    coord := &COORD{80,40}
    ret, _, _ := fCreatePseudoConsole.Call(
    coord.Pack(),
    uintptr(hIn),
    uintptr(hOut),
    0,
    uintptr(unsafe.Pointer(&hPc)))
    if ret != S_OK {
    return 0, fmt.Errorf("CreatePseudoConsole() failed with status 0x%x", ret)
    }
    return hPc, nil
    }

    func main() {
    var cmdIn, cmdOut syscall.Handle
    var ptyIn, ptyOut syscall.Handle
    if err := syscall.CreatePipe(&ptyIn, &cmdIn, nil, 0); err != nil {
    log.Fatal("CreatePipe: %v", err)
    }
    if err := syscall.CreatePipe(&cmdOut, &ptyOut, nil, 0); err != nil {
    log.Fatal("CreatePipe: %v", err)
    }

    cmd := exec.Command("cmd.exe")
    cmd.Stdin = &HandleIO{ptyIn}
    cmd.Stdout = &HandleIO{ptyOut}
    cmd.Stderr = &HandleIO{ptyOut}

    hPc, err := CreatePseudoConsole(ptyIn, ptyOut)
    if err != nil {
    log.Fatalf("CreatePseudoConsole %s", err)
    }
    defer ClosePseudoConsole(hPc)

    go io.Copy(os.Stdout, &HandleIO{cmdOut})
    go io.Copy(&HandleIO{cmdIn}, os.Stdin)
    err = cmd.Run()
    log.Printf("cmd.Run(): %v", err)
    }