返回 DeepSeek-Reasonix
external_opener_windows_helpers.go
根目录 / desktop / external_opener_windows_helpers.go
1 package main
2
3 import (
4 "path/filepath"
5 "strings"
6 )
7
8 // windowsTerminalIconCandidatePaths returns ordered icon lookup paths for
9 // Windows Terminal. Store installs register only a wt.exe App Execution Alias
10 // under LocalAppData\Microsoft\WindowsApps; the real WindowsTerminal.exe lives
11 // under Program Files\WindowsApps\Microsoft.WindowsTerminal_*.
12 //
13 // Non-elevated processes often cannot glob the protected WindowsApps package
14 // directory, so callers must treat a missing package hit as normal and apply
15 // pickWindowsTerminalIconSource (renderable fallback before zero-byte aliases).
16 //
17 // Patterns may contain filepath globs; callers expand them on the host.
18 func windowsTerminalIconCandidatePaths(wtPath, localAppData, programFiles string) []string {
19 var out []string
20 if programFiles = strings.TrimSpace(programFiles); programFiles != "" {
21 out = append(out,
22 filepath.Join(programFiles, "WindowsApps", "Microsoft.WindowsTerminal_*", "WindowsTerminal.exe"),
23 filepath.Join(programFiles, "WindowsApps", "Microsoft.WindowsTerminalPreview_*", "WindowsTerminal.exe"),
24 )
25 }
26 // Legacy/portable layouts occasionally nest the package under LocalAppData.
27 if localAppData = strings.TrimSpace(localAppData); localAppData != "" {
28 out = append(out,
29 filepath.Join(localAppData, "Microsoft", "WindowsApps", "Microsoft.WindowsTerminal_*", "WindowsTerminal.exe"),
30 filepath.Join(localAppData, "Microsoft", "WindowsApps", "Microsoft.WindowsTerminalPreview_*", "WindowsTerminal.exe"),
31 )
32 }
33 if wtPath = strings.TrimSpace(wtPath); wtPath != "" {
34 out = append(out, wtPath)
35 }
36 return out
37 }
38
39 // windowsIconCandidate is a filesystem path considered for SHGetFileInfo, with
40 // its observed size. Size 0 is typical for App Execution Alias reparse points.
41 type windowsIconCandidate struct {
42 Path string
43 Size int64
44 }
45
46 // pickWindowsTerminalIconSource chooses an IconSource path for the WT menu row.
47 // Order:
48 // 1. first non-zero-size package/binary path (real WindowsTerminal.exe)
49 // 2. renderableFallback (e.g. powershell.exe) — must beat zero-byte aliases so
50 // SHGetFileInfo is not pointed at an empty stub that yields a blank glyph
51 // 3. zero-size alias / last-known path only when nothing else is available
52 func pickWindowsTerminalIconSource(resolved []windowsIconCandidate, renderableFallback string) string {
53 for _, c := range resolved {
54 if strings.TrimSpace(c.Path) == "" || c.Size <= 0 {
55 continue
56 }
57 return c.Path
58 }
59 if fb := strings.TrimSpace(renderableFallback); fb != "" {
60 return fb
61 }
62 for _, c := range resolved {
63 if path := strings.TrimSpace(c.Path); path != "" {
64 return path
65 }
66 }
67 return ""
68 }
69
70 // windowsConsoleLaunch describes a console opener launch that never goes
71 // through cmd.exe / start (which re-parse working directories as shell text).
72 type windowsConsoleLaunch struct {
73 File string // absolute or PATH-resolved executable
74 Dir string // working directory passed to ShellExecute lpDirectory
75 }
76
77 // planWindowsConsoleLaunch builds a shell-free console launch. File is opened
78 // with ShellExecute("open") and Dir is the process working directory — paths
79 // with spaces or shell metacharacters (& | ( ) ^ %) are never concatenated into
80 // a cmd.exe command line.
81 func planWindowsConsoleLaunch(target, workdir string) windowsConsoleLaunch {
82 return windowsConsoleLaunch{
83 File: strings.TrimSpace(target),
84 Dir: workdir,
85 }
86 }
87
88 // windowsConsoleLaunchIsDirect reports that the plan opens the target binary
89 // itself (via ShellExecute) rather than a cmd.exe /c start wrapper. Command
90 // Prompt as a terminal is still direct: File is cmd.exe and Dir is the
91 // workspace — there is no intermediate shell command line.
92 func windowsConsoleLaunchIsDirect(plan windowsConsoleLaunch) bool {
93 if strings.TrimSpace(plan.File) == "" {
94 return false
95 }
96 // The plan struct has no Args/CommandLine field by design; a regression that
97 // reintroduces cmd /c start would need extra fields or a different shape.
98 return true
99 }
100
100 lines GO