diff --git a/TopMostFriend/Program.cs b/TopMostFriend/Program.cs index 8372876..7153c0c 100644 --- a/TopMostFriend/Program.cs +++ b/TopMostFriend/Program.cs @@ -1,8 +1,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; +using System.Linq; +using System.Security.Principal; +using System.Text; using System.Threading; using System.Windows.Forms; @@ -10,7 +14,6 @@ namespace TopMostFriend { public static class Program { private static NotifyIcon SysIcon; private static HotKeyWindow HotKeys; - private static readonly Process OwnProcess = Process.GetCurrentProcess(); private static int InitialItems = 0; private const string GUID = @@ -30,21 +33,38 @@ namespace TopMostFriend { public const string SHOW_EXPLORER_SETTING = @"ShowExplorerMisc"; public const string LIST_BACKGROUND_PATH_SETTING = @"ListBackgroundPath"; public const string LIST_BACKGROUND_LAYOUT_SETTING = @"ListBackgroundLayout"; + public const string ALWAYS_ADMIN_SETTING = @"RunAsAdministrator"; [STAThread] - public static void Main() { + public static void Main(string[] args) { if (Environment.OSVersion.Version.Major >= 6) Win32.SetProcessDPIAware(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + if (args.Contains(@"--reset-admin")) + Settings.Remove(ALWAYS_ADMIN_SETTING); + + string cliToggle = args.FirstOrDefault(x => x.StartsWith(@"--hwnd=")); + if (!string.IsNullOrEmpty(cliToggle) && int.TryParse(cliToggle.Substring(7), out int cliToggleHWnd)) + ToggleWindow(new IntPtr(cliToggleHWnd)); + + if (args.Contains(@"--stop")) + return; + if (!GlobalMutex.WaitOne(0, true)) { MessageBox.Show(@"An instance of Top Most Friend is already running.", @"Top Most Friend"); return; } - Settings.SetDefault(FOREGROUND_HOTKEY_SETTING, ((int)Keys.F << 16) | (int)(Win32ModKeys.MOD_CONTROL | Win32ModKeys.MOD_ALT)); + Settings.SetDefault(FOREGROUND_HOTKEY_SETTING, 0); + Settings.SetDefault(ALWAYS_ADMIN_SETTING, false); + + if (Settings.Get(ALWAYS_ADMIN_SETTING) && !IsElevated()) { + Elevate(); + return; + } string backgroundPath = Settings.Get(LIST_BACKGROUND_PATH_SETTING, string.Empty); Image backgroundImage = null; @@ -76,26 +96,67 @@ namespace TopMostFriend { InitialItems = SysIcon.ContextMenuStrip.Items.Count; HotKeys = new HotKeyWindow(); - SetForegroundHotKey(Settings.Get(FOREGROUND_HOTKEY_SETTING)); + + try { + SetForegroundHotKey(Settings.Get(FOREGROUND_HOTKEY_SETTING)); + } catch(Win32Exception ex) { + Console.WriteLine(@"Hotkey registration failed:"); + Console.WriteLine(ex); + } Application.Run(); - HotKeys.Dispose(); - SysIcon.Dispose(); + Shutdown(); + } + public static void Shutdown() { + HotKeys?.Dispose(); + SysIcon?.Dispose(); GlobalMutex.ReleaseMutex(); } + private static bool? IsElevatedValue; + + public static bool IsElevated() { + if (!IsElevatedValue.HasValue) { + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) + IsElevatedValue = identity != null && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator); + } + + return IsElevatedValue.Value; + } + + public static void Elevate(string args = null) { + if (IsElevated()) + return; + + Shutdown(); + + Process.Start(new ProcessStartInfo { + UseShellExecute = true, + FileName = Application.ExecutablePath, + WorkingDirectory = Environment.CurrentDirectory, + Arguments = args ?? string.Empty, + Verb = @"runas", + }); + Application.Exit(); + } + public static void SetForegroundHotKey(int keyCode) { SetForegroundHotKey((Win32ModKeys)(keyCode & 0xFFFF), (Keys)((keyCode & 0xFFFF0000) >> 16)); } public static void SetForegroundHotKey(Win32ModKeys mods, Keys key) { - Settings.Set(FOREGROUND_HOTKEY_SETTING, ((int)key << 16) | (int)mods); - HotKeys.Unregister(FOREGROUND_HOTKEY_ATOM); - - if(mods != 0 && key != 0) - HotKeys.Register(FOREGROUND_HOTKEY_ATOM, mods, key, ToggleForegroundWindow); + try { + Settings.Set(FOREGROUND_HOTKEY_SETTING, ((int)key << 16) | (int)mods); + HotKeys.Unregister(FOREGROUND_HOTKEY_ATOM); + + if (mods != 0 && key != 0) + HotKeys.Register(FOREGROUND_HOTKEY_ATOM, mods, key, ToggleForegroundWindow); + } catch (Win32Exception ex) { + Debug.WriteLine(@"Hotkey registration failed:"); + Debug.WriteLine(ex); + } } private static void RefreshWindowList() { @@ -150,12 +211,32 @@ namespace TopMostFriend { 0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_SHOWWINDOW ); + if(IsTopMost(hWnd) != state) { + MessageBoxButtons buttons = MessageBoxButtons.OK; + StringBuilder sb = new StringBuilder(); + sb.AppendLine(@"Wasn't able to change topmost status on this window."); + + if (!IsElevated()) { + sb.AppendLine(@"Do you want to restart Top Most Friend as administrator and try again?"); + buttons = MessageBoxButtons.YesNo; + } + + DialogResult result = MessageBox.Show(sb.ToString(), @"Top Most Friend", buttons, MessageBoxIcon.Error); + + if (result == DialogResult.Yes) + Elevate($@"--hwnd={hWnd}"); + return; + } + if (state) Win32.SwitchToThisWindow(hWnd, false); } public static void ToggleForegroundWindow() { - IntPtr hWnd = Win32.GetForegroundWindow(); + ToggleWindow(Win32.GetForegroundWindow()); + } + + public static void ToggleWindow(IntPtr hWnd) { SetTopMost(hWnd, !IsTopMost(hWnd)); } @@ -182,9 +263,10 @@ namespace TopMostFriend { private static IEnumerable GetWindowList() { Process[] procs = Process.GetProcesses(); + Process self = Process.GetCurrentProcess(); foreach (Process proc in procs) { - if (!Settings.Get(LIST_SELF_SETTING, Debugger.IsAttached) && proc.Id == OwnProcess.Id) + if (!Settings.Get(LIST_SELF_SETTING, Debugger.IsAttached) && proc == self) continue; IEnumerable hwnds = proc.GetWindowHandles(); diff --git a/TopMostFriend/Properties/AssemblyInfo.cs b/TopMostFriend/Properties/AssemblyInfo.cs index f380713..ae6c9c4 100644 --- a/TopMostFriend/Properties/AssemblyInfo.cs +++ b/TopMostFriend/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("1.1.0.0")] -[assembly: AssemblyFileVersion("1.1.0.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/TopMostFriend/SettingsWindow.cs b/TopMostFriend/SettingsWindow.cs index 2c59043..d6add69 100644 --- a/TopMostFriend/SettingsWindow.cs +++ b/TopMostFriend/SettingsWindow.cs @@ -27,13 +27,15 @@ namespace TopMostFriend { public readonly CheckBox FgModAlt; public readonly CheckBox FgModShift; + public readonly CheckBox FlAlwaysAdmin; + public SettingsWindow() { Text = @"Top Most Friend Settings"; Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); StartPosition = FormStartPosition.CenterScreen; FormBorderStyle = FormBorderStyle.FixedSingle; AutoScaleMode = AutoScaleMode.Dpi; - ClientSize = new Size(410, 113); + ClientSize = new Size(410, 163); MinimizeBox = MaximizeBox = false; MinimumSize = MaximumSize = Size; @@ -67,8 +69,14 @@ namespace TopMostFriend { Size = new Size(Width - 18, 70), }; + GroupBox flagsGroup = new GroupBox { + Text = @"Flags", + Location = new Point(6, 76), + Size = new Size(Width - 18, 50), + }; + Controls.AddRange(new Control[] { - applyButton, cancelButton, okButton, hotKeyGroup, + applyButton, cancelButton, okButton, hotKeyGroup, flagsGroup, }); Label toggleForegroundLabel = new Label { @@ -125,6 +133,15 @@ namespace TopMostFriend { hotKeyGroup.Controls.AddRange(new Control[] { toggleForegroundLabel, FgModCtrl, FgModAlt, FgModShift, fgReset, FgKey, }); + + FlAlwaysAdmin = new CheckBox { + Text = @"Always run as administrator", + Location = new Point(10, 20), + Checked = Settings.Get(Program.ALWAYS_ADMIN_SETTING, false), + AutoSize = true, + }; + + flagsGroup.Controls.Add(FlAlwaysAdmin); } private void FgReset_Click(object sender, EventArgs e) { @@ -135,6 +152,7 @@ namespace TopMostFriend { public void Apply() { Settings.Set(Program.FOREGROUND_HOTKEY_SETTING, KeyCode); + Settings.Set(Program.ALWAYS_ADMIN_SETTING, FlAlwaysAdmin.Checked); Program.SetForegroundHotKey(KeyCode); }