microsoft/TypeAgent

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ec1a127c43e511cfaf5bdb778f936623a42ef74b

Branches

Tags

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

Clone

HTTPS

Download ZIP

dotnet/autoShell/AutoShell.cs

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