1// +build windows
2// +build !go1.4
3
4package mousetrap
5
6import (
7	"fmt"
8	"os"
9	"syscall"
10	"unsafe"
11)
12
13const (
14	// defined by the Win32 API
15	th32cs_snapprocess uintptr = 0x2
16)
17
18var (
19	kernel                   = syscall.MustLoadDLL("kernel32.dll")
20	CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
21	Process32First           = kernel.MustFindProc("Process32FirstW")
22	Process32Next            = kernel.MustFindProc("Process32NextW")
23)
24
25// ProcessEntry32 structure defined by the Win32 API
26type processEntry32 struct {
27	dwSize              uint32
28	cntUsage            uint32
29	th32ProcessID       uint32
30	th32DefaultHeapID   int
31	th32ModuleID        uint32
32	cntThreads          uint32
33	th32ParentProcessID uint32
34	pcPriClassBase      int32
35	dwFlags             uint32
36	szExeFile           [syscall.MAX_PATH]uint16
37}
38
39func getProcessEntry(pid int) (pe *processEntry32, err error) {
40	snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
41	if snapshot == uintptr(syscall.InvalidHandle) {
42		err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
43		return
44	}
45	defer syscall.CloseHandle(syscall.Handle(snapshot))
46
47	var processEntry processEntry32
48	processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
49	ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
50	if ok == 0 {
51		err = fmt.Errorf("Process32First: %v", e1)
52		return
53	}
54
55	for {
56		if processEntry.th32ProcessID == uint32(pid) {
57			pe = &processEntry
58			return
59		}
60
61		ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
62		if ok == 0 {
63			err = fmt.Errorf("Process32Next: %v", e1)
64			return
65		}
66	}
67}
68
69func getppid() (pid int, err error) {
70	pe, err := getProcessEntry(os.Getpid())
71	if err != nil {
72		return
73	}
74
75	pid = int(pe.th32ParentProcessID)
76	return
77}
78
79// StartedByExplorer returns true if the program was invoked by the user double-clicking
80// on the executable from explorer.exe
81//
82// It is conservative and returns false if any of the internal calls fail.
83// It does not guarantee that the program was run from a terminal. It only can tell you
84// whether it was launched from explorer.exe
85func StartedByExplorer() bool {
86	ppid, err := getppid()
87	if err != nil {
88		return false
89	}
90
91	pe, err := getProcessEntry(ppid)
92	if err != nil {
93		return false
94	}
95
96	name := syscall.UTF16ToString(pe.szExeFile[:])
97	return name == "explorer.exe"
98}
99