pax_global_header00006660000000000000000000000064124202733610014512gustar00rootroot0000000000000052 comment=76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 mousetrap-1.0.0/000077500000000000000000000000001242027336100135275ustar00rootroot00000000000000mousetrap-1.0.0/LICENSE000066400000000000000000000010471242027336100145360ustar00rootroot00000000000000Copyright 2014 Alan Shreve Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. mousetrap-1.0.0/README.md000066400000000000000000000015201242027336100150040ustar00rootroot00000000000000# mousetrap mousetrap is a tiny library that answers a single question. On a Windows machine, was the process invoked by someone double clicking on the executable file while browsing in explorer? ### Motivation Windows developers unfamiliar with command line tools will often "double-click" the executable for a tool. Because most CLI tools print the help and then exit when invoked without arguments, this is often very frustrating for those users. mousetrap provides a way to detect these invocations so that you can provide more helpful behavior and instructions on how to run the CLI tool. To see what this looks like, both from an organizational and a technical perspective, see https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/ ### The interface The library exposes a single interface: func StartedByExplorer() (bool) mousetrap-1.0.0/trap_others.go000066400000000000000000000007431242027336100164140ustar00rootroot00000000000000// +build !windows package mousetrap // 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 // // On non-Windows platforms, it always returns false. func StartedByExplorer() bool { return false } mousetrap-1.0.0/trap_windows.go000066400000000000000000000045311242027336100166010ustar00rootroot00000000000000// +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" } mousetrap-1.0.0/trap_windows_1.4.go000066400000000000000000000022411242027336100171570ustar00rootroot00000000000000// +build windows // +build go1.4 package mousetrap import ( "os" "syscall" "unsafe" ) func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) if err != nil { return nil, err } defer syscall.CloseHandle(snapshot) var procEntry syscall.ProcessEntry32 procEntry.Size = uint32(unsafe.Sizeof(procEntry)) if err = syscall.Process32First(snapshot, &procEntry); err != nil { return nil, err } for { if procEntry.ProcessID == uint32(pid) { return &procEntry, nil } err = syscall.Process32Next(snapshot, &procEntry) if err != nil { return nil, err } } } // 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 { pe, err := getProcessEntry(os.Getppid()) if err != nil { return false } return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) }