blob: 336142a5e3ec4c6cd2f5f3fb8706def45c758e88 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// +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}