|
1 // +build windows |
|
2 // +build !go1.4 |
|
3 |
|
4 package mousetrap |
|
5 |
|
6 import ( |
|
7 "fmt" |
|
8 "os" |
|
9 "syscall" |
|
10 "unsafe" |
|
11 ) |
|
12 |
|
13 const ( |
|
14 // defined by the Win32 API |
|
15 th32cs_snapprocess uintptr = 0x2 |
|
16 ) |
|
17 |
|
18 var ( |
|
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 |
|
26 type 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 |
|
39 func 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 |
|
69 func 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 |
|
85 func 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 } |