Skip to content

Commit

Permalink
add support for windows xp using NtQueryObject in ntdll.dll (#40)
Browse files Browse the repository at this point in the history
* add support for windows xp using NtQueryObject in ntdll.dll
  • Loading branch information
mysqto authored and mattn committed Aug 18, 2019
1 parent e1f7b56 commit bf9a1de
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
39 changes: 35 additions & 4 deletions isatty_windows.go
Expand Up @@ -4,22 +4,26 @@
package isatty

import (
"errors"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
)

const (
fileNameInfo uintptr = 2
fileTypePipe = 3
objectNameInfo uintptr = 1
fileNameInfo = 2
fileTypePipe = 3
)

var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
ntdll = syscall.NewLazyDLL("ntdll.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
procGetFileType = kernel32.NewProc("GetFileType")
procNtQueryObject = ntdll.NewProc("NtQueryObject")
)

func init() {
Expand All @@ -45,7 +49,10 @@ func isCygwinPipeName(name string) bool {
return false
}

if token[0] != `\msys` && token[0] != `\cygwin` {
if token[0] != `\msys` &&
token[0] != `\cygwin` &&
token[0] != `\Device\NamedPipe\msys` &&
token[0] != `\Device\NamedPipe\cygwin` {
return false
}

Expand All @@ -68,11 +75,35 @@ func isCygwinPipeName(name string) bool {
return true
}

// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
// Windows vista to 10
// see https://stackoverflow.com/a/18792477 for details
func getFileNameByHandle(fd uintptr) (string, error) {
if procNtQueryObject == nil {
return "", errors.New("ntdll.dll: NtQueryObject not supported")
}

var buf [4 + syscall.MAX_PATH]uint16
var result int
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
if r != 0 {
return "", e
}
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
}

// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
// terminal.
func IsCygwinTerminal(fd uintptr) bool {
if procGetFileInformationByHandleEx == nil {
return false
name, err := getFileNameByHandle(fd)
if err != nil {
return false
}
return isCygwinPipeName(name)
}

// Cygwin/msys's pty is a pipe.
Expand Down
3 changes: 3 additions & 0 deletions isatty_windows_test.go
Expand Up @@ -23,6 +23,9 @@ func TestCygwinPipeName(t *testing.T) {
{`\cygwin-e022582115c10879-pty4-from-master`, true},
{`\msys-e022582115c10879-pty4-to-master`, true},
{`\cygwin-e022582115c10879-pty4-to-master`, true},
{`\Device\NamedPipe\cygwin-e022582115c10879-pty4-from-master`, true},
{`\Device\NamedPipe\msys-e022582115c10879-pty4-to-master`, true},
{`Device\NamedPipe\cygwin-e022582115c10879-pty4-to-master`, false},
}

for _, test := range tests {
Expand Down

0 comments on commit bf9a1de

Please sign in to comment.