microsoft/TypeAgent

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
c59afeb081afa02f7e1cb390adbe7254e1ef893e

Branches

Tags

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

Clone

HTTPS

Download ZIP

dotnet/autoShell/AutoShell.cs

464lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System;
5using System.Collections.Generic;
6using System.Diagnostics;
7using System.Linq;
8using System.Text;
9using System.Threading.Tasks;
10using System.Runtime.InteropServices;
11using System.IO;
12using System.Collections;
13using Newtonsoft.Json.Linq;
14using Microsoft.VisualBasic;
15using Microsoft.WindowsAPICodePack.Shell;
16using Newtonsoft.Json;
17
18
19namespace autoShell
20{
21 internal partial class AutoShell
22 {
23 // create a map of friendly names to executable paths
24 static Hashtable s_friendlyNameToPath = [];
25 static Hashtable s_friendlyNameToId = [];
26 static double s_savedVolumePct = 0.0;
27
28 static AutoShell()
29 {
30 // get current user name
31 string userName = Environment.UserName;
32 SortedList<string, string> sortedList = new SortedList<string, string>
33 {
34 { "chrome", "chrome.exe" },
35 { "power point", "C:\\Program Files\\Microsoft Office\\root\\Office16\\POWERPNT.EXE" },
36 { "powerpoint", "C:\\Program Files\\Microsoft Office\\root\\Office16\\POWERPNT.EXE" },
37 { "word", "C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE" },
38 { "winword", "C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE" },
39 { "excel", "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE" },
40 { "outlook", "C:\\Program Files\\Microsoft Office\\root\\Office16\\OUTLOOK.EXE" },
41 { "visual studio", "devenv.exe" },
42 { "visual studio code", "C:\\Users\\" + userName + "\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe" },
43 { "edge", "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" },
44 { "microsoft edge", "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" },
45 { "notepad", "C:\\Windows\\System32\\notepad.exe" },
46 { "paint", "mspaint.exe" },
47 { "calculator", "calc.exe" },
48 { "file explorer", "C:\\Windows\\explorer.exe" },
49 { "control panel", "C:\\Windows\\System32\\control.exe" },
50 { "task manager", "C:\\Windows\\System32\\Taskmgr.exe" },
51 { "cmd", "C:\\Windows\\System32\\cmd.exe" },
52 { "powershell", "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" },
53 { "snipping tool", "C:\\Windows\\System32\\SnippingTool.exe" },
54 { "magnifier", "C:\\Windows\\System32\\Magnify.exe" },
55 { "paint 3d", "C:\\Program Files\\WindowsApps\\Microsoft.MSPaint_10.1807.18022.0_x64__8wekyb3d8bbwe\\"},
56 { "m365 copilot", "C:\\Program Files\\WindowsApps\\Microsoft.MicrosoftOfficeHub_19.2512.45041.0_x64__8wekyb3d8bbwe\\M365Copilot.exe" },
57 { "copilot", "C:\\Program Files\\WindowsApps\\Microsoft.MicrosoftOfficeHub_19.2512.45041.0_x64__8wekyb3d8bbwe\\M365Copilot.exe" },
58 { "spotify", "C:\\Program Files\\WindowsApps\\SpotifyAB.SpotifyMusic_1.278.418.0_x64__zpdnekdrzrea0\\spotify.exe" },
59 };
60 // add the entries to the hashtable
61 foreach (var kvp in sortedList)
62 {
63 s_friendlyNameToPath.Add(kvp.Key, kvp.Value);
64 }
65
66 var installedApps = GetAllInstalledAppsIds();
67 foreach (var kvp in installedApps)
68 {
69 s_friendlyNameToId.Add(kvp.Key, kvp.Value);
70 }
71
72 // Load the installed themes
73 LoadThemes();
74 }
75
76 static SortedList<string, string> GetAllInstalledAppsIds()
77 {
78 // GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
79 var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
80 ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
81 var appIds = new SortedList<string, string>();
82
83 foreach (var app in (IKnownFolder)appsFolder)
84 {
85 string appName = app.Name.ToLowerInvariant();
86 if (appIds.ContainsKey(appName))
87 {
88 Debug.WriteLine("Key has multiple values: " + appName);
89 }
90 else
91 {
92 // The ParsingName property is the AppUserModelID
93 appIds.Add(appName, app.ParsingName);
94 }
95 }
96
97 return appIds;
98 }
99
100 static void SetMasterVolume(int pct)
101 {
102 // Using Windows Core Audio API via COM interop
103 try
104 {
105 var deviceEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
106 deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice device);
107 var audioEndpointVolumeGuid = typeof(IAudioEndpointVolume).GUID;
108 device.Activate(ref audioEndpointVolumeGuid, 0, IntPtr.Zero, out object obj);
109 var audioEndpointVolume = (IAudioEndpointVolume)obj;
110 audioEndpointVolume.GetMasterVolumeLevelScalar(out float currentVolume);
111 s_savedVolumePct = currentVolume * 100.0;
112 audioEndpointVolume.SetMasterVolumeLevelScalar(pct / 100.0f, Guid.Empty);
113 }
114 catch (Exception ex)
115 {
116 Debug.WriteLine("Failed to set volume: " + ex.Message);
117 }
118 }
119
120 static void RestoreMasterVolume()
121 {
122 // Using Windows Core Audio API via COM interop
123 try
124 {
125 var deviceEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
126 deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice device);
127 var audioEndpointVolumeGuid = typeof(IAudioEndpointVolume).GUID;
128 device.Activate(ref audioEndpointVolumeGuid, 0, IntPtr.Zero, out object obj);
129 var audioEndpointVolume = (IAudioEndpointVolume)obj;
130 audioEndpointVolume.SetMasterVolumeLevelScalar((float)(s_savedVolumePct / 100.0), Guid.Empty);
131 }
132 catch (Exception ex)
133 {
134 Debug.WriteLine("Failed to restore volume: " + ex.Message);
135 }
136 }
137
138 static void SetMasterMute(bool mute)
139 {
140 // Using Windows Core Audio API via COM interop
141 try
142 {
143 var deviceEnumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();
144 deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia, out IMMDevice device);
145 var audioEndpointVolumeGuid = typeof(IAudioEndpointVolume).GUID;
146 device.Activate(ref audioEndpointVolumeGuid, 0, IntPtr.Zero, out object obj);
147 var audioEndpointVolume = (IAudioEndpointVolume)obj;
148 audioEndpointVolume.GetMute(out bool currentMute);
149 Debug.WriteLine("Current Mute:" + currentMute);
150 audioEndpointVolume.SetMute(mute, Guid.Empty);
151 }
152 catch (Exception ex)
153 {
154 Debug.WriteLine("Failed to set mute: " + ex.Message);
155 }
156 }
157
158 static string ResolveProcessNameFromFriendlyName(string friendlyName)
159 {
160 string path = (string)s_friendlyNameToPath[friendlyName.ToLowerInvariant()];
161 if (path != null)
162 {
163 return Path.GetFileNameWithoutExtension(path);
164 }
165 else
166 {
167 return friendlyName;
168 }
169 }
170
171 // given part of a process name, raise the window of that process to the top level
172 static void RaiseWindow(string processName)
173 {
174 processName = ResolveProcessNameFromFriendlyName(processName);
175 Process[] processes = Process.GetProcessesByName(processName);
176 bool foundMatch = false;
177 // loop through the processes that match the name; raise the first one that has a main window
178 foreach (Process p in processes)
179 {
180 if (p.MainWindowHandle != IntPtr.Zero)
181 {
182 foundMatch = true;
183 SetForegroundWindow(p.MainWindowHandle);
184 Interaction.AppActivate(p.Id);
185 break;
186 }
187 }
188
189 if (!foundMatch)
190 {
191 // this means all the applications processes are running in the background. This happens for edge and chrome browsers.
192 string path = (string)s_friendlyNameToPath[processName];
193 if (path != null)
194 {
195 Process.Start(path);
196 }
197 }
198 }
199
200 static void MaximizeWindow(string processName)
201 {
202 processName = ResolveProcessNameFromFriendlyName(processName);
203 Process[] processes = Process.GetProcessesByName(processName);
204 // loop through the processes that match the name; raise the first one that has a main window
205 foreach (Process p in processes)
206 {
207 if (p.MainWindowHandle != IntPtr.Zero)
208 {
209 uint WM_SYSCOMMAND = 0x112;
210 uint SC_MAXIMIZE = 0xf030;
211 SendMessage(p.MainWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, IntPtr.Zero);
212 SetForegroundWindow(p.MainWindowHandle);
213 Interaction.AppActivate(p.Id);
214 break;
215 }
216 }
217 }
218
219 static void MinimizeWindow(string processName)
220 {
221 processName = ResolveProcessNameFromFriendlyName(processName);
222 Process[] processes = Process.GetProcessesByName(processName);
223 // loop through the processes that match the name; raise the first one that has a main window
224 foreach (Process p in processes)
225 {
226 if (p.MainWindowHandle != IntPtr.Zero)
227 {
228 uint WM_SYSCOMMAND = 0x112;
229 uint SC_MINIMIZE = 0xF020;
230 SendMessage(p.MainWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, IntPtr.Zero);
231 break;
232 }
233 }
234 }
235
236 static void TileWindowPair(string processName1, string processName2)
237 {
238 // find both processes
239 // TODO: Update this to account for UWP apps (e.g. calculator). UWPs are hosted by ApplicationFrameHost.exe
240 processName1 = ResolveProcessNameFromFriendlyName(processName1);
241 Process[] processes1 = Process.GetProcessesByName(processName1);
242 IntPtr hWnd1 = IntPtr.Zero;
243 IntPtr hWnd2 = IntPtr.Zero;
244 int pid1 = -1;
245 int pid2 = -1;
246
247 foreach (Process p in processes1)
248 {
249 if (p.MainWindowHandle != IntPtr.Zero)
250 {
251 hWnd1 = p.MainWindowHandle;
252 pid1 = p.Id;
253 break;
254 }
255 }
256 processName2 = ResolveProcessNameFromFriendlyName(processName2);
257 Process[] processes2 = Process.GetProcessesByName(processName2);
258 foreach (Process p in processes2)
259 {
260 if (p.MainWindowHandle != IntPtr.Zero)
261 {
262 hWnd2 = p.MainWindowHandle;
263 pid2 = p.Id;
264 break;
265 }
266 }
267 if (hWnd1 != IntPtr.Zero && hWnd2 != IntPtr.Zero)
268 {
269 // TODO: handle multiple monitors
270 // get the screen size
271 IntPtr desktopHandle = GetDesktopWindow();
272 RECT desktopRect = new RECT();
273 GetWindowRect(desktopHandle, ref desktopRect);
274 // get the dimensions of the taskbar
275 // find the taskbar window
276 IntPtr taskbarHandle = IntPtr.Zero;
277 IntPtr hWnd = IntPtr.Zero;
278 while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "Shell_TrayWnd", null)) != IntPtr.Zero)
279 {
280 // find the taskbar window's child
281 taskbarHandle = FindWindowEx(hWnd, IntPtr.Zero, "ReBarWindow32", null);
282 if (taskbarHandle != IntPtr.Zero)
283 {
284 break;
285 }
286 }
287 if (hWnd == IntPtr.Zero)
288 {
289 Debug.WriteLine("Taskbar not found");
290 return;
291 }
292 else
293 {
294 RECT taskbarRect = new RECT();
295 GetWindowRect(hWnd, ref taskbarRect);
296 Debug.WriteLine("Taskbar Rect: " + taskbarRect.Left + ", " + taskbarRect.Top + ", " + taskbarRect.Right + ", " + taskbarRect.Bottom);
297 // TODO: handle left, top, right and nonexistant taskbars
298 // subtract the taskbar height from the screen height
299 desktopRect.Bottom -= (int)((taskbarRect.Bottom - taskbarRect.Top) / 2);
300 }
301 // set the window positions using the shellRect and making sure the windows are visible
302 int halfwidth = (desktopRect.Right - desktopRect.Left) / 2;
303 IntPtr HWND_TOP = IntPtr.Zero;
304 uint showWindow = 0x40;
305 SetWindowPos(hWnd1, HWND_TOP, desktopRect.Left, desktopRect.Top, halfwidth, desktopRect.Bottom, showWindow);
306 SetForegroundWindow(hWnd1);
307 Interaction.AppActivate(pid1);
308 SetWindowPos(hWnd2, HWND_TOP, desktopRect.Left + halfwidth, desktopRect.Top, halfwidth, desktopRect.Bottom, showWindow);
309 SetForegroundWindow(hWnd2);
310 Interaction.AppActivate(pid2);
311 }
312 }
313
314 // given a friendly name, check if it's running and if not, start it; if it's running raise it to the top level
315 static void OpenApplication(string friendlyName)
316 {
317 // check to see if the application is running
318 Process[] processes = Process.GetProcessesByName(friendlyName);
319 if (processes.Length == 0)
320 {
321 // if not, start it
322 Debug.WriteLine("Starting " + friendlyName);
323 string path = (string)s_friendlyNameToPath[friendlyName.ToLowerInvariant()];
324 if (path != null)
325 {
326 try
327 {
328 Process.Start(path);
329 }
330 catch { }
331 }
332 else
333 {
334 string appModelUserID = (string)s_friendlyNameToId[friendlyName.ToLowerInvariant()];
335 if (appModelUserID != null)
336 {
337 try
338 {
339 Process.Start("explorer.exe", @" shell:appsFolder\" + appModelUserID);
340 }
341 catch { }
342 }
343 }
344 }
345 else
346 {
347 // if so, raise it to the top level
348 Debug.WriteLine("Raising " + friendlyName);
349 RaiseWindow(friendlyName);
350 }
351 }
352
353 // close application
354 static void CloseApplication(string friendlyName)
355 {
356 // check to see if the application is running
357 string processName = ResolveProcessNameFromFriendlyName(friendlyName);
358 Process[] processes = Process.GetProcessesByName(processName);
359 if (processes.Length != 0)
360 {
361 // if so, close it
362 Debug.WriteLine("Closing " + friendlyName);
363 foreach (Process p in processes)
364 {
365 if (p.MainWindowHandle != IntPtr.Zero)
366 {
367 p.CloseMainWindow();
368 }
369 }
370 }
371 }
372
373 private static void SetDesktopWallpaper(string imagePath)
374 {
375 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, imagePath, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
376 }
377
378 static bool execLine(string line)
379 {
380 var quit = false;
381 // parse the line as a json object with one or more command keys (with values as parameters)
382 JObject root = JObject.Parse(line);
383 foreach (var kvp in root)
384 {
385 string key = kvp.Key;
386 string value = kvp.Value.ToString();
387 switch (key)
388 {
389 case "launchProgram":
390 OpenApplication(value);
391 break;
392 case "closeProgram":
393 CloseApplication(value);
394 break;
395 case "maximize":
396 MaximizeWindow(value);
397 break;
398 case "minimize":
399 MinimizeWindow(value);
400 break;
401 case "switchTo":
402 RaiseWindow(value);
403 break;
404 case "quit":
405 quit = true;
406 break;
407 case "tile":
408 string[] apps = value.Split(',');
409 if (apps.Length == 2)
410 {
411 TileWindowPair(apps[0], apps[1]);
412 }
413 break;
414 case "volume":
415 int pct = 0;
416 if (int.TryParse(value, out pct))
417 {
418 SetMasterVolume(pct);
419 }
420 break;
421 case "restoreVolume":
422 RestoreMasterVolume();
423 break;
424 case "mute":
425 bool mute = false;
426 if (bool.TryParse(value, out mute))
427 {
428 SetMasterMute(mute);
429 }
430 break;
431 case "listAppNames":
432 var installedApps = GetAllInstalledAppsIds();
433 Console.WriteLine(JsonConvert.SerializeObject(installedApps.Keys));
434 break;
435 case "setWallpaper":
436 SetDesktopWallpaper(value);
437 break;
438 case "applyTheme":
439 bool result = ApplyTheme(value);
440 break;
441 case "listThemes":
442 var themes = GetInstalledThemes();
443 Console.WriteLine(JsonConvert.SerializeObject(themes));
444 break;
445 default:
446 Debug.WriteLine("Unknown command: " + key);
447 break;
448 }
449 }
450 return quit;
451 }
452
453 static void Main(string[] args)
454 {
455 bool quit = false;
456 while (!quit)
457 {
458 // read a line from the console
459 string line = Console.ReadLine();
460 quit = execLine(line);
461 }
462 }
463 }
464}
465