返回 DeepSeek-Reasonix
executable_path_windows.go
根目录 / internal / desktoplauncher / executable_path_windows.go
1 //go:build windows
2
3 package desktoplauncher
4
5 import (
6 "fmt"
7 "os"
8 "strings"
9
10 "golang.org/x/sys/windows"
11 )
12
13 const maxFinalPathUTF16 = 1 << 16
14
15 // resolveExecutablePath opens the launcher and asks Windows for the final DOS
16 // path represented by that handle. Unlike filepath.EvalSymlinks, this resolves
17 // directory junctions such as Scoop's stable current entry.
18 func resolveExecutablePath(path string) (string, error) {
19 file, err := os.Open(path)
20 if err != nil {
21 return "", fmt.Errorf("open executable: %w", err)
22 }
23 defer file.Close()
24
25 handle := windows.Handle(file.Fd())
26 size := uint32(256)
27 for {
28 buf := make([]uint16, size)
29 n, err := windows.GetFinalPathNameByHandle(handle, &buf[0], size, 0)
30 if err != nil {
31 return "", fmt.Errorf("get final executable path: %w", err)
32 }
33 if n < size {
34 return normalizeFinalWindowsPath(windows.UTF16ToString(buf[:n])), nil
35 }
36 if n >= maxFinalPathUTF16 {
37 return "", fmt.Errorf("get final executable path: required buffer is too large: %d", n)
38 }
39 size = n + 1
40 }
41 }
42
43 func normalizeFinalWindowsPath(path string) string {
44 const (
45 extendedPrefix = `\\?\`
46 extendedUNC = `\\?\UNC\`
47 )
48 if len(path) >= len(extendedUNC) && strings.EqualFold(path[:len(extendedUNC)], extendedUNC) {
49 return `\\` + path[len(extendedUNC):]
50 }
51 if len(path) >= 7 && strings.EqualFold(path[:len(extendedPrefix)], extendedPrefix) &&
52 isASCIILetter(path[4]) && path[5] == ':' && path[6] == '\\' {
53 return path[len(extendedPrefix):]
54 }
55 return path
56 }
57
58 func isASCIILetter(ch byte) bool {
59 return ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z'
60 }
61
61 lines GO