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.cs

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