microsoft/TypeAgent

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
062ddd7af06b4ae80da2e7160128e0cc38eee3b8

Branches

Tags

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

Clone

HTTPS

Download ZIP

dotnet/autoShell/AutoShell.cs

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