//go:build windows package windows import ( "context" "fmt" "os/exec" "path/filepath" "syscall" "time" "golang.org/x/sys/windows" "softbox.local/core/updater" ) func (platform) WaitForProcessExit(ctx context.Context, pid int, timeout time.Duration) error { return waitForProcessExit(ctx, pid, timeout, openWindowsProcess) } func (platform) StartSelfUpdate(command updater.StartCommand) (int, error) { if !filepath.IsAbs(command.Entrypoint) || !filepath.IsAbs(command.WorkingDirectory) { return 0, fmt.Errorf("self-update launch paths must be absolute") } if filepath.Base(command.Entrypoint) != updater.ProductExecutableName || filepath.Dir(command.Entrypoint) != filepath.Clean(command.WorkingDirectory) || command.HealthRequestID == "" { return 0, fmt.Errorf("invalid fixed self-update launch command") } commandLine := exec.Command(command.Entrypoint, updater.InternalHealthFlag, command.HealthRequestID) commandLine.Dir = command.WorkingDirectory if err := commandLine.Start(); err != nil { return 0, err } return commandLine.Process.Pid, nil } func (platform) SyncDirectory(path string) error { pathPointer, err := syscall.UTF16PtrFromString(path) if err != nil { return fmt.Errorf("encode directory path: %w", err) } handle, err := syscall.CreateFile(pathPointer, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) if err != nil { return fmt.Errorf("open directory handle: %w", err) } if err := syscall.FlushFileBuffers(handle); err != nil { _ = syscall.CloseHandle(handle) return fmt.Errorf("flush directory handle: %w", err) } if err := syscall.CloseHandle(handle); err != nil { return fmt.Errorf("close directory handle: %w", err) } return nil } type windowsPIDHandle struct{ handle windows.Handle } func openWindowsProcess(pid int) (pidWaitHandle, error) { handle, err := windows.OpenProcess(windows.SYNCHRONIZE, false, uint32(pid)) if err != nil { return nil, err } return windowsPIDHandle{handle: handle}, nil } func (handle windowsPIDHandle) Wait(timeout time.Duration) (bool, error) { milliseconds := uint32(timeout / time.Millisecond) if milliseconds == 0 { milliseconds = 1 } result, err := windows.WaitForSingleObject(handle.handle, milliseconds) if err != nil { return false, err } switch result { case windows.WAIT_OBJECT_0: return true, nil case uint32(windows.WAIT_TIMEOUT): return false, nil default: return false, fmt.Errorf("WaitForSingleObject returned %d", result) } } func (handle windowsPIDHandle) Close() error { return windows.CloseHandle(handle.handle) }