1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.IO;
26 using System.Reflection;
27 using System.Runtime.InteropServices;
28 using System.Security.Cryptography;
29 using System.Text;
30 using System.Text.RegularExpressions;
31 using System.Windows.Forms;
32 
33 using Microsoft.Win32;
34 
35 using KeePass.App;
36 using KeePass.Forms;
37 using KeePass.Native;
38 using KeePass.Resources;
39 using KeePass.Util.Spr;
40 
41 using KeePassLib;
42 using KeePassLib.Delegates;
43 using KeePassLib.Serialization;
44 using KeePassLib.Utility;
45 
46 using NativeLib = KeePassLib.Native.NativeLib;
47 
48 namespace KeePass.Util
49 {
50 	public sealed class OpenUrlEventArgs : EventArgs
51 	{
52 		private string m_strUrl;
53 		public string Url
54 		{
55 			get { return m_strUrl; }
56 			set { m_strUrl = value; }
57 		}
58 
59 		private readonly PwEntry m_pe;
60 		public PwEntry Entry
61 		{
62 			get { return m_pe; }
63 		}
64 
65 		private readonly bool m_bAllowOverride;
66 		public bool AllowOverride
67 		{
68 			get { return m_bAllowOverride; }
69 		}
70 
71 		private readonly string m_strBaseRaw;
72 		public string BaseRaw
73 		{
74 			get { return m_strBaseRaw; }
75 		}
76 
OpenUrlEventArgs(string strUrlToOpen, PwEntry peDataSource, bool bAllowOverride, string strBaseRaw)77 		public OpenUrlEventArgs(string strUrlToOpen, PwEntry peDataSource,
78 			bool bAllowOverride, string strBaseRaw)
79 		{
80 			m_strUrl = strUrlToOpen;
81 			m_pe = peDataSource;
82 			m_bAllowOverride = bAllowOverride;
83 			m_strBaseRaw = strBaseRaw;
84 		}
85 	}
86 
87 	public static class WinUtil
88 	{
89 		private static bool m_bIsWindows9x = false;
90 		private static bool m_bIsWindows2000 = false;
91 		private static bool m_bIsWindowsXP = false;
92 		private static bool m_bIsAtLeastWindows2000 = false;
93 		private static bool m_bIsAtLeastWindowsVista = false;
94 		private static bool m_bIsAtLeastWindows7 = false;
95 		private static bool m_bIsAtLeastWindows8 = false;
96 		private static bool m_bIsAtLeastWindows10 = false;
97 		private static bool m_bIsAppX = false;
98 
99 		private static string m_strExePath = null;
100 
101 		private static ulong m_uFrameworkVersion = 0;
102 
103 		public static event EventHandler<OpenUrlEventArgs> OpenUrlPre;
104 
105 		public static bool IsWindows9x
106 		{
107 			get { return m_bIsWindows9x; }
108 		}
109 
110 		public static bool IsWindows2000
111 		{
112 			get { return m_bIsWindows2000; }
113 		}
114 
115 		public static bool IsWindowsXP
116 		{
117 			get { return m_bIsWindowsXP; }
118 		}
119 
120 		public static bool IsAtLeastWindows2000
121 		{
122 			get { return m_bIsAtLeastWindows2000; }
123 		}
124 
125 		public static bool IsAtLeastWindowsVista
126 		{
127 			get { return m_bIsAtLeastWindowsVista; }
128 		}
129 
130 		public static bool IsAtLeastWindows7
131 		{
132 			get { return m_bIsAtLeastWindows7; }
133 		}
134 
135 		public static bool IsAtLeastWindows8
136 		{
137 			get { return m_bIsAtLeastWindows8; }
138 		}
139 
140 		public static bool IsAtLeastWindows10
141 		{
142 			get { return m_bIsAtLeastWindows10; }
143 		}
144 
145 		public static bool IsAppX
146 		{
147 			get { return m_bIsAppX; }
148 		}
149 
WinUtil()150 		static WinUtil()
151 		{
152 			if(NativeLib.IsUnix()) return;
153 
154 			OperatingSystem os = Environment.OSVersion;
155 			Version v = os.Version;
156 
157 			m_bIsWindows9x = (os.Platform == PlatformID.Win32Windows);
158 			m_bIsWindows2000 = ((v.Major == 5) && (v.Minor == 0));
159 			m_bIsWindowsXP = ((v.Major == 5) && (v.Minor == 1));
160 
161 			m_bIsAtLeastWindows2000 = (v.Major >= 5);
162 			m_bIsAtLeastWindowsVista = (v.Major >= 6);
163 			m_bIsAtLeastWindows7 = ((v.Major >= 7) || ((v.Major == 6) && (v.Minor >= 1)));
164 			m_bIsAtLeastWindows8 = ((v.Major >= 7) || ((v.Major == 6) && (v.Minor >= 2)));
165 
166 			// Environment.OSVersion is reliable only up to version 6.2;
167 			// https://msdn.microsoft.com/library/windows/desktop/ms724832.aspx
168 			RegistryKey rk = null;
169 			try
170 			{
171 				rk = Registry.LocalMachine.OpenSubKey(
172 					"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", false);
173 				if(rk != null)
174 				{
175 					string str = rk.GetValue("CurrentMajorVersionNumber",
176 						string.Empty).ToString();
177 					uint u;
178 					if(uint.TryParse(str, out u))
179 						m_bIsAtLeastWindows10 = (u >= 10);
180 					else { Debug.Assert(string.IsNullOrEmpty(str)); }
181 				}
182 				else { Debug.Assert(false); }
183 			}
184 			catch(Exception) { Debug.Assert(false); }
185 			finally { if(rk != null) rk.Close(); }
186 
187 			try
188 			{
189 				string strDir = UrlUtil.GetFileDirectory(GetExecutable(), false, false);
190 				if(strDir.IndexOf("\\WindowsApps\\", StrUtil.CaseIgnoreCmp) >= 0)
191 				{
192 					Regex rx = new Regex("\\\\WindowsApps\\\\.*?_\\d+(\\.\\d+)*_",
193 						RegexOptions.IgnoreCase);
194 					m_bIsAppX = rx.IsMatch(strDir);
195 				}
196 				else { Debug.Assert(!m_bIsAppX); } // No AppX by default
197 			}
198 			catch(Exception) { Debug.Assert(false); }
199 		}
200 
OpenEntryUrl(PwEntry pe)201 		public static void OpenEntryUrl(PwEntry pe)
202 		{
203 			if(pe == null) { Debug.Assert(false); throw new ArgumentNullException("pe"); }
204 
205 			string strUrl = pe.Strings.ReadSafe(PwDefs.UrlField);
206 
207 			// The user interface enables the URL open command if and
208 			// only if the URL is not empty, i.e. it ignores overrides
209 			if(strUrl.Length == 0) return;
210 
211 			if(pe.OverrideUrl.Length > 0)
212 				OpenUrl(pe.OverrideUrl, pe, true, strUrl);
213 			else
214 			{
215 				string strOverride = Program.Config.Integration.UrlOverride;
216 				if(strOverride.Length > 0)
217 					OpenUrl(strOverride, pe, true, strUrl);
218 				else
219 					OpenUrl(strUrl, pe, true);
220 			}
221 		}
222 
OpenUrl(string strUrlToOpen, PwEntry peDataSource)223 		public static void OpenUrl(string strUrlToOpen, PwEntry peDataSource)
224 		{
225 			OpenUrl(strUrlToOpen, peDataSource, true, null);
226 		}
227 
OpenUrl(string strUrlToOpen, PwEntry peDataSource, bool bAllowOverride)228 		public static void OpenUrl(string strUrlToOpen, PwEntry peDataSource,
229 			bool bAllowOverride)
230 		{
231 			OpenUrl(strUrlToOpen, peDataSource, bAllowOverride, null);
232 		}
233 
OpenUrl(string strUrlToOpen, PwEntry peDataSource, bool bAllowOverride, string strBaseRaw)234 		public static void OpenUrl(string strUrlToOpen, PwEntry peDataSource,
235 			bool bAllowOverride, string strBaseRaw)
236 		{
237 			VoidDelegate f = delegate()
238 			{
239 				try { OpenUrlPriv(strUrlToOpen, peDataSource, bAllowOverride, strBaseRaw); }
240 				catch(Exception) { Debug.Assert(false); }
241 			};
242 
243 			MainForm mf = Program.MainForm;
244 			if((mf != null) && mf.InvokeRequired) mf.Invoke(f);
245 			else f();
246 		}
247 
OpenUrlPriv(string strUrlToOpen, PwEntry peDataSource, bool bAllowOverride, string strBaseRaw)248 		private static void OpenUrlPriv(string strUrlToOpen, PwEntry peDataSource,
249 			bool bAllowOverride, string strBaseRaw)
250 		{
251 			if(string.IsNullOrEmpty(strUrlToOpen)) { Debug.Assert(false); return; }
252 
253 			if(WinUtil.OpenUrlPre != null)
254 			{
255 				OpenUrlEventArgs e = new OpenUrlEventArgs(strUrlToOpen, peDataSource,
256 					bAllowOverride, strBaseRaw);
257 				WinUtil.OpenUrlPre(null, e);
258 				strUrlToOpen = e.Url;
259 
260 				if(string.IsNullOrEmpty(strUrlToOpen)) return;
261 			}
262 
263 			string strPrevWorkDir = WinUtil.GetWorkingDirectory();
264 			string strThisExe = WinUtil.GetExecutable();
265 
266 			string strExeDir = UrlUtil.GetFileDirectory(strThisExe, false, true);
267 			WinUtil.SetWorkingDirectory(strExeDir);
268 
269 			string strUrl = CompileUrl(strUrlToOpen, peDataSource, bAllowOverride,
270 				strBaseRaw, null);
271 
272 			if(string.IsNullOrEmpty(strUrl)) { } // Might be placeholder only
273 			else if(WinUtil.IsCommandLineUrl(strUrl))
274 			{
275 				string strApp, strArgs;
276 				StrUtil.SplitCommandLine(WinUtil.GetCommandLineFromUrl(strUrl),
277 					out strApp, out strArgs);
278 
279 				try
280 				{
281 					try { NativeLib.StartProcess(strApp, strArgs); }
282 					catch(Win32Exception)
283 					{
284 						ProcessStartInfo psi = new ProcessStartInfo();
285 						psi.FileName = strApp;
286 						if(!string.IsNullOrEmpty(strArgs)) psi.Arguments = strArgs;
287 						psi.UseShellExecute = false;
288 
289 						NativeLib.StartProcess(psi);
290 					}
291 				}
292 				catch(Exception exCmd)
293 				{
294 					string strMsg = KPRes.FileOrUrl + ": " + strApp;
295 					if(!string.IsNullOrEmpty(strArgs))
296 						strMsg += MessageService.NewParagraph +
297 							KPRes.Arguments + ": " + strArgs;
298 
299 					MessageService.ShowWarning(strMsg, exCmd);
300 				}
301 			}
302 			else // Standard URL
303 			{
304 				try { NativeLib.StartProcess(strUrl); }
305 				catch(Exception exUrl)
306 				{
307 					MessageService.ShowWarning(strUrl, exUrl);
308 				}
309 			}
310 
311 			// Restore previous working directory
312 			WinUtil.SetWorkingDirectory(strPrevWorkDir);
313 
314 			// SprEngine.Compile might have modified the database
315 			MainForm mf = Program.MainForm;
316 			if(mf != null)
317 			{
318 				mf.RefreshEntriesList();
319 				mf.UpdateUI(false, null, false, null, false, null, false);
320 			}
321 		}
322 
CompileUrl(string strUrlToOpen, PwEntry pe, bool bAllowOverride, string strBaseRaw, bool? obForceEncCmd)323 		internal static string CompileUrl(string strUrlToOpen, PwEntry pe,
324 			bool bAllowOverride, string strBaseRaw, bool? obForceEncCmd)
325 		{
326 			MainForm mf = Program.MainForm;
327 			PwDatabase pd = null;
328 			try { if(mf != null) pd = mf.DocumentManager.SafeFindContainerOf(pe); }
329 			catch(Exception) { Debug.Assert(false); }
330 
331 			string strUrlFlt = strUrlToOpen;
332 			strUrlFlt = strUrlFlt.TrimStart(new char[] { ' ', '\t', '\r', '\n' });
333 
334 			bool bEncCmd = (obForceEncCmd.HasValue ? obForceEncCmd.Value :
335 				WinUtil.IsCommandLineUrl(strUrlFlt));
336 
337 			SprContext ctx = new SprContext(pe, pd, SprCompileFlags.All, false, bEncCmd);
338 			ctx.Base = strBaseRaw;
339 			ctx.BaseIsEncoded = false;
340 
341 			string strUrl = SprEngine.Compile(strUrlFlt, ctx);
342 
343 			string strOvr = Program.Config.Integration.UrlSchemeOverrides.GetOverrideForUrl(
344 				strUrl);
345 			if(!bAllowOverride) strOvr = null;
346 			if(strOvr != null)
347 			{
348 				bool bEncCmdOvr = WinUtil.IsCommandLineUrl(strOvr);
349 
350 				SprContext ctxOvr = new SprContext(pe, pd, SprCompileFlags.All,
351 					false, bEncCmdOvr);
352 				ctxOvr.Base = strUrl;
353 				ctxOvr.BaseIsEncoded = bEncCmd;
354 
355 				strUrl = SprEngine.Compile(strOvr, ctxOvr);
356 			}
357 
358 			return strUrl;
359 		}
360 
OpenUrlWithApp(string strUrlToOpen, PwEntry peDataSource, string strAppPath)361 		public static void OpenUrlWithApp(string strUrlToOpen, PwEntry peDataSource,
362 			string strAppPath)
363 		{
364 			if(string.IsNullOrEmpty(strUrlToOpen)) { Debug.Assert(false); return; }
365 			if(string.IsNullOrEmpty(strAppPath)) { Debug.Assert(false); return; }
366 
367 			string strUrl = strUrlToOpen.Trim();
368 			if(strUrl.Length == 0) { Debug.Assert(false); return; }
369 			strUrl = SprEncoding.EncodeForCommandLine(strUrl);
370 
371 			string strApp = strAppPath.Trim();
372 			if(strApp.Length == 0) { Debug.Assert(false); return; }
373 			strApp = SprEncoding.EncodeForCommandLine(strApp);
374 
375 			string str = "cmd://\"" + strApp + "\" \"" + strUrl + "\"";
376 			OpenUrl(str, peDataSource, false);
377 		}
378 
OpenUrlDirectly(string strUrl)379 		internal static void OpenUrlDirectly(string strUrl)
380 		{
381 			if(string.IsNullOrEmpty(strUrl)) { Debug.Assert(false); return; }
382 
383 			try { NativeLib.StartProcess(strUrl); }
384 			catch(Exception ex) { MessageService.ShowWarning(strUrl, ex); }
385 		}
386 
Restart()387 		public static void Restart()
388 		{
389 			try { NativeLib.StartProcess(WinUtil.GetExecutable()); }
390 			catch(Exception ex) { MessageService.ShowWarning(ex); }
391 		}
392 
GetExecutable()393 		public static string GetExecutable()
394 		{
395 			string str = m_strExePath;
396 			if(str != null) return str;
397 
398 			try { str = Assembly.GetExecutingAssembly().Location; }
399 			catch(Exception) { }
400 
401 			if(string.IsNullOrEmpty(str))
402 			{
403 				str = Assembly.GetExecutingAssembly().GetName().CodeBase;
404 				str = UrlUtil.FileUrlToPath(str);
405 			}
406 
407 			m_strExePath = str;
408 			return str;
409 		}
410 
411 		private static string g_strAsmVersion = null;
GetAssemblyVersion()412 		internal static string GetAssemblyVersion()
413 		{
414 			if(g_strAsmVersion == null)
415 			{
416 				try
417 				{
418 					Version v = typeof(WinUtil).Assembly.GetName().Version;
419 					g_strAsmVersion = v.ToString(4);
420 				}
421 				catch(Exception) { Debug.Assert(false); }
422 
423 				if(g_strAsmVersion == null)
424 					g_strAsmVersion = StrUtil.VersionToString(PwDefs.FileVersion64, 4);
425 			}
426 
427 			return g_strAsmVersion;
428 		}
429 
430 		/// <summary>
431 		/// Shorten a path.
432 		/// </summary>
433 		/// <param name="strPath">Path to make shorter.</param>
434 		/// <param name="cchMax">Maximum number of characters in the returned string.</param>
435 		/// <returns>Shortened path.</returns>
CompactPath(string strPath, int cchMax)436 		public static string CompactPath(string strPath, int cchMax)
437 		{
438 			Debug.Assert(strPath != null);
439 			if(strPath == null) throw new ArgumentNullException("strPath");
440 			Debug.Assert(cchMax >= 0);
441 			if(cchMax < 0) throw new ArgumentOutOfRangeException("cchMax");
442 
443 			if(strPath.Length <= cchMax) return strPath;
444 			if(cchMax == 0) return string.Empty;
445 
446 			try
447 			{
448 				if(!NativeLib.IsUnix())
449 				{
450 					StringBuilder sb = new StringBuilder(strPath.Length + 2);
451 
452 					if(NativeMethods.PathCompactPathEx(sb, strPath, (uint)cchMax + 1, 0))
453 					{
454 						if((sb.Length <= cchMax) && (sb.Length != 0))
455 							return sb.ToString();
456 						else { Debug.Assert(false); }
457 					}
458 				}
459 			}
460 			catch(Exception) { Debug.Assert(false); }
461 
462 			return StrUtil.CompactString3Dots(strPath, cchMax);
463 		}
464 
FlushStorageBuffers(char chDriveLetter, bool bOnlyIfRemovable)465 		public static bool FlushStorageBuffers(char chDriveLetter, bool bOnlyIfRemovable)
466 		{
467 			string strDriveLetter = new string(chDriveLetter, 1);
468 			bool bResult = true;
469 
470 			try
471 			{
472 				if(bOnlyIfRemovable)
473 				{
474 					DriveInfo di = new DriveInfo(strDriveLetter);
475 					if(di.DriveType != DriveType.Removable) return true;
476 				}
477 
478 				string strDevice = "\\\\.\\" + strDriveLetter + ":";
479 
480 				IntPtr hDevice = NativeMethods.CreateFile(strDevice,
481 					NativeMethods.EFileAccess.GenericRead | NativeMethods.EFileAccess.GenericWrite,
482 					NativeMethods.EFileShare.Read | NativeMethods.EFileShare.Write,
483 					IntPtr.Zero, NativeMethods.ECreationDisposition.OpenExisting,
484 					0, IntPtr.Zero);
485 				if(NativeMethods.IsInvalidHandleValue(hDevice))
486 				{
487 					Debug.Assert(false);
488 					return false;
489 				}
490 
491 				string strDir = FreeDriveIfCurrent(chDriveLetter);
492 
493 				uint dwDummy;
494 				if(NativeMethods.DeviceIoControl(hDevice, NativeMethods.FSCTL_LOCK_VOLUME,
495 					IntPtr.Zero, 0, IntPtr.Zero, 0, out dwDummy, IntPtr.Zero) != false)
496 				{
497 					if(NativeMethods.DeviceIoControl(hDevice, NativeMethods.FSCTL_UNLOCK_VOLUME,
498 						IntPtr.Zero, 0, IntPtr.Zero, 0, out dwDummy, IntPtr.Zero) == false)
499 					{
500 						Debug.Assert(false);
501 					}
502 				}
503 				else bResult = false;
504 
505 				if(strDir.Length > 0) WinUtil.SetWorkingDirectory(strDir);
506 
507 				if(!NativeMethods.CloseHandle(hDevice)) { Debug.Assert(false); }
508 			}
509 			catch(Exception)
510 			{
511 				Debug.Assert(false);
512 				return false;
513 			}
514 
515 			return bResult;
516 		}
517 
FlushStorageBuffers(string strFileOnStorage, bool bOnlyIfRemovable)518 		public static bool FlushStorageBuffers(string strFileOnStorage, bool bOnlyIfRemovable)
519 		{
520 			if(strFileOnStorage == null) { Debug.Assert(false); return false; }
521 			if(strFileOnStorage.Length < 3) return false;
522 			if(strFileOnStorage[1] != ':') return false;
523 			if(strFileOnStorage[2] != '\\') return false;
524 
525 			return FlushStorageBuffers(char.ToUpper(strFileOnStorage[0]), bOnlyIfRemovable);
526 		}
527 
FreeDriveIfCurrent(char chDriveLetter)528 		private static string FreeDriveIfCurrent(char chDriveLetter)
529 		{
530 			try
531 			{
532 				string strCur = WinUtil.GetWorkingDirectory();
533 				if((strCur == null) || (strCur.Length < 3)) return string.Empty;
534 				if(strCur[1] != ':') return string.Empty;
535 				if(strCur[2] != '\\') return string.Empty;
536 
537 				char chPar = char.ToUpper(chDriveLetter);
538 				char chCur = char.ToUpper(strCur[0]);
539 				if(chPar != chCur) return string.Empty;
540 
541 				string strTemp = UrlUtil.GetTempPath();
542 				WinUtil.SetWorkingDirectory(strTemp);
543 
544 				return strCur;
545 			}
546 			catch(Exception) { Debug.Assert(false); }
547 
548 			return string.Empty;
549 		}
550 
551 		private static readonly string[] m_vIE7Windows = new string[] {
552 			"Windows Internet Explorer", "Maxthon"
553 		};
554 
IsInternetExplorer7Window(string strWindowTitle)555 		public static bool IsInternetExplorer7Window(string strWindowTitle)
556 		{
557 			if(strWindowTitle == null) return false; // No assert or throw
558 			if(strWindowTitle.Length == 0) return false; // No assert or throw
559 
560 			foreach(string str in m_vIE7Windows)
561 			{
562 				if(strWindowTitle.IndexOf(str) >= 0) return true;
563 			}
564 
565 			return false;
566 		}
567 
HashFile(IOConnectionInfo iocFile)568 		public static byte[] HashFile(IOConnectionInfo iocFile)
569 		{
570 			if(iocFile == null) { Debug.Assert(false); return null; } // Assert only
571 
572 			Stream sIn;
573 			try
574 			{
575 				sIn = IOConnection.OpenRead(iocFile);
576 				if(sIn == null) throw new FileNotFoundException();
577 			}
578 			catch(Exception) { return null; }
579 
580 			byte[] pbHash;
581 			try
582 			{
583 				using(SHA256Managed sha256 = new SHA256Managed())
584 				{
585 					pbHash = sha256.ComputeHash(sIn);
586 				}
587 			}
588 			catch(Exception) { Debug.Assert(false); sIn.Close(); return null; }
589 
590 			sIn.Close();
591 			return pbHash;
592 		}
593 
594 		// See GetCommandLineFromUrl when editing this method
IsCommandLineUrl(string strUrl)595 		public static bool IsCommandLineUrl(string strUrl)
596 		{
597 			if(strUrl == null) { Debug.Assert(false); return false; }
598 
599 			string strLower = strUrl.ToLower();
600 
601 			if(strLower.StartsWith("cmd://")) return true;
602 			if(strLower.StartsWith("\\\\")) return true; // UNC path support
603 
604 			return false;
605 		}
606 
607 		// See IsCommandLineUrl when editing this method
GetCommandLineFromUrl(string strUrl)608 		public static string GetCommandLineFromUrl(string strUrl)
609 		{
610 			if(strUrl == null) { Debug.Assert(false); return string.Empty; }
611 
612 			string strLower = strUrl.ToLower();
613 
614 			if(strLower.StartsWith("cmd://")) return strUrl.Remove(0, 6);
615 			if(strLower.StartsWith("\\\\")) return strUrl; // UNC path support
616 
617 			return strUrl;
618 		}
619 
RunElevated(string strExe, string strArgs, bool bShowMessageIfFailed)620 		public static bool RunElevated(string strExe, string strArgs,
621 			bool bShowMessageIfFailed)
622 		{
623 			if(strExe == null) throw new ArgumentNullException("strExe");
624 
625 			try
626 			{
627 				ProcessStartInfo psi = new ProcessStartInfo();
628 				psi.FileName = strExe;
629 				if(!string.IsNullOrEmpty(strArgs)) psi.Arguments = strArgs;
630 				psi.UseShellExecute = true;
631 
632 				// Elevate on Windows Vista and higher
633 				if(WinUtil.IsAtLeastWindowsVista) psi.Verb = "runas";
634 
635 				NativeLib.StartProcess(psi);
636 			}
637 			catch(Exception ex)
638 			{
639 				if(bShowMessageIfFailed) MessageService.ShowWarning(ex);
640 				return false;
641 			}
642 
643 			return true;
644 		}
645 
GetMaxNetFrameworkVersion()646 		public static ulong GetMaxNetFrameworkVersion()
647 		{
648 			if(m_uFrameworkVersion != 0) return m_uFrameworkVersion;
649 
650 			try { m_uFrameworkVersion = GetMaxNetVersionPriv(); }
651 			catch(Exception) { Debug.Assert(false); }
652 
653 			if(m_uFrameworkVersion == 0)
654 			{
655 				Version v = Environment.Version;
656 				if(v.Major > 0) m_uFrameworkVersion |= (uint)v.Major;
657 				m_uFrameworkVersion <<= 16;
658 				if(v.Minor > 0) m_uFrameworkVersion |= (uint)v.Minor;
659 				m_uFrameworkVersion <<= 16;
660 				if(v.Build > 0) m_uFrameworkVersion |= (uint)v.Build;
661 				m_uFrameworkVersion <<= 16;
662 				if(v.Revision > 0) m_uFrameworkVersion |= (uint)v.Revision;
663 			}
664 
665 			return m_uFrameworkVersion;
666 		}
667 
GetMaxNetVersionPriv()668 		private static ulong GetMaxNetVersionPriv()
669 		{
670 			RegistryKey kNdp = Registry.LocalMachine.OpenSubKey(
671 				"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP", false);
672 			if(kNdp == null) { Debug.Assert(false); return 0; }
673 
674 			ulong uMaxVer = 0;
675 
676 			string[] vInNdp = kNdp.GetSubKeyNames();
677 			foreach(string strInNdp in vInNdp)
678 			{
679 				if(strInNdp == null) { Debug.Assert(false); continue; }
680 				if(!strInNdp.StartsWith("v", StrUtil.CaseIgnoreCmp)) continue;
681 
682 				RegistryKey kVer = kNdp.OpenSubKey(strInNdp, false);
683 				if(kVer != null)
684 				{
685 					UpdateNetVersionFromRegKey(kVer, ref uMaxVer);
686 
687 					string[] vProfiles = kVer.GetSubKeyNames();
688 					foreach(string strProfile in vProfiles)
689 					{
690 						if(string.IsNullOrEmpty(strProfile)) { Debug.Assert(false); continue; }
691 
692 						RegistryKey kPro = kVer.OpenSubKey(strProfile, false);
693 						UpdateNetVersionFromRegKey(kPro, ref uMaxVer);
694 						if(kPro != null) kPro.Close();
695 					}
696 
697 					kVer.Close();
698 				}
699 				else { Debug.Assert(false); }
700 			}
701 
702 			kNdp.Close();
703 			return uMaxVer;
704 		}
705 
UpdateNetVersionFromRegKey(RegistryKey k, ref ulong uMaxVer)706 		private static void UpdateNetVersionFromRegKey(RegistryKey k, ref ulong uMaxVer)
707 		{
708 			if(k == null) { Debug.Assert(false); return; }
709 
710 			try
711 			{
712 				// https://msdn.microsoft.com/en-us/library/hh925568.aspx
713 				string strInstall = k.GetValue("Install", string.Empty).ToString();
714 				if((strInstall.Length > 0) && (strInstall != "1")) return;
715 
716 				string strVer = k.GetValue("Version", string.Empty).ToString();
717 				if(strVer.Length > 0)
718 				{
719 					ulong uVer = StrUtil.ParseVersion(strVer);
720 					if(uVer > uMaxVer) uMaxVer = uVer;
721 				}
722 			}
723 			catch(Exception) { Debug.Assert(false); }
724 		}
725 
726 		/* private static ulong GetMaxNetVersionPriv()
727 		{
728 			string strSysRoot = Environment.GetEnvironmentVariable("SystemRoot");
729 			string strFrameworks = UrlUtil.EnsureTerminatingSeparator(strSysRoot,
730 				false) + "Microsoft.NET" + Path.DirectorySeparatorChar + "Framework";
731 			if(!Directory.Exists(strFrameworks)) { Debug.Assert(false); return 0; }
732 
733 			ulong uFrameworkVersion = 0;
734 			DirectoryInfo diFrameworks = new DirectoryInfo(strFrameworks);
735 			foreach(DirectoryInfo di in diFrameworks.GetDirectories("v*",
736 				SearchOption.TopDirectoryOnly))
737 			{
738 				string strVer = di.Name.TrimStart('v', 'V');
739 				ulong uVer = StrUtil.ParseVersion(strVer);
740 				if(uVer > uFrameworkVersion) uFrameworkVersion = uVer;
741 			}
742 
743 			return uFrameworkVersion;
744 		} */
745 
GetOSStr()746 		public static string GetOSStr()
747 		{
748 			if(NativeLib.IsUnix()) return "Unix";
749 			return "Windows";
750 		}
751 
RemoveZoneIdentifier(string strFilePath)752 		public static void RemoveZoneIdentifier(string strFilePath)
753 		{
754 			// No throw
755 			if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return; }
756 
757 			try
758 			{
759 				string strZoneId = strFilePath + ":Zone.Identifier";
760 
761 				if(NativeMethods.FileExists(strZoneId))
762 					NativeMethods.DeleteFile(strZoneId);
763 			}
764 			catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
765 		}
766 
767 		[Obsolete]
RunConsoleApp(string strAppPath, string strParams)768 		public static string RunConsoleApp(string strAppPath, string strParams)
769 		{
770 			return NativeLib.RunConsoleApp(strAppPath, strParams);
771 		}
772 
LocateSystemApp(string strExeName)773 		public static string LocateSystemApp(string strExeName)
774 		{
775 			if(strExeName == null) { Debug.Assert(false); return string.Empty; }
776 			if(strExeName.Length == 0) return strExeName;
777 
778 			if(NativeLib.IsUnix()) return strExeName;
779 
780 			try
781 			{
782 				string str = null;
783 				for(int i = 0; i < 3; ++i)
784 				{
785 					if(i == 0)
786 						str = Environment.GetFolderPath(
787 							Environment.SpecialFolder.System);
788 					else if(i == 1)
789 						str = Environment.GetEnvironmentVariable("WinDir");
790 					else if(i == 2)
791 						str = Environment.GetEnvironmentVariable("SystemRoot");
792 
793 					if(!string.IsNullOrEmpty(str))
794 					{
795 						str = UrlUtil.EnsureTerminatingSeparator(str, false);
796 						str += strExeName;
797 
798 						if(File.Exists(str)) return str;
799 					}
800 				}
801 			}
802 			catch(Exception) { Debug.Assert(false); }
803 
804 			return strExeName;
805 		}
806 
GetHomeDirectory()807 		public static string GetHomeDirectory()
808 		{
809 			string str = null;
810 			try
811 			{
812 				str = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
813 			}
814 			catch(Exception) { Debug.Assert(false); }
815 
816 			if(string.IsNullOrEmpty(str))
817 			{
818 				try
819 				{
820 					str = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
821 				}
822 				catch(Exception) { Debug.Assert(false); }
823 			}
824 
825 			if(string.IsNullOrEmpty(str)) { Debug.Assert(false); return string.Empty; }
826 
827 			return str;
828 		}
829 
GetWorkingDirectory()830 		public static string GetWorkingDirectory()
831 		{
832 			string strWorkDir = null;
833 			try { strWorkDir = Directory.GetCurrentDirectory(); }
834 			catch(Exception) { Debug.Assert(false); }
835 
836 			return (!string.IsNullOrEmpty(strWorkDir) ? strWorkDir : GetHomeDirectory());
837 		}
838 
SetWorkingDirectory(string strWorkDir)839 		public static void SetWorkingDirectory(string strWorkDir)
840 		{
841 			string str = strWorkDir; // May be null
842 
843 			if(!string.IsNullOrEmpty(str))
844 			{
845 				try { if(!Directory.Exists(str)) str = null; }
846 				catch(Exception) { Debug.Assert(false); str = null; }
847 			}
848 
849 			if(string.IsNullOrEmpty(str))
850 				str = GetHomeDirectory(); // Not app dir
851 
852 			try { Directory.SetCurrentDirectory(str); }
853 			catch(Exception) { Debug.Assert(false); }
854 		}
855 
ShowFileInFileManager(string strFilePath, bool bShowError)856 		internal static void ShowFileInFileManager(string strFilePath, bool bShowError)
857 		{
858 			if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return; }
859 
860 			try
861 			{
862 				string strDir = UrlUtil.GetFileDirectory(strFilePath, false, true);
863 				if(NativeLib.IsUnix())
864 				{
865 					NativeLib.StartProcess(strDir);
866 					return;
867 				}
868 
869 				string strExplorer = WinUtil.LocateSystemApp("Explorer.exe");
870 
871 				if(File.Exists(strFilePath))
872 					NativeLib.StartProcess(strExplorer, "/select,\"" +
873 						NativeLib.EncodeDataToArgs(strFilePath) + "\"");
874 				else
875 					NativeLib.StartProcess(strDir);
876 			}
877 			catch(Exception ex)
878 			{
879 				if(bShowError)
880 					MessageService.ShowWarning(strFilePath, ex.Message);
881 			}
882 		}
883 	}
884 }
885