| // +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" |
| } |