microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
6cc8936acc292a25ffffc08efeb06fe6cc3261c9

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

dotnet/autoShell/AutoShell_Themes.cs

295lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System;
5using System.Collections.Generic;
6using System.Diagnostics;
7using System.IO;
8using System.Linq;
9using System.Text;
10using System.Threading.Tasks;
11using Microsoft.Win32;
12
13namespace autoShell
14{
15 /// <summary>
16 /// AutoShell class partial for managing Windows themes.
17 /// </summary>
18 /// <remarks>This code was mostly generated by AI under the guidance of a human.</remarks>
19 internal partial class AutoShell
20 {
21 private static string s_previousTheme = null;
22 private static Dictionary<string, string> s_themeDictionary = null;
23 private static Dictionary<string, string> s_themeDisplayNameDictionary = null;
24
25 private static void LoadThemes()
26 {
27 s_themeDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
28 s_themeDisplayNameDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
29
30 string[] themePaths = new string[]
31 {
32 Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Resources", "Themes"),
33 Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Resources", "Ease of Access Themes"),
34 Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "Windows", "Themes")
35 };
36
37 foreach (string themesFolder in themePaths)
38 {
39 if (Directory.Exists(themesFolder))
40 {
41 foreach (string themeFile in Directory.GetFiles(themesFolder, "*.theme"))
42 {
43 string themeName = Path.GetFileNameWithoutExtension(themeFile);
44 if (!s_themeDictionary.ContainsKey(themeName))
45 {
46 s_themeDictionary[themeName] = themeFile;
47
48 // Parse display name from theme file
49 string displayName = GetThemeDisplayName(themeFile);
50 if (!string.IsNullOrEmpty(displayName) && !s_themeDisplayNameDictionary.ContainsKey(displayName))
51 {
52 s_themeDisplayNameDictionary[displayName] = themeName;
53 }
54 }
55 }
56 }
57 }
58
59 s_themeDictionary["previous"] = GetCurrentTheme();
60 }
61
62 /// <summary>
63 /// Parses the display name from a .theme file.
64 /// </summary>
65 /// <param name="themeFilePath">The full path to the .theme file.</param>
66 /// <returns>The display name, or null if not found.</returns>
67 private static string GetThemeDisplayName(string themeFilePath)
68 {
69 try
70 {
71 foreach (string line in File.ReadLines(themeFilePath))
72 {
73 if (line.StartsWith("DisplayName=", StringComparison.OrdinalIgnoreCase))
74 {
75 string displayName = line.Substring("DisplayName=".Length).Trim();
76
77 // Handle localized strings (e.g., @%SystemRoot%\System32\themeui.dll,-2013)
78 if (displayName.StartsWith("@"))
79 {
80 displayName = ResolveLocalizedString(displayName);
81 }
82
83 return displayName;
84 }
85 }
86 }
87 catch
88 {
89 // Ignore errors reading theme file
90 }
91 return null;
92 }
93
94 /// <summary>
95 /// Resolves a localized string resource reference.
96 /// </summary>
97 /// <param name="localizedString">The localized string reference (e.g., @%SystemRoot%\System32\themeui.dll,-2013).</param>
98 /// <returns>The resolved string, or the original string if resolution fails.</returns>
99 private static string ResolveLocalizedString(string localizedString)
100 {
101 try
102 {
103 // Remove the @ prefix
104 string resourcePath = localizedString.Substring(1);
105
106 // Expand environment variables
107 int commaIndex = resourcePath.LastIndexOf(',');
108 if (commaIndex > 0)
109 {
110 string dllPath = Environment.ExpandEnvironmentVariables(resourcePath.Substring(0, commaIndex));
111 string resourceIdStr = resourcePath.Substring(commaIndex + 1);
112
113 if (int.TryParse(resourceIdStr, out int resourceId))
114 {
115 StringBuilder buffer = new StringBuilder(256);
116 IntPtr hModule = LoadLibraryEx(dllPath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
117 if (hModule != IntPtr.Zero)
118 {
119 try
120 {
121 int result = LoadString(hModule, (uint)Math.Abs(resourceId), buffer, buffer.Capacity);
122 if (result > 0)
123 {
124 return buffer.ToString();
125 }
126 }
127 finally
128 {
129 FreeLibrary(hModule);
130 }
131 }
132 }
133 }
134 }
135 catch
136 {
137 // Ignore errors resolving localized string
138 }
139 return localizedString;
140 }
141
142 /// <summary>
143 /// Returns a list of all installed Windows themes.
144 /// </summary>
145 /// <returns>A list of theme names (without the .theme extension).</returns>
146 public static List<string> GetInstalledThemes()
147 {
148 HashSet<string> themes = new HashSet<string>();
149
150 themes.UnionWith(s_themeDictionary.Keys);
151 themes.UnionWith(s_themeDisplayNameDictionary.Keys);
152
153 return themes.ToList();
154 }
155
156 /// <summary>
157 /// Gets the current Windows theme name.
158 /// </summary>
159 /// <returns>The current theme name, or null if it cannot be determined.</returns>
160 public static string GetCurrentTheme()
161 {
162 try
163 {
164 using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes"))
165 {
166 if (key != null)
167 {
168 string currentThemePath = key.GetValue("CurrentTheme") as string;
169 if (!string.IsNullOrEmpty(currentThemePath))
170 {
171 return Path.GetFileNameWithoutExtension(currentThemePath);
172 }
173 }
174 }
175 }
176 catch
177 {
178 // Ignore errors reading registry
179 }
180 return null;
181 }
182
183 /// <summary>
184 /// Applies a Windows theme by name.
185 /// </summary>
186 /// <param name="themeName">The name of the theme to apply (without .theme extension).</param>
187 /// <returns>True if the theme was applied successfully, false otherwise.</returns>
188 public static bool ApplyTheme(string themeName)
189 {
190 string themePath = FindThemePath(themeName);
191 if (string.IsNullOrEmpty(themePath))
192 {
193 return false;
194 }
195
196 try
197 {
198 string previous = GetCurrentTheme();
199
200 if (themeName.ToLowerInvariant() != "previous")
201 {
202 // Apply theme by opening the .theme file
203 Process p = Process.Start(themePath);
204 s_previousTheme = previous;
205
206 p.Exited += P_Exited;
207
208 return true;
209 }
210 else
211 {
212 bool success = RevertToPreviousTheme();
213
214 if (success)
215 {
216 s_previousTheme = previous;
217 }
218
219 return success;
220 }
221 }
222 catch
223 {
224 return false;
225 }
226 }
227
228 private static void P_Exited(object sender, EventArgs e)
229 {
230 Debug.WriteLine(((Process)sender).ExitCode);
231 }
232
233 /// <summary>
234 /// Reverts to the previous Windows theme.
235 /// </summary>
236 /// <returns>True if the previous theme was applied successfully, false otherwise.</returns>
237 public static bool RevertToPreviousTheme()
238 {
239 if (string.IsNullOrEmpty(s_previousTheme))
240 {
241 return false;
242 }
243
244 string themePath = FindThemePath(s_previousTheme);
245 if (string.IsNullOrEmpty(themePath))
246 {
247 return false;
248 }
249
250 try
251 {
252 Process.Start(themePath);
253 return true;
254 }
255 catch
256 {
257 return false;
258 }
259 }
260
261 /// <summary>
262 /// Gets the name of the previous theme.
263 /// </summary>
264 /// <returns>The previous theme name, or null if no theme change has been made.</returns>
265 public static string GetPreviousTheme()
266 {
267 return s_previousTheme;
268 }
269
270 /// <summary>
271 /// Finds the full path to a theme file by name or display name.
272 /// </summary>
273 /// <param name="themeName">The name of the theme (file name without extension or display name).</param>
274 /// <returns>The full path to the theme file, or null if not found.</returns>
275 private static string FindThemePath(string themeName)
276 {
277 // First check by file name
278 if (s_themeDictionary.TryGetValue(themeName, out string themePath))
279 {
280 return themePath;
281 }
282
283 // Then check by display name
284 if (s_themeDisplayNameDictionary.TryGetValue(themeName, out string fileNameFromDisplay))
285 {
286 if (s_themeDictionary.TryGetValue(fileNameFromDisplay, out string themePathFromDisplay))
287 {
288 return themePathFromDisplay;
289 }
290 }
291
292 return null;
293 }
294 }
295}
296