Skip to content

Instantly share code, notes, and snippets.

@UserExistsError
Created September 5, 2020 18:14
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save UserExistsError/e700d4f11b79753f5c21b3f0dfd43a3b to your computer and use it in GitHub Desktop.
Save UserExistsError/e700d4f11b79753f5c21b3f0dfd43a3b to your computer and use it in GitHub Desktop.
Windows Pseudo Console (ConPTY) in Golang
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)
}
@NHAS
Copy link

NHAS commented Jul 19, 2021

This is pretty awesome! Thanks for making it

@lonnywong
Copy link

Pseudo-terminal will not be allocated because stdin is not a terminal.

cmd := exec.Command("ssh", "x.x.x.x")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment