Script for automating a large assortment of AME related actions
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1088 lines
64 KiB

9 months ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. using System.Threading;
  11. using Ameliorated.ConsoleUtils;
  12. using Microsoft.Dism;
  13. using Microsoft.Win32;
  14. using static Ameliorated.ConsoleUtils.Menu;
  15. namespace amecs.Actions
  16. {
  17. public class Languages
  18. {
  19. public static bool ShowMenu()
  20. {
  21. while (true)
  22. {
  23. Program.Frame.Clear();
  24. var mainMenu = new Ameliorated.ConsoleUtils.Menu()
  25. {
  26. Choices =
  27. {
  28. true
  29. ? new Menu.MenuItem("Change Display Language", new Func<bool>(ShowDisplayMenu))
  30. {
  31. IsEnabled = false,
  32. SecondaryText = "[Not Supported]",
  33. SecondaryTextForeground = ConsoleColor.Red,
  34. PrimaryTextForeground = ConsoleColor.DarkGray
  35. }
  36. : new Menu.MenuItem("Change Display Language", new Func<bool>(ShowDisplayMenu)),
  37. new Menu.MenuItem("Add Keyboard Language", new Func<bool>(ShowAddKeyboardLanguageMenu)),
  38. new Menu.MenuItem("Remove Keyboard Language", new Func<bool>(ShowRemoveKeyboardLanguageMenu)),
  39. Globals.WinVer >= 22000
  40. ? new Menu.MenuItem("Install Language Pack", new Func<bool>(ShowLanguagePackMenu))
  41. {
  42. IsEnabled = false,
  43. SecondaryText = "[Not Supported]",
  44. SecondaryTextForeground = ConsoleColor.Red,
  45. PrimaryTextForeground = ConsoleColor.DarkGray
  46. }
  47. : new Menu.MenuItem("Install Language Pack", new Func<bool>(ShowLanguagePackMenu)),
  48. Globals.WinVer >= 22000
  49. ? new Menu.MenuItem("Uninstall Language Pack", new Func<bool>(ShowRemoveLanguagePackMenu))
  50. {
  51. IsEnabled = false,
  52. SecondaryText = "[Not Supported]",
  53. SecondaryTextForeground = ConsoleColor.Red,
  54. PrimaryTextForeground = ConsoleColor.DarkGray
  55. }
  56. : new Menu.MenuItem("Uninstall Language Pack", new Func<bool>(ShowRemoveLanguagePackMenu)),
  57. Menu.MenuItem.Blank,
  58. new Menu.MenuItem("Return to Menu", null),
  59. new Menu.MenuItem("Exit", new Func<bool>(Globals.Exit))
  60. },
  61. SelectionForeground = ConsoleColor.Green
  62. };
  63. Func<bool> result;
  64. try
  65. {
  66. mainMenu.Write();
  67. var res = mainMenu.Load();
  68. if (res == null)
  69. return true;
  70. result = (Func<bool>)res;
  71. } catch (Exception e)
  72. {
  73. Console.WriteLine(e);
  74. Console.ReadLine();
  75. return false;
  76. }
  77. try
  78. {
  79. result.Invoke();
  80. } catch (Exception e)
  81. {
  82. ConsoleTUI.ShowErrorBox("Error while loading a menu: " + e.ToString(), null);
  83. }
  84. }
  85. }
  86. private static bool ShowDisplayMenu() => ShowLanguageMenu(true);
  87. private static bool ShowLanguagePackMenu() => ShowLanguageMenu(false);
  88. private static List<string> currentLanguagePacks;
  89. public static bool ShowLanguageMenu(bool display)
  90. {
  91. /*
  92. if (display)
  93. {
  94. try
  95. {
  96. var deskKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\Desktop");
  97. var pendingLang = ((string[])deskKey.GetValue("PreferredUILanguagesPending")).First();
  98. ConsoleTUI.OpenFrame.WriteCentered("Checking languages");
  99. var indicator = new ConsoleUtils.LoadingIndicator(true);
  100. Thread.Sleep(50);
  101. indicator.Dispose();
  102. Console.WriteLine();
  103. ConsoleTUI.OpenFrame.Close("A language change is currently pending.\r\nYou must restart before continuing.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  104. {
  105. AnyKey = true,
  106. Text = "Press any key to return to the Menu: "
  107. });
  108. return false;
  109. } catch {}
  110. }
  111. */
  112. List<MenuItem> displayItems = new List<MenuItem>(LanguagePackItems);
  113. if (!display)
  114. {
  115. DismApi.Initialize(DismLogLevel.LogErrors);
  116. DismPackageCollection packages;
  117. using (var session = DismApi.OpenOnlineSession())
  118. {
  119. packages = DismApi.GetPackages(session);
  120. session.Close();
  121. }
  122. DismApi.Shutdown();
  123. currentLanguagePacks = new List<string>(packages.Where(x => x.PackageName.StartsWith("Microsoft-Windows-Client-LanguagePack-Package")).Select(x => x.PackageName));
  124. }
  125. foreach (var menuItem in new List<MenuItem>(displayItems))
  126. {
  127. var tag = ((string)menuItem.ReturnValue).Split('|').First();
  128. string activeLang = null;
  129. string pendingLang = null;
  130. try
  131. {
  132. var deskKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\Desktop");
  133. activeLang = ((string[])deskKey.GetValue("PreferredUILanguages")).First();
  134. pendingLang = ((string[])deskKey.GetValue("PreferredUILanguagesPending")).First();
  135. } catch
  136. {
  137. }
  138. if (display && tag == pendingLang && activeLang != pendingLang)
  139. {
  140. var clone = menuItem.Clone();
  141. clone.IsEnabled = false;
  142. clone.PrimaryTextForeground = ConsoleColor.DarkGray;
  143. clone.SecondaryText = "[Pending Restart]";
  144. clone.SecondaryTextForeground = ConsoleColor.Yellow;
  145. displayItems.ReplaceItem(menuItem, clone);
  146. }
  147. if (display && tag == activeLang && (activeLang == pendingLang || pendingLang == null))
  148. {
  149. var clone = menuItem.Clone();
  150. clone.IsEnabled = false;
  151. clone.PrimaryTextForeground = ConsoleColor.DarkGray;
  152. clone.SecondaryText = "[Current]";
  153. clone.SecondaryTextForeground = ConsoleColor.Yellow;
  154. displayItems.ReplaceItem(menuItem, clone);
  155. }
  156. if (!display)
  157. {
  158. if (currentLanguagePacks.Any(x => x.Contains('~' + tag + '~')))
  159. {
  160. var clone = menuItem.Clone();
  161. clone.IsEnabled = false;
  162. clone.PrimaryTextForeground = ConsoleColor.DarkGray;
  163. clone.SecondaryText = "[Installed]";
  164. clone.SecondaryTextForeground = ConsoleColor.Yellow;
  165. displayItems.ReplaceItem(menuItem, clone);
  166. }
  167. }
  168. }
  169. var result = amecs.ShowDefaultMenu(displayItems.ToList());
  170. if (result.GetType().Name.StartsWith("Func")) return ((Func<bool>)result).Invoke();
  171. var languageTag = ((string)result).Split('|').First();
  172. var downloadAmount = int.Parse(((string)result).Split('|').Last(), CultureInfo.InvariantCulture);
  173. if (!display)
  174. {
  175. return InstallLanguagePack(downloadAmount, languageTag);
  176. }
  177. else
  178. {
  179. return ChangeDisplayLanguage(downloadAmount, languageTag);
  180. }
  181. }
  182. private static List<string> CurrentLanguages;
  183. public static bool ShowAddKeyboardLanguageMenu()
  184. {
  185. while (true)
  186. {
  187. CurrentLanguages = new List<string>();
  188. try
  189. {
  190. var langsKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\International\\User Profile");
  191. foreach (var langKey in langsKey.GetSubKeyNames())
  192. {
  193. CurrentLanguages.AddRange(langsKey.OpenSubKey(langKey).GetValueNames().Where(x => x.Contains(':')));
  194. }
  195. } catch
  196. {
  197. }
  198. List<MenuItem> displayItems = new List<MenuItem>(KeyboardLanguageItems);
  199. foreach (var menuItem in displayItems.Where(x => x.ReturnValue.GetType().Name == "String").ToList())
  200. {
  201. if (CurrentLanguages.Contains((string)menuItem.ReturnValue, StringComparer.InvariantCultureIgnoreCase))
  202. {
  203. var clone = menuItem.Clone();
  204. clone.IsEnabled = false;
  205. clone.PrimaryTextForeground = ConsoleColor.DarkGray;
  206. clone.SecondaryText = "[Installed]";
  207. clone.SecondaryTextForeground = ConsoleColor.Yellow;
  208. displayItems.ReplaceItem(menuItem, clone);
  209. }
  210. }
  211. var result = amecs.ShowDefaultMenu(displayItems);
  212. if (result.GetType().Name.StartsWith("Func")) return ((Func<bool>)result).Invoke();
  213. if (result.GetType().Name == "String")
  214. {
  215. return AddKeyboardLanguage((string)result);
  216. }
  217. else
  218. {
  219. var subDisplayItems = ((MenuItem[])result).ToList();
  220. foreach (var menuItem in subDisplayItems)
  221. {
  222. if (CurrentLanguages.Contains((string)menuItem.ReturnValue, StringComparer.InvariantCultureIgnoreCase))
  223. {
  224. menuItem.IsEnabled = false;
  225. menuItem.PrimaryTextForeground = ConsoleColor.DarkGray;
  226. menuItem.SecondaryText = "[Installed]";
  227. menuItem.SecondaryTextForeground = ConsoleColor.Yellow;
  228. }
  229. else
  230. {
  231. menuItem.IsEnabled = true;
  232. menuItem.PrimaryTextForeground = null;
  233. menuItem.SecondaryText = null;
  234. menuItem.SecondaryTextForeground = null;
  235. }
  236. }
  237. var subResult = amecs.ShowDefaultMenu(subDisplayItems);
  238. if (subResult.GetType().Name.StartsWith("Func"))
  239. {
  240. ((Func<bool>)subResult).Invoke();
  241. continue;
  242. }
  243. return AddKeyboardLanguage((string)subResult);
  244. }
  245. }
  246. }
  247. public static bool AddKeyboardLanguage(string tip)
  248. {
  249. var choice = new ChoicePrompt()
  250. {
  251. Text = "Make default keyboard language? (Y/N): "
  252. }.Start();
  253. if (!choice.HasValue) return true;
  254. bool makeDefault = choice == 0;
  255. try
  256. {
  257. ConsoleTUI.OpenFrame.WriteCentered("\r\nAdding keyboard language");
  258. using (new ConsoleUtils.LoadingIndicator(true))
  259. {
  260. var result = NSudo.RunProcessAsUser(NSudo.GetUserToken(), "PowerShell.exe", makeDefault ? $"-NoP -C \"$List=Get-WinUserLanguageList;" + $"$List[0].InputMethodTips.Add('{tip}');" + $"Set-WinUserLanguageList $List -Force;" + $"Set-WinDefaultInputMethodOverride -InputTip '{tip}'\"" : $"-NoP -C \"$List=Get-WinUserLanguageList;" + $"$List[0].InputMethodTips.Add('{tip}');" + $"Set-WinUserLanguageList $List -Force\"");
  261. if (result == null)
  262. {
  263. throw new Exception("Could not start user PowerShell process.");
  264. }
  265. if (result > 0)
  266. {
  267. throw new Exception("PowerShell exited with a non-zero exitcode.");
  268. }
  269. }
  270. } catch (Exception e)
  271. {
  272. Console.WriteLine();
  273. ConsoleTUI.OpenFrame.Close("Error: " + e.Message.TrimEnd('\n').TrimEnd('\r'), ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  274. {
  275. AnyKey = true,
  276. Text = "Press any key to return to the Menu: "
  277. });
  278. return false;
  279. }
  280. Console.WriteLine();
  281. ConsoleTUI.OpenFrame.Close($"Keyboard language added successfully", ConsoleColor.Green, Console.BackgroundColor, new ChoicePrompt()
  282. {
  283. AnyKey = true,
  284. Text = "Press any key to return to the Menu: "
  285. });
  286. return true;
  287. }
  288. [StructLayout(LayoutKind.Sequential)]
  289. public struct COORD
  290. {
  291. public short X;
  292. public short Y;
  293. public COORD(short X, short Y)
  294. {
  295. this.X = X;
  296. this.Y = Y;
  297. }
  298. };
  299. [DllImport("kernel32.dll", SetLastError = true)]
  300. internal static extern bool WriteConsoleOutputCharacter(IntPtr hConsoleOutput, StringBuilder lpCharacter, uint nLength, COORD dwWriteCoord, out uint lpNumberOfCharsWritten);
  301. [DllImport("kernel32.dll", SetLastError = true)]
  302. static extern IntPtr GetStdHandle(int nStdHandle);
  303. private static bool InstallLanguagePack(int downloadAmount, string language) => InstallLanguage(downloadAmount, language, true);
  304. private static bool InstallLanguage(int downloadAmount, string language, bool finalize)
  305. {
  306. var downloadFolder = Directory.Exists(Path.Combine(Globals.UserFolder, "Desktop")) ? Path.Combine(Globals.UserFolder, "Desktop") : Environment.GetEnvironmentVariable("TEMP");
  307. var file = "LangPacks-" + new Random().Next(0, 9999);
  308. try
  309. {
  310. try
  311. {
  312. DriveInfo drive = DriveInfo.GetDrives().First(x => x.Name == Environment.GetEnvironmentVariable("SYSTEMDRIVE") + "\\");
  313. if (drive.AvailableFreeSpace < downloadAmount * 2.3)
  314. {
  315. Console.WriteLine();
  316. ConsoleTUI.OpenFrame.Close("There is not enough available space on the system drive.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  317. {
  318. AnyKey = true,
  319. Text = "Press any key to return to the Menu: "
  320. });
  321. return false;
  322. }
  323. } catch { }
  324. string amountString = "0";
  325. if (downloadAmount == 2480000) amountString = "2.5";
  326. if (downloadAmount == 2900000) amountString = "2.9";
  327. if (downloadAmount == 3230000) amountString = "3.2";
  328. var choice = new ChoicePrompt()
  329. {
  330. Text = $"A ~{amountString}GB Language Packs ISO must be downloaded\r\nContinue? (Y/N): "
  331. }.Start();
  332. if (choice == null || choice == 1) return false;
  333. if (!amecs.IsInternetAvailable())
  334. {
  335. Console.WriteLine();
  336. ConsoleTUI.OpenFrame.Close("An internet connection is required for this action.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  337. {
  338. AnyKey = true,
  339. Text = "Press any key to return to the Menu: "
  340. });
  341. return false;
  342. }
  343. var sevenZip = ExistsInPath("7z.exe");
  344. sevenZip = sevenZip == null ? File.Exists(Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%\7-Zip\7z.exe")) ? Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%\7-Zip\7z.exe") : null : sevenZip;
  345. var choco = ExistsInPath("choco.exe");
  346. choco = choco == null ? File.Exists(Environment.ExpandEnvironmentVariables(@"%PROGRAMDATA%\chocolatey\choco.exe")) ? Environment.ExpandEnvironmentVariables(@"%PROGRAMDATA%\chocolatey\choco.exe") : null : choco;
  347. if (String.IsNullOrEmpty(sevenZip) && String.IsNullOrEmpty(choco))
  348. {
  349. Console.WriteLine();
  350. ConsoleTUI.OpenFrame.Close("7zip or Chocolatey must be installed for this action.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  351. {
  352. AnyKey = true,
  353. Text = "Press any key to return to the Menu: "
  354. });
  355. return false;
  356. }
  357. bool wasMissing = false;
  358. if (String.IsNullOrEmpty(sevenZip))
  359. {
  360. wasMissing = true;
  361. ConsoleTUI.OpenFrame.WriteCentered("\r\nInstalling 7zip");
  362. using (new ConsoleUtils.LoadingIndicator(true))
  363. {
  364. var proc = new Process();
  365. var startInfo = new ProcessStartInfo
  366. {
  367. CreateNoWindow = true,
  368. UseShellExecute = false,
  369. WindowStyle = ProcessWindowStyle.Normal,
  370. Arguments = "install -y --force --allow-empty-checksums 7zip",
  371. FileName = choco,
  372. };
  373. proc.StartInfo = startInfo;
  374. proc.Start();
  375. proc.WaitForExit();
  376. }
  377. sevenZip = ExistsInPath("7z.exe");
  378. sevenZip = sevenZip == null ? File.Exists(Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%\7-Zip\7z.exe")) ? Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES%\7-Zip\7z.exe") : null : sevenZip;
  379. if (sevenZip == null)
  380. {
  381. ConsoleTUI.OpenFrame.WriteCentered("\r\nUninstalling 7zip");
  382. using (new ConsoleUtils.LoadingIndicator(true))
  383. {
  384. var proc = new Process();
  385. var startInfo = new ProcessStartInfo
  386. {
  387. CreateNoWindow = true,
  388. UseShellExecute = false,
  389. WindowStyle = ProcessWindowStyle.Normal,
  390. Arguments = "uninstall 7zip -y --force-dependencies --allow-empty-checksums",
  391. FileName = choco,
  392. };
  393. proc.StartInfo = startInfo;
  394. proc.Start();
  395. proc.WaitForExit();
  396. }
  397. Console.WriteLine();
  398. ConsoleTUI.OpenFrame.Close("Could not detect 7zip.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  399. {
  400. AnyKey = true,
  401. Text = "Press any key to return to the Menu: "
  402. });
  403. return false;
  404. }
  405. }
  406. try
  407. {
  408. ConsoleTUI.OpenFrame.WriteCenteredLine("\r\nDownload progress");
  409. using (WebClientEx wc = new WebClientEx(0, (long)downloadAmount * 1000))
  410. {
  411. var stdout = GetStdHandle(-11);
  412. var maxHashTags = (ConsoleTUI.OpenFrame.DisplayWidth - 5);
  413. wc.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
  414. {
  415. var currentHashTags = (int)Math.Ceiling(Math.Min(((double)e.ProgressPercentage / 100) * maxHashTags, maxHashTags));
  416. var spaces = maxHashTags - currentHashTags + (4 - e.ProgressPercentage.ToString().Length);
  417. var sb = new StringBuilder(new string('#', currentHashTags) + new string(' ', spaces) + e.ProgressPercentage + "%");
  418. uint throwaway;
  419. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out throwaway);
  420. };
  421. var task = wc.DownloadFileTaskAsync(new Uri("https://software-download.microsoft.com/download/pr/19041.1.191206-1406.vb_release_CLIENTLANGPACKDVD_OEM_MULTI.iso"), Path.Combine(downloadFolder, file + ".ISO"));
  422. task.Wait();
  423. Thread.Sleep(100);
  424. var sb = new StringBuilder(new string('#', maxHashTags) + " 100%");
  425. uint throwaway;
  426. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out throwaway);
  427. }
  428. ConsoleTUI.OpenFrame.WriteCentered("\r\n\r\nExtracting ISO");
  429. using (new ConsoleUtils.LoadingIndicator(true))
  430. {
  431. var proc = new Process();
  432. var startInfo = new ProcessStartInfo
  433. {
  434. CreateNoWindow = true,
  435. UseShellExecute = false,
  436. WindowStyle = ProcessWindowStyle.Normal,
  437. Arguments = $@"e -y -o""{Path.Combine(downloadFolder, file)}"" ""{Path.Combine(downloadFolder, file + ".ISO")}"" x64\langpacks\*.cab",
  438. FileName = sevenZip,
  439. };
  440. proc.StartInfo = startInfo;
  441. proc.Start();
  442. proc.WaitForExit();
  443. }
  444. try
  445. {
  446. File.Delete(Path.Combine(downloadFolder, file + ".ISO"));
  447. } catch
  448. {
  449. }
  450. } catch (Exception e)
  451. {
  452. try
  453. {
  454. if (wasMissing)
  455. {
  456. ConsoleTUI.OpenFrame.WriteCentered("\r\nUninstalling 7zip");
  457. using (new ConsoleUtils.LoadingIndicator(true))
  458. {
  459. var proc = new Process();
  460. var startInfo = new ProcessStartInfo
  461. {
  462. CreateNoWindow = true,
  463. UseShellExecute = false,
  464. WindowStyle = ProcessWindowStyle.Normal,
  465. Arguments = "uninstall 7zip -y --force-dependencies --allow-empty-checksums",
  466. FileName = choco,
  467. };
  468. proc.StartInfo = startInfo;
  469. proc.Start();
  470. proc.WaitForExit();
  471. }
  472. }
  473. } catch
  474. {
  475. }
  476. Console.WriteLine();
  477. ConsoleTUI.OpenFrame.Close("Error: " + e.Message.TrimEnd('\n').TrimEnd('\r'), ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  478. {
  479. AnyKey = true,
  480. Text = "Press any key to return to the Menu: "
  481. });
  482. return false;
  483. }
  484. if (wasMissing)
  485. {
  486. ConsoleTUI.OpenFrame.WriteCentered("\r\nUninstalling 7zip");
  487. using (new ConsoleUtils.LoadingIndicator(true))
  488. {
  489. var proc = new Process();
  490. var startInfo = new ProcessStartInfo
  491. {
  492. CreateNoWindow = true,
  493. UseShellExecute = false,
  494. WindowStyle = ProcessWindowStyle.Normal,
  495. Arguments = "uninstall 7zip -y --force-dependencies --allow-empty-checksums",
  496. FileName = choco,
  497. };
  498. proc.StartInfo = startInfo;
  499. proc.Start();
  500. proc.WaitForExit();
  501. }
  502. }
  503. var cab = Path.Combine(downloadFolder, file, $"Microsoft-Windows-Client-Language-Pack_x64_{language}.cab");
  504. if (!File.Exists(cab))
  505. {
  506. try
  507. {
  508. Directory.Delete(Path.Combine(downloadFolder, file));
  509. } catch
  510. {
  511. }
  512. throw new Exception("Extraction failed.");
  513. }
  514. ConsoleTUI.OpenFrame.WriteCentered("\r\nInstalling language pack");
  515. var topCache = Console.CursorTop;
  516. var leftCache = Console.CursorLeft;
  517. Console.WriteLine();
  518. bool inProgress = false;
  519. try
  520. {
  521. using (var indicator = new ConsoleUtils.LoadingIndicator(true))
  522. {
  523. DismApi.Initialize(DismLogLevel.LogErrors);
  524. using (var session = DismApi.OpenOnlineSession())
  525. {
  526. var stdout = GetStdHandle(-11);
  527. bool indicatorStopped = false;
  528. var maxHashTags = (ConsoleTUI.OpenFrame.DisplayWidth - 5);
  529. DismApi.AddPackage(session, cab, false, true, delegate(DismProgress progress)
  530. {
  531. inProgress = true;
  532. if (!indicatorStopped)
  533. {
  534. indicator.Stop();
  535. Console.SetCursorPosition(leftCache, topCache);
  536. Console.WriteLine(" ");
  537. }
  538. indicatorStopped = true;
  539. var progressPerc = progress.Current / 10;
  540. var currentHashTags = (int)Math.Ceiling(Math.Min(((double)progressPerc / 100) * maxHashTags, maxHashTags));
  541. var spaces = maxHashTags - currentHashTags + (4 - progressPerc.ToString().Length);
  542. var sb = new StringBuilder(new string('#', currentHashTags) + new string(' ', spaces) + progressPerc + "%");
  543. uint throwaway;
  544. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)topCache), out throwaway);
  545. inProgress = false;
  546. });
  547. session.Close();
  548. Thread.Sleep(100);
  549. var sb = new StringBuilder(new string('#', maxHashTags) + " 100%");
  550. uint throwaway;
  551. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)topCache), out throwaway);
  552. }
  553. DismApi.Shutdown();
  554. }
  555. } catch (Exception e)
  556. {
  557. while (inProgress)
  558. {
  559. Thread.Sleep(50);
  560. }
  561. try
  562. {
  563. Directory.Delete(Path.Combine(downloadFolder, file), true);
  564. } catch
  565. {
  566. }
  567. Console.WriteLine();
  568. ConsoleTUI.OpenFrame.Close("DISM error: " + e.Message, ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  569. {
  570. AnyKey = true,
  571. Text = "Press any key to return to the Menu: "
  572. });
  573. return false;
  574. }
  575. Directory.Delete(Path.Combine(downloadFolder, file), true);
  576. } catch (Exception e)
  577. {
  578. try
  579. {
  580. Directory.Delete(Path.Combine(downloadFolder, file), true);
  581. } catch
  582. {
  583. }
  584. Console.WriteLine();
  585. ConsoleTUI.OpenFrame.Close("Error: " + e.Message.TrimEnd('\n').TrimEnd('\r'), ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  586. {
  587. AnyKey = true,
  588. Text = "Press any key to return to the Menu: "
  589. });
  590. return false;
  591. }
  592. if (finalize)
  593. {
  594. Console.WriteLine();
  595. ConsoleTUI.OpenFrame.Close("Language pack installed successfully", ConsoleColor.Green, Console.BackgroundColor, new ChoicePrompt()
  596. {
  597. AnyKey = true,
  598. Text = "Press any key to return to the Menu: "
  599. });
  600. }
  601. return true;
  602. }
  603. public static string ExistsInPath(string fileName)
  604. {
  605. if (File.Exists(fileName)) return fileName;
  606. var values = Environment.GetEnvironmentVariable("PATH");
  607. foreach (var path in values.Split(Path.PathSeparator).Where(x => !x.Contains("chocolatey\\bin")))
  608. {
  609. var fullPath = Path.Combine(path, fileName);
  610. if (File.Exists(fullPath)) return fullPath;
  611. if (!fileName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) && File.Exists(fullPath + ".exe")) return fullPath + ".exe";
  612. }
  613. return null;
  614. }
  615. public static bool ShowRemoveKeyboardLanguageMenu()
  616. {
  617. CurrentLanguages = new List<string>();
  618. try
  619. {
  620. var langsKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\International\\User Profile");
  621. foreach (var langKey in langsKey.GetSubKeyNames())
  622. {
  623. CurrentLanguages.AddRange(langsKey.OpenSubKey(langKey).GetValueNames().Where(x => x.Contains(':')));
  624. }
  625. } catch
  626. {
  627. throw new Exception("Could not fetch installed keyboard languages.\r\n");
  628. }
  629. List<MenuItem> displayItems = new List<MenuItem>();
  630. foreach (var menuItem in KeyboardLanguageItems.Where(x => x.ReturnValue.GetType().Name == "String"))
  631. {
  632. if (CurrentLanguages.Contains((string)menuItem.ReturnValue, StringComparer.InvariantCultureIgnoreCase))
  633. {
  634. displayItems.Add(menuItem);
  635. }
  636. }
  637. foreach (var menuItemList in KeyboardLanguageItems.Where(x => x.ReturnValue.GetType().Name != "String").Select(x => x.ReturnValue))
  638. {
  639. var subDisplayItems = ((MenuItem[])menuItemList).ToList();
  640. foreach (var subMenuItem in subDisplayItems)
  641. {
  642. if (CurrentLanguages.Contains((string)subMenuItem.ReturnValue, StringComparer.InvariantCultureIgnoreCase))
  643. {
  644. displayItems.Add(subMenuItem);
  645. }
  646. }
  647. }
  648. var result = amecs.ShowDefaultMenu(displayItems);
  649. if (result.GetType().Name.StartsWith("Func")) return ((Func<bool>)result).Invoke();
  650. return RemoveKeyboardLanguage((string)result);
  651. }
  652. private static bool RemoveKeyboardLanguage(string language)
  653. {
  654. ConsoleTUI.OpenFrame.WriteCentered("Removing keyboard language");
  655. try
  656. {
  657. using (new ConsoleUtils.LoadingIndicator(true))
  658. {
  659. StringBuilder reAddCommand = new StringBuilder("");
  660. foreach (var tag in CurrentLanguages.Where(x => !x.Equals(language, StringComparison.InvariantCultureIgnoreCase)))
  661. {
  662. reAddCommand.Append($@";$List[0].InputMethodTips.Add('{tag}')");
  663. }
  664. var result = NSudo.RunProcessAsUser(NSudo.GetUserToken(),
  665. "PowerShell.exe", $"-NoP -C \"$Tag=(Get-WinUserLanguageList)[0].LanguageTag;" +
  666. $"$List = New-WinUserLanguageList $Tag" +
  667. $"{reAddCommand.ToString()};" +
  668. $"Set-WinUserLanguageList $List -Force\"");
  669. if (result == null)
  670. {
  671. throw new Exception("Could not start user PowerShell process.");
  672. }
  673. if (result > 0)
  674. {
  675. throw new Exception("PowerShell exited with a non-zero exitcode.");
  676. }
  677. }
  678. } catch (Exception e)
  679. {
  680. Console.WriteLine();
  681. ConsoleTUI.OpenFrame.Close("Error: " + e.Message.TrimEnd('\n').TrimEnd('\r'), ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  682. {
  683. AnyKey = true,
  684. Text = "Press any key to return to the Menu: "
  685. });
  686. return false;
  687. }
  688. CurrentLanguages = new List<string>();
  689. var langsKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\International\\User Profile");
  690. foreach (var langKey in langsKey.GetSubKeyNames())
  691. {
  692. CurrentLanguages.AddRange(langsKey.OpenSubKey(langKey).GetValueNames().Where(x => x.Contains(':')));
  693. }
  694. if (CurrentLanguages.Contains(language, StringComparer.InvariantCultureIgnoreCase))
  695. {
  696. Console.WriteLine();
  697. ConsoleTUI.OpenFrame.Close("Keyboard language could not be removed. If it is the\r\nactive UI language, this is normal.", ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  698. {
  699. AnyKey = true,
  700. Text = "Press any key to return to the Menu: "
  701. });
  702. return false;
  703. }
  704. Console.WriteLine();
  705. ConsoleTUI.OpenFrame.Close("Keyboard language removed successfully", ConsoleColor.Green, Console.BackgroundColor, new ChoicePrompt()
  706. {
  707. AnyKey = true,
  708. Text = "Press any key to return to the Menu: "
  709. });
  710. return true;
  711. }
  712. public static bool ShowRemoveLanguagePackMenu()
  713. {
  714. DismApi.Initialize(DismLogLevel.LogErrors);
  715. DismPackageCollection packages;
  716. using (var session = DismApi.OpenOnlineSession())
  717. {
  718. packages = DismApi.GetPackages(session);
  719. session.Close();
  720. }
  721. DismApi.Shutdown();
  722. currentLanguagePacks = new List<string>(packages.Where(x => x.PackageName.StartsWith("Microsoft-Windows-Client-LanguagePack-Package")).Select(x => x.PackageName));
  723. List<MenuItem> displayItems = new List<MenuItem>();
  724. foreach (var menuItem in LanguagePackItems)
  725. {
  726. var tag = ((string)menuItem.ReturnValue).Split('|').First();
  727. var pkg = currentLanguagePacks.FirstOrDefault(x => x.Contains('~' + tag + '~'));
  728. if (pkg != null)
  729. {
  730. var clone = menuItem.Clone();
  731. string activeLang = null;
  732. try
  733. {
  734. var deskKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\Desktop");
  735. activeLang = ((string[])deskKey.GetValue("PreferredUILanguages")).First();
  736. } catch
  737. {
  738. }
  739. if (activeLang == tag)
  740. {
  741. clone.IsEnabled = false;
  742. clone.PrimaryTextForeground = ConsoleColor.DarkGray;
  743. clone.SecondaryText = "[UI Language]";
  744. clone.SecondaryTextForeground = ConsoleColor.Yellow;
  745. }
  746. clone.ReturnValue = pkg;
  747. displayItems.Add(clone);
  748. }
  749. }
  750. var result = amecs.ShowDefaultMenu(displayItems.ToList());
  751. if (result.GetType().Name.StartsWith("Func")) return ((Func<bool>)result).Invoke();
  752. return RemoveLanguagePack((string)result);
  753. }
  754. private static bool RemoveLanguagePack(string package)
  755. {
  756. ConsoleTUI.OpenFrame.WriteCentered("Removing language pack");
  757. var topCache = Console.CursorTop;
  758. var leftCache = Console.CursorLeft;
  759. bool inProgress = false;
  760. try
  761. {
  762. using (var indicator = new ConsoleUtils.LoadingIndicator(true))
  763. {
  764. DismApi.Initialize(DismLogLevel.LogErrors);
  765. using (var session = DismApi.OpenOnlineSession())
  766. {
  767. var stdout = GetStdHandle(-11);
  768. bool indicatorStopped = false;
  769. var maxHashTags = (ConsoleTUI.OpenFrame.DisplayWidth - 5);
  770. DismApi.RemovePackageByName(session, package, delegate(DismProgress progress)
  771. {
  772. inProgress = true;
  773. if (!indicatorStopped)
  774. {
  775. indicator.Stop();
  776. Console.SetCursorPosition(leftCache, topCache);
  777. Console.WriteLine(" ");
  778. }
  779. indicatorStopped = true;
  780. var progressPerc = progress.Current / 10;
  781. var currentHashTags = (int)Math.Ceiling(Math.Min(((double)progressPerc / 100) * maxHashTags, maxHashTags));
  782. var spaces = maxHashTags - currentHashTags + (4 - progressPerc.ToString().Length);
  783. var sb = new StringBuilder(new string('#', currentHashTags) + new string(' ', spaces) + progressPerc + "%");
  784. uint throwaway;
  785. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out throwaway);
  786. inProgress = false;
  787. });
  788. session.Close();
  789. Thread.Sleep(100);
  790. var sb = new StringBuilder(new string('#', maxHashTags) + " 100%");
  791. uint throwaway;
  792. WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out throwaway);
  793. }
  794. DismApi.Shutdown();
  795. }
  796. } catch (Exception e)
  797. {
  798. Console.WriteLine();
  799. ConsoleTUI.OpenFrame.Close("DISM error: " + e.Message, ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  800. {
  801. AnyKey = true,
  802. Text = "Press any key to return to the Menu: "
  803. });
  804. return false;
  805. }
  806. Console.WriteLine();
  807. ConsoleTUI.OpenFrame.Close("Language pack removed successfully", ConsoleColor.Green, Console.BackgroundColor, new ChoicePrompt()
  808. {
  809. AnyKey = true,
  810. Text = "Press any key to return to the Menu: "
  811. });
  812. return true;
  813. }
  814. public static bool ChangeDisplayLanguage(int downloadAmount, string language)
  815. {
  816. DismApi.Initialize(DismLogLevel.LogErrors);
  817. DismPackageCollection packages;
  818. using (var session = DismApi.OpenOnlineSession())
  819. {
  820. packages = DismApi.GetPackages(session);
  821. session.Close();
  822. }
  823. DismApi.Shutdown();
  824. currentLanguagePacks = new List<string>(packages.Where(x => x.PackageName.StartsWith("Microsoft-Windows-Client-LanguagePack-Package")).Select(x => x.PackageName));
  825. if (!currentLanguagePacks.Any(x => x.Contains('~' + language + '~')))
  826. {
  827. if (!InstallLanguage(downloadAmount, language, false))
  828. return false;
  829. ConsoleTUI.OpenFrame.WriteLine();
  830. ConsoleTUI.OpenFrame.WriteLine();
  831. }
  832. var choice = new ChoicePrompt()
  833. {
  834. Text = "Make default keyboard language? (Y/N): "
  835. }.Start();
  836. if (!choice.HasValue) return true;
  837. bool makeDefault = choice == 0;
  838. string previousDefault = null;
  839. if (!makeDefault)
  840. {
  841. try
  842. {
  843. var langsKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\International\\User Profile");
  844. try { previousDefault = (string)langsKey.GetValue("InputMethodOverride");} catch {}
  845. if (previousDefault == null)
  846. {
  847. string activeLang = null;
  848. try
  849. {
  850. var deskKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\Desktop");
  851. activeLang = ((string[])deskKey.GetValue("PreferredUILanguages")).First();
  852. } catch
  853. {
  854. }
  855. var keyName = langsKey.GetSubKeyNames().FirstOrDefault(x => x == activeLang);
  856. if (keyName == null) keyName = langsKey.GetSubKeyNames().FirstOrDefault(x => x.StartsWith(activeLang.Split('-').First()));
  857. var langKey = langsKey.OpenSubKey(keyName);
  858. foreach (var langValue in langKey.GetValueNames().Where(x => x.Contains(':')))
  859. {
  860. if ((int)langKey.GetValue(langValue) == 1)
  861. previousDefault = langValue;
  862. }
  863. }
  864. } catch (Exception e) {
  865. }
  866. }
  867. CurrentLanguages = new List<string>();
  868. try
  869. {
  870. var langsKey = Registry.Users.OpenSubKey(Globals.UserSID + "\\Control Panel\\International\\User Profile");
  871. foreach (var langKey in langsKey.GetSubKeyNames())
  872. {
  873. CurrentLanguages.AddRange(langsKey.OpenSubKey(langKey).GetValueNames().Where(x => x.Contains(':')));
  874. }
  875. } catch
  876. {
  877. }
  878. ConsoleTUI.OpenFrame.WriteCentered("\r\nSetting language");
  879. try
  880. {
  881. using (new ConsoleUtils.LoadingIndicator(true))
  882. {
  883. try
  884. {
  885. Process proc = new Process();
  886. proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
  887. proc.StartInfo.FileName = "PowerShell.exe";
  888. proc.StartInfo.Arguments = $"-NoP -C \"Set-WinSystemLocale {language}\"";
  889. proc.Start();
  890. proc.WaitForExit();
  891. } catch { }
  892. StringBuilder reAddCommand = new StringBuilder("");
  893. foreach (var tag in CurrentLanguages)
  894. {
  895. reAddCommand.Append($@";$List[0].InputMethodTips.Add('{tag}')");
  896. }
  897. var result = NSudo.RunProcessAsUser(NSudo.GetUserToken(),
  898. "PowerShell.exe", previousDefault == null
  899. ? $"-NoP -C \"$List = New-WinUserLanguageList {language}" +
  900. $"{reAddCommand.ToString()};" +
  901. $"Set-WinUserLanguageList $List -Force;" +
  902. $"Set-WinDefaultInputMethodOverride\""
  903. : $"-NoP -C \"$List = New-WinUserLanguageList {language}" +
  904. $"{reAddCommand.ToString()};" +
  905. $"Set-WinUserLanguageList $List -Force;" +
  906. $"Set-WinDefaultInputMethodOverride '{previousDefault}'\""
  907. );
  908. if (result == null)
  909. {
  910. throw new Exception("Could not start user PowerShell process.");
  911. }
  912. if (result > 0)
  913. {
  914. throw new Exception("PowerShell exited with a non-zero exitcode.");
  915. }
  916. new Reg.Value() { KeyName = "HKU\\" + Globals.UserSID + "\\Control Panel\\Desktop", ValueName = "PreferredUILanguagesPending", Data = new[] { language }, Type = Reg.RegistryValueType.REG_MULTI_SZ }.Apply();
  917. }
  918. } catch (Exception e)
  919. {
  920. Console.WriteLine();
  921. ConsoleTUI.OpenFrame.Close("Error: " + e.Message.TrimEnd('\n').TrimEnd('\r'), ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt()
  922. {
  923. AnyKey = true,
  924. Text = "Press any key to return to the Menu: "
  925. });
  926. return false;
  927. }
  928. Console.WriteLine();
  929. if ((int?)ConsoleTUI.OpenFrame.Close("Display language changed to " + language, ConsoleColor.Green, Console.BackgroundColor, new ChoicePrompt()
  930. {
  931. TextForeground = ConsoleColor.Yellow,
  932. Text = "Restart to apply changes? (Y/N): "
  933. }) == 0) amecs.RestartWindows(false, true);
  934. return true;
  935. }
  936. private static readonly MenuItem[] LanguagePackItems = new[] { new MenuItem("Arabic (ar-SA)", "ar-SA|2480000"), new MenuItem("Bulgarian (bg-BG)", "bg-BG|2480000"), new MenuItem("Chineese [Simplified] (zh-CN)", "zh-CN|3230000"), new MenuItem("Chineese [Traditional] (zh-TW)", "zh-TW|3230000"), new MenuItem("Croatian (hr-HR)", "hr-HR|2900000"), new MenuItem("Czech (cs-CZ)", "cs-CZ|2480000"), new MenuItem("Danish (da-DK)", "da-DK|2480000"), new MenuItem("Dutch (nl-NL)", "nl-NL|2900000"), new MenuItem("English [US] (en-US)", "en-US|2480000"), new MenuItem("English [UK] (en-GB)", "en-GB|2480000"), new MenuItem("Estonian (et-EE)", "et-EE|2480000"), new MenuItem("Finnish (fi-FI)", "fi-FI|2480000"), new MenuItem("French [Canada] (fr-CA)", "fr-CA|2480000"), new MenuItem("French [France] (fr-FR)", "fr-FR|2900000"), new MenuItem("German (de-DE)", "de-DE|2480000"), new MenuItem("Greek (el-GR)", "el-GR|2480000"), new MenuItem("Hebrew (he-IL)", "he-IL|2900000"), new MenuItem("Hungarian (hu-HU)", "hu-HU|2900000"), new MenuItem("Italian (it-IT)", "it-IT|2900000"), new MenuItem("Japanese (ja-JP)", "ja-JP|2900000"), new MenuItem("Korean (ko-KR)", "ko-KR|2900000"), new MenuItem("Latvian (lv-LV)", "lv-LV|2900000"), new MenuItem("Lithuanian (lt-LT)", "lt-LT|2900000"), new MenuItem("Norwegian (nb-NO)", "nb-NO|2900000"), new MenuItem("Polish (pl-PL)", "pl-PL|3230000"), new MenuItem("Portugeese [Brazil] (pt-BR)", "pt-BR|3230000"), new MenuItem("Portugeese [Portugal] (pt-PT)", "pt-PT|3230000"), new MenuItem("Romanian (ro-RO)", "ro-RO|3230000"), new MenuItem("Russian (ru-RU)", "ru-RU|3230000"), new MenuItem("Serbian (sr-Latn-RS)", "tn-RS|3230000"), new MenuItem("Slovak (sk-SK)", "sk-SK|3230000"), new MenuItem("Slovenian (sl-SI)", "sl-SI|3230000"), new MenuItem("Spanish [Mexico] (es-MX)", "es-MX|2480000"), new MenuItem("Spanish [Spain] (es-ES)", "es-ES|2480000"), new MenuItem("Swedish (sv-SE)", "sv-SE|3230000"), new MenuItem("Thai (th-TH)", "th-TH|3230000"), new MenuItem("Turkish (tr-TR)", "tr-TR|3230000"), new MenuItem("Ukrainian (uk-UA)", "uk-UA|3230000"), };
  937. private static readonly MenuItem[] KeyboardLanguageItems = new[] { new MenuItem("United States", new[] { new MenuItem("United States - English", "0409:00000409"), new MenuItem("United States - International", "0409:00020409"), new MenuItem("United States - Dvorak", "0409:00010409"), new MenuItem("United States - Dvorak (Left Hand)", "0409:00030409"), new MenuItem("United States - Dvorak (Right Hand)", "0409:00040409") }), new MenuItem("Chinese", new[] { new MenuItem("Chineese (Simplified)", "0804:{81D4E9C9-1D3B-41BC-9E6C-4B40BF79E35E}{FA550B04-5AD7-411f-A5AC-CA038EC515D7}"), new MenuItem("Chineese (Traditional) NON-FUNCTIONAL", "0404:{B115690A-EA02-48D5-A231-E3578D2FDF80}{B2F9C502-1742-11D4-9790-0080C882687E}"), new MenuItem("Chineese (Traditional, Hong Kong S.A.R.)", "0404:00000c04"), new MenuItem("Chineese (Traditonal Macao S.A.R.)", "0404:00001404"), new MenuItem("Chineese (Simplified, Singapore)", "0404:00001004") }), new MenuItem("Hindi (Devanagari) Traditional", "0439:00010439"), new MenuItem("Spanish", new[] { new MenuItem("Spanish (Spain)", "0c0a:0000040a"), new MenuItem("Spanish (Mexico)", "080a:0000080a"), new MenuItem("Spanish Variation", "0c0a:0001040a") }), new MenuItem("French", "040c:0000040c"), new MenuItem("Arabic", new[] { new MenuItem("Arabic (101)", "0401:00000401"), new MenuItem("Arabic (102)", "0401:00010401"), new MenuItem("Arabic (102 AZERTY)", "0401:00020401") }), new MenuItem("Russian", new[] { new MenuItem("Russian", "0419:00000419"), new MenuItem("Russian - Mnemonic", "0419:00020419"), new MenuItem("Russian (Typewriter)", "0419:00010419") }), new MenuItem("Bangla", new[] { new MenuItem("Bangla (Bangladesh)", "0445:00000445"), new MenuItem("Bangla (India)", "0445:00020445"), new MenuItem("Bangla (India) - Legacy", "0445:00010445") }), new MenuItem("Portuguese", new[] { new MenuItem("Portuguese", "0816:00000816"), new MenuItem("Portuguese (Brazilian ABNT)", "0816:00000416"), new MenuItem("Portuguese (Brazilian ABNT2)", "0816:00010416") }), new MenuItem("Albanian", "041c:0000041c"), new MenuItem("Amharic", "045e:{E429B25A-E5D3-4D1F-9BE3-0C608477E3A1}{8F96574E-C86C-4bd6-9666-3F7327D4CBE8}"), new MenuItem("Armenian", new[] { new MenuItem("Armenian Eastern", "042b:0000042b"), new MenuItem("Armenian Phonetic", "042b:0002042b"), new MenuItem("Armenian Typewriter", "042b:0003042b"), new MenuItem("Armenian Western", "042b:0001042b") }), new MenuItem("Assamese - Inscript", "044d:0000044d"), new MenuItem("Azerbaijani", new[] { new MenuItem("Azerbaijani (Standard)", "042c:0001042c"), new MenuItem("Azerbaijani Cyrillic", "042c:0000082c"), new MenuItem("Azerbaijani Latin", "042c:0000042c") }), new MenuItem("Bashkir", "046d:0000046d"), new MenuItem("Belarusian", "0423:00000423"), new MenuItem("Belgian", new[] { new MenuItem("Belgian (Comma)", "080c:0001080c"), new MenuItem("Belgian (Period)", "080c:00000813"), new MenuItem("Belgian French", "080c:0000080c") }), new MenuItem("Bosnian (Cyrillic)", "141a:00000201a"), new MenuItem("Buginese", "0421:000b0c00"), new MenuItem("Bulgarian", new[] { new MenuItem("Bulgarian", "0402:00030402"), new MenuItem("Bulgarian Latin", "0402:00010402"), new MenuItem("Bulgarian (Phonetic Layout)", "0402:00020402"), new MenuItem("Bulgarian (Phonetic Traditonal)", "0402:00040402"), new MenuItem("Bulgarian (Typewriter)", "0402:00000402") }), new MenuItem("Canadian", new[] { new MenuItem("Canadian French", "0c0c:00001009"), new MenuItem("Canadian French (Legacy)", "0c0c:00000c0c"), new MenuItem("Canadian Multilingual Standard", "0c0c:00011009") }), new MenuItem("Central Atlas Tamazight", "085f:0000085f"), new MenuItem("Central Kurdish", "0429:00000429"), new MenuItem("Cherokee", new[] { new MenuItem("Cherokee Nation", "045c:0000045c"), new MenuItem("Cherokee Nation Phonetic", "045c:0001045c") }), new MenuItem("Croatian", "041a:0000041a"), new MenuItem("Czech", new[] { new MenuItem("Czech", "2000:00000405"), new MenuItem("Czech (QWERTY)", "2000:00010405"), new MenuItem("Czech Programmers", "2000:00020405") }), new MenuItem("Danish", "0406:00000406"), new MenuItem("Divehi", ne
  938. }
  939. public static class NativeMethods
  940. {
  941. private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
  942. private const uint FILE_READ_EA = 0x0008;
  943. private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
  944. [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  945. static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
  946. [DllImport("kernel32.dll", SetLastError = true)]
  947. [return: MarshalAs(UnmanagedType.Bool)]
  948. static extern bool CloseHandle(IntPtr hObject);
  949. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  950. public static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] uint access, [MarshalAs(UnmanagedType.U4)] FileShare share, IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
  951. [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, IntPtr templateFile);
  952. public static string GetFinalPathName(string path)
  953. {
  954. var h = CreateFile(path, FILE_READ_EA, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
  955. if (h == INVALID_HANDLE_VALUE) return null;
  956. try
  957. {
  958. var sb = new StringBuilder(1024);
  959. var res = GetFinalPathNameByHandle(h, sb, 1024, 0);
  960. if (res == 0) return null;
  961. return sb.ToString();
  962. } finally
  963. {
  964. CloseHandle(h);
  965. }
  966. }
  967. }
  968. public class WebClientEx : WebClient
  969. {
  970. private readonly long from;
  971. private readonly long to;
  972. public WebClientEx(long from, long to)
  973. {
  974. this.from = from;
  975. this.to = to;
  976. }
  977. protected override WebRequest GetWebRequest(Uri address)
  978. {
  979. var request = (HttpWebRequest)base.GetWebRequest(address);
  980. request.AddRange(this.from, this.to);
  981. return request;
  982. }
  983. }
  984. }