trap_windows.go 2.3 KB
Newer Older
S
stormgbs 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
// +build windows
// +build !go1.4

package mousetrap

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

const (
	// defined by the Win32 API
	th32cs_snapprocess uintptr = 0x2
)

var (
	kernel                   = syscall.MustLoadDLL("kernel32.dll")
	CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
	Process32First           = kernel.MustFindProc("Process32FirstW")
	Process32Next            = kernel.MustFindProc("Process32NextW")
)

// ProcessEntry32 structure defined by the Win32 API
type processEntry32 struct {
	dwSize              uint32
	cntUsage            uint32
	th32ProcessID       uint32
	th32DefaultHeapID   int
	th32ModuleID        uint32
	cntThreads          uint32
	th32ParentProcessID uint32
	pcPriClassBase      int32
	dwFlags             uint32
	szExeFile           [syscall.MAX_PATH]uint16
}

func getProcessEntry(pid int) (pe *processEntry32, err error) {
	snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
	if snapshot == uintptr(syscall.InvalidHandle) {
		err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
		return
	}
	defer syscall.CloseHandle(syscall.Handle(snapshot))

	var processEntry processEntry32
	processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
	ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
	if ok == 0 {
		err = fmt.Errorf("Process32First: %v", e1)
		return
	}

	for {
		if processEntry.th32ProcessID == uint32(pid) {
			pe = &processEntry
			return
		}

		ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
		if ok == 0 {
			err = fmt.Errorf("Process32Next: %v", e1)
			return
		}
	}
}

func getppid() (pid int, err error) {
	pe, err := getProcessEntry(os.Getpid())
	if err != nil {
		return
	}

	pid = int(pe.th32ParentProcessID)
	return
}

// StartedByExplorer returns true if the program was invoked by the user double-clicking
// on the executable from explorer.exe
//
// It is conservative and returns false if any of the internal calls fail.
// It does not guarantee that the program was run from a terminal. It only can tell you
// whether it was launched from explorer.exe
func StartedByExplorer() bool {
	ppid, err := getppid()
	if err != nil {
		return false
	}

	pe, err := getProcessEntry(ppid)
	if err != nil {
		return false
	}

	name := syscall.UTF16ToString(pe.szExeFile[:])
	return name == "explorer.exe"
}