Add elimination API handler, update browser logic for process tracking, and refine UI animations and modal handling.
This commit is contained in:
+156
-20
@@ -4,42 +4,178 @@ import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
shell32 = syscall.NewLazyDLL("shell32.dll")
|
||||
shellExecuteW = shell32.NewProc("ShellExecuteW")
|
||||
shell32 = syscall.NewLazyDLL("shell32.dll")
|
||||
shellExecuteExW = shell32.NewProc("ShellExecuteExW")
|
||||
)
|
||||
|
||||
const (
|
||||
SEE_MASK_NOCLOSEPROCESS = 0x00000040
|
||||
SW_SHOW = 5
|
||||
)
|
||||
|
||||
// SHELLEXECUTEINFO structure for ShellExecuteEx
|
||||
type shellExecuteInfo struct {
|
||||
cbSize uint32
|
||||
fMask uint32
|
||||
hwnd uintptr
|
||||
lpVerb *uint16
|
||||
lpFile *uint16
|
||||
lpParameters *uint16
|
||||
lpDirectory *uint16
|
||||
nShow int32
|
||||
hInstApp uintptr
|
||||
lpIDList uintptr
|
||||
lpClass *uint16
|
||||
hkeyClass uintptr
|
||||
dwHotKey uint32
|
||||
hIconOrMonitor uintptr
|
||||
hProcess windows.Handle
|
||||
}
|
||||
|
||||
// BrowserHandle represents a handle to the opened browser process
|
||||
type BrowserHandle struct {
|
||||
ProcessID uint32
|
||||
Handle windows.Handle
|
||||
}
|
||||
|
||||
// OpenBrowser opens the default browser to the specified URL using Windows ShellExecute API
|
||||
func OpenBrowser(url string) error {
|
||||
// Returns a handle to the browser process that can be used to close it later
|
||||
func OpenBrowser(url string) (*BrowserHandle, error) {
|
||||
// Convert strings to UTF16 pointers
|
||||
operation, err := syscall.UTF16PtrFromString("open")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert operation string: %w", err)
|
||||
return nil, fmt.Errorf("failed to convert operation string: %w", err)
|
||||
}
|
||||
|
||||
urlPtr, err := syscall.UTF16PtrFromString(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert URL string: %w", err)
|
||||
return nil, fmt.Errorf("failed to convert URL string: %w", err)
|
||||
}
|
||||
|
||||
// ShellExecuteW(hwnd, operation, file, parameters, directory, showCmd)
|
||||
// SW_SHOWNORMAL = 1, SW_SHOW = 5
|
||||
ret, _, callErr := shellExecuteW.Call(
|
||||
0, // hwnd (NULL)
|
||||
uintptr(unsafe.Pointer(operation)), // operation ("open")
|
||||
uintptr(unsafe.Pointer(urlPtr)), // file (URL)
|
||||
0, // parameters (NULL)
|
||||
0, // directory (NULL)
|
||||
5, // showCmd (SW_SHOW)
|
||||
)
|
||||
|
||||
// ShellExecute returns a value > 32 on success
|
||||
if ret <= 32 {
|
||||
return fmt.Errorf("ShellExecute failed with code: %d (error: %v)", ret, callErr)
|
||||
// Initialize SHELLEXECUTEINFO structure
|
||||
sei := shellExecuteInfo{
|
||||
cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})),
|
||||
fMask: SEE_MASK_NOCLOSEPROCESS, // Request process handle
|
||||
hwnd: 0,
|
||||
lpVerb: operation,
|
||||
lpFile: urlPtr,
|
||||
lpParameters: nil,
|
||||
lpDirectory: nil,
|
||||
nShow: SW_SHOW,
|
||||
hInstApp: 0,
|
||||
}
|
||||
|
||||
fmt.Printf("[web] Browser opened successfully (return code: %d)\n", ret)
|
||||
// Call ShellExecuteExW
|
||||
ret, _, err := shellExecuteExW.Call(uintptr(unsafe.Pointer(&sei)))
|
||||
if ret == 0 {
|
||||
return nil, fmt.Errorf("ShellExecuteExW failed: %w", err)
|
||||
}
|
||||
|
||||
if sei.hInstApp <= 32 {
|
||||
return nil, fmt.Errorf("ShellExecuteExW failed with code: %d", sei.hInstApp)
|
||||
}
|
||||
|
||||
// Get process ID from handle
|
||||
var processID uint32
|
||||
if sei.hProcess != 0 {
|
||||
processID, err = windows.GetProcessId(sei.hProcess)
|
||||
if err != nil {
|
||||
// If we can't get PID, still return the handle
|
||||
processID = 0
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("[web] Browser opened successfully (PID: %d)\n", processID)
|
||||
|
||||
return &BrowserHandle{
|
||||
ProcessID: processID,
|
||||
Handle: sei.hProcess,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close terminates the browser process and all child processes
|
||||
func (bh *BrowserHandle) Close() error {
|
||||
if bh == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First try to kill the direct process if we have a handle
|
||||
if bh.Handle != 0 {
|
||||
windows.CloseHandle(bh.Handle)
|
||||
}
|
||||
|
||||
// Kill all browser processes that might have our URL open
|
||||
// This is more reliable than trying to track the exact process tree
|
||||
killed := killBrowserProcesses()
|
||||
|
||||
fmt.Printf("[web] Terminated %d browser process(es)\n", killed)
|
||||
return nil
|
||||
}
|
||||
|
||||
// killBrowserProcesses finds and kills common browser processes
|
||||
func killBrowserProcesses() int {
|
||||
browserExes := []string{
|
||||
"chrome.exe",
|
||||
"msedge.exe",
|
||||
"firefox.exe",
|
||||
"brave.exe",
|
||||
"opera.exe",
|
||||
"iexplore.exe",
|
||||
}
|
||||
|
||||
killed := 0
|
||||
for _, exeName := range browserExes {
|
||||
count := killProcessByName(exeName)
|
||||
killed += count
|
||||
}
|
||||
|
||||
return killed
|
||||
}
|
||||
|
||||
// killProcessByName kills all processes with the given executable name
|
||||
func killProcessByName(exeName string) int {
|
||||
snapshot, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer windows.CloseHandle(snapshot)
|
||||
|
||||
var procEntry windows.ProcessEntry32
|
||||
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||
|
||||
err = windows.Process32First(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
killed := 0
|
||||
for {
|
||||
// Convert the process name from [260]uint16 to string
|
||||
processName := syscall.UTF16ToString(procEntry.ExeFile[:])
|
||||
|
||||
if processName == exeName {
|
||||
// Open process with terminate rights
|
||||
handle, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, procEntry.ProcessID)
|
||||
if err == nil {
|
||||
err = windows.TerminateProcess(handle, 0)
|
||||
if err == nil {
|
||||
killed++
|
||||
fmt.Printf("[web] Killed %s (PID: %d)\n", exeName, procEntry.ProcessID)
|
||||
}
|
||||
windows.CloseHandle(handle)
|
||||
}
|
||||
}
|
||||
|
||||
err = windows.Process32Next(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return killed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user