mirror of
https://github.com/flashwave/topmostfriend.git
synced 2024-11-25 03:06:09 +00:00
v1.5.0 - 2021-02-04
This commit is contained in:
parent
fbfe5c6837
commit
5a51414469
11 changed files with 263 additions and 185 deletions
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2020 flashwave
|
Copyright (c) 2020-2021 flashwave
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted.
|
purpose with or without fee is hereby granted.
|
||||||
|
|
|
@ -45,6 +45,15 @@ namespace TopMostFriend {
|
||||||
websiteButton.Click += (s, e) => Process.Start(@"https://flash.moe/topmostfriend");
|
websiteButton.Click += (s, e) => Process.Start(@"https://flash.moe/topmostfriend");
|
||||||
Controls.Add(websiteButton);
|
Controls.Add(websiteButton);
|
||||||
|
|
||||||
|
Button donateButton = new Button {
|
||||||
|
Text = @"Donate",
|
||||||
|
Size = new Size(BUTTON_WIDTH, BUTTON_HEIGHT),
|
||||||
|
TabIndex = ++tabIndex,
|
||||||
|
};
|
||||||
|
donateButton.Location = new Point(websiteButton.Left - donateButton.Width - BUTTON_SPACING, closeButton.Top);
|
||||||
|
donateButton.Click += (s, e) => Process.Start(@"https://flash.moe/donate");
|
||||||
|
Controls.Add(donateButton);
|
||||||
|
|
||||||
Button creditButton = new Button {
|
Button creditButton = new Button {
|
||||||
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top,
|
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top,
|
||||||
Text = string.Empty,
|
Text = string.Empty,
|
||||||
|
|
|
@ -13,9 +13,9 @@ using System.Windows.Forms;
|
||||||
namespace TopMostFriend {
|
namespace TopMostFriend {
|
||||||
public static class Program {
|
public static class Program {
|
||||||
private static NotifyIcon SysIcon;
|
private static NotifyIcon SysIcon;
|
||||||
|
private static ContextMenuStrip CtxMenu;
|
||||||
private static HotKeyWindow HotKeys;
|
private static HotKeyWindow HotKeys;
|
||||||
private static Icon OriginalIcon;
|
private static Icon OriginalIcon;
|
||||||
private static int InitialItems = 0;
|
|
||||||
|
|
||||||
private const string GUID =
|
private const string GUID =
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -43,9 +43,16 @@ namespace TopMostFriend {
|
||||||
public const string SHIFT_CLICK_BLACKLIST = @"ShiftClickToBlacklist";
|
public const string SHIFT_CLICK_BLACKLIST = @"ShiftClickToBlacklist";
|
||||||
public const string TITLE_BLACKLIST = @"TitleBlacklist";
|
public const string TITLE_BLACKLIST = @"TitleBlacklist";
|
||||||
public const string SHOW_HOTKEY_ICON = @"ShowHotkeyIcon";
|
public const string SHOW_HOTKEY_ICON = @"ShowHotkeyIcon";
|
||||||
|
public const string SHOW_WINDOW_LIST = @"ShowWindowList";
|
||||||
|
|
||||||
|
private static ToolStripItem RefreshButton;
|
||||||
|
private static ToolStripItem LastSelectedItem = null;
|
||||||
|
|
||||||
private static readonly List<string> TitleBlacklist = new List<string>();
|
private static readonly List<string> TitleBlacklist = new List<string>();
|
||||||
|
|
||||||
|
private static ToolStripItem[] ListActionItems;
|
||||||
|
private static ToolStripItem[] AppActionItems;
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args) {
|
public static void Main(string[] args) {
|
||||||
if(Environment.OSVersion.Version.Major >= 6)
|
if(Environment.OSVersion.Version.Major >= 6)
|
||||||
|
@ -58,8 +65,11 @@ namespace TopMostFriend {
|
||||||
Settings.Remove(ALWAYS_ADMIN_SETTING);
|
Settings.Remove(ALWAYS_ADMIN_SETTING);
|
||||||
|
|
||||||
string cliToggle = args.FirstOrDefault(x => x.StartsWith(@"--hwnd="));
|
string cliToggle = args.FirstOrDefault(x => x.StartsWith(@"--hwnd="));
|
||||||
if (!string.IsNullOrEmpty(cliToggle) && int.TryParse(cliToggle.Substring(7), out int cliToggleHWnd))
|
if(!string.IsNullOrEmpty(cliToggle) && int.TryParse(cliToggle.Substring(7), out int cliToggleHWnd)) {
|
||||||
ToggleWindow(new IntPtr(cliToggleHWnd));
|
WindowInfo cliWindow = new WindowInfo(cliToggleHWnd);
|
||||||
|
if(!cliWindow.ToggleTopMost())
|
||||||
|
TopMostFailed(cliWindow);
|
||||||
|
}
|
||||||
|
|
||||||
if(args.Contains(@"--stop"))
|
if(args.Contains(@"--stop"))
|
||||||
return;
|
return;
|
||||||
|
@ -73,6 +83,7 @@ namespace TopMostFriend {
|
||||||
Settings.SetDefault(ALWAYS_ADMIN_SETTING, false);
|
Settings.SetDefault(ALWAYS_ADMIN_SETTING, false);
|
||||||
Settings.SetDefault(SHIFT_CLICK_BLACKLIST, true);
|
Settings.SetDefault(SHIFT_CLICK_BLACKLIST, true);
|
||||||
Settings.SetDefault(SHOW_HOTKEY_ICON, true);
|
Settings.SetDefault(SHOW_HOTKEY_ICON, true);
|
||||||
|
Settings.SetDefault(SHOW_WINDOW_LIST, true);
|
||||||
// Defaulting to false on Windows 10 because it uses the stupid, annoying and intrusive new Android style notification system
|
// Defaulting to false on Windows 10 because it uses the stupid, annoying and intrusive new Android style notification system
|
||||||
// This would fucking piledrive the notification history and also just be annoying in general because intrusive
|
// This would fucking piledrive the notification history and also just be annoying in general because intrusive
|
||||||
Settings.SetDefault(TOGGLE_BALLOON_SETTING, ToggleBalloonDefault);
|
Settings.SetDefault(TOGGLE_BALLOON_SETTING, ToggleBalloonDefault);
|
||||||
|
@ -115,32 +126,33 @@ namespace TopMostFriend {
|
||||||
|
|
||||||
OriginalIcon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
OriginalIcon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
|
||||||
|
|
||||||
|
CtxMenu = new ContextMenuStrip {
|
||||||
|
BackgroundImage = backgroundImage,
|
||||||
|
BackgroundImageLayout = backgroundLayout,
|
||||||
|
};
|
||||||
|
CtxMenu.Closing += CtxMenu_Closing;
|
||||||
|
CtxMenu.ItemClicked += CtxMenu_ItemClicked;
|
||||||
|
ListActionItems = new ToolStripItem[] {
|
||||||
|
new ToolStripSeparator(),
|
||||||
|
RefreshButton = new ToolStripMenuItem(@"&Refresh", Properties.Resources.arrow_refresh, new EventHandler((s, e) => RefreshWindowList())),
|
||||||
|
};
|
||||||
|
AppActionItems = new ToolStripItem[] {
|
||||||
|
new ToolStripMenuItem(@"&Settings", Properties.Resources.cog, new EventHandler((s, e) => SettingsWindow.Display())),
|
||||||
|
new ToolStripMenuItem(@"&About", Properties.Resources.help, new EventHandler((s, e) => AboutWindow.Display())),
|
||||||
|
new ToolStripMenuItem(@"&Quit", Properties.Resources.door_in, new EventHandler((s, e) => Application.Exit())),
|
||||||
|
};
|
||||||
|
CtxMenu.Items.AddRange(AppActionItems);
|
||||||
|
|
||||||
SysIcon = new NotifyIcon {
|
SysIcon = new NotifyIcon {
|
||||||
Visible = true,
|
Visible = true,
|
||||||
Icon = OriginalIcon,
|
Icon = OriginalIcon,
|
||||||
Text = @"Top Most Application Manager",
|
Text = @"Top Most Application Manager",
|
||||||
};
|
};
|
||||||
|
SysIcon.ContextMenuStrip = CtxMenu;
|
||||||
SysIcon.MouseDown += SysIcon_MouseDown;
|
SysIcon.MouseDown += SysIcon_MouseDown;
|
||||||
SysIcon.ContextMenuStrip = new ContextMenuStrip {
|
|
||||||
BackgroundImage = backgroundImage,
|
|
||||||
BackgroundImageLayout = backgroundLayout,
|
|
||||||
};
|
|
||||||
SysIcon.ContextMenuStrip.Items.AddRange(new ToolStripItem[] {
|
|
||||||
new ToolStripSeparator(),
|
|
||||||
new ToolStripMenuItem(@"&Settings", Properties.Resources.cog, new EventHandler((s, e) => SettingsWindow.Display())),
|
|
||||||
new ToolStripMenuItem(@"&About", Properties.Resources.help, new EventHandler((s, e) => AboutWindow.Display())),
|
|
||||||
new ToolStripMenuItem(@"&Quit", Properties.Resources.door_in, new EventHandler((s, e) => Application.Exit())),
|
|
||||||
});
|
|
||||||
InitialItems = SysIcon.ContextMenuStrip.Items.Count;
|
|
||||||
|
|
||||||
HotKeys = new HotKeyWindow();
|
HotKeys = new HotKeyWindow();
|
||||||
|
|
||||||
try {
|
|
||||||
SetForegroundHotKey(Settings.Get<int>(FOREGROUND_HOTKEY_SETTING));
|
SetForegroundHotKey(Settings.Get<int>(FOREGROUND_HOTKEY_SETTING));
|
||||||
} catch(Win32Exception ex) {
|
|
||||||
Console.WriteLine(@"Hotkey registration failed:");
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Application.Run();
|
Application.Run();
|
||||||
|
|
||||||
|
@ -225,23 +237,24 @@ namespace TopMostFriend {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RefreshWindowList() {
|
private static void RefreshWindowList() {
|
||||||
while (SysIcon.ContextMenuStrip.Items.Count > InitialItems)
|
IEnumerable<WindowInfo> windows = WindowInfo.GetAllWindows();
|
||||||
SysIcon.ContextMenuStrip.Items.RemoveAt(0);
|
List<ToolStripItem> items = new List<ToolStripItem>();
|
||||||
|
|
||||||
IEnumerable<WindowEntry> windows = GetWindowList();
|
|
||||||
Process lastProc = null;
|
Process lastProc = null;
|
||||||
bool procSeparator = Settings.Get(PROCESS_SEPARATOR_SETTING, false);
|
bool procSeparator = Settings.Get(PROCESS_SEPARATOR_SETTING, false);
|
||||||
bool showEmptyTitles = Settings.Get(SHOW_EMPTY_WINDOW_SETTING, false);
|
bool showEmptyTitles = Settings.Get(SHOW_EMPTY_WINDOW_SETTING, false);
|
||||||
bool shiftClickBlacklist = Settings.Get(SHIFT_CLICK_BLACKLIST, true);
|
bool listSelf = Settings.Get(LIST_SELF_SETTING, Debugger.IsAttached);
|
||||||
|
|
||||||
foreach(WindowEntry window in windows) {
|
foreach(WindowInfo window in windows) {
|
||||||
if(procSeparator && lastProc != window.Process) {
|
if(!listSelf && window.IsOwnWindow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(procSeparator && lastProc != window.Owner) {
|
||||||
if(lastProc != null)
|
if(lastProc != null)
|
||||||
SysIcon.ContextMenuStrip.Items.Insert(0, new ToolStripSeparator());
|
items.Add(new ToolStripSeparator());
|
||||||
lastProc = window.Process;
|
lastProc = window.Owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
string title = Win32.GetWindowTextString(window.Window);
|
string title = window.Title;
|
||||||
|
|
||||||
// i think it's a fair assumption that any visible window worth a damn has a window title
|
// i think it's a fair assumption that any visible window worth a damn has a window title
|
||||||
if(!showEmptyTitles && string.IsNullOrEmpty(title))
|
if(!showEmptyTitles && string.IsNullOrEmpty(title))
|
||||||
|
@ -251,28 +264,39 @@ namespace TopMostFriend {
|
||||||
if(CheckBlacklistedTitles(title))
|
if(CheckBlacklistedTitles(title))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Image icon = GetWindowIcon(window.Window)?.ToBitmap() ?? null;
|
items.Add(new ToolStripMenuItem(title, window.IconBitmap, new EventHandler((s, e) => {
|
||||||
bool isTopMost = IsTopMost(window.Window);
|
if(Settings.Get(SHIFT_CLICK_BLACKLIST, true) && Control.ModifierKeys.HasFlag(Keys.Shift)) {
|
||||||
|
|
||||||
SysIcon.ContextMenuStrip.Items.Insert(0, new ToolStripMenuItem(
|
|
||||||
title, icon,
|
|
||||||
new EventHandler((s, e) => {
|
|
||||||
if (shiftClickBlacklist && Control.ModifierKeys.HasFlag(Keys.Shift)) {
|
|
||||||
AddBlacklistedTitle(title);
|
AddBlacklistedTitle(title);
|
||||||
SaveBlacklistedTitles();
|
SaveBlacklistedTitles();
|
||||||
} else
|
} else if(!window.ToggleTopMost())
|
||||||
SetTopMost(window.Window, !isTopMost);
|
TopMostFailed(window);
|
||||||
})
|
})) {
|
||||||
) {
|
|
||||||
CheckOnClick = true,
|
CheckOnClick = true,
|
||||||
Checked = isTopMost,
|
Checked = window.IsTopMost,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items.AddRange(ListActionItems);
|
||||||
|
items.AddRange(AppActionItems);
|
||||||
|
|
||||||
|
CtxMenu.Items.Clear();
|
||||||
|
CtxMenu.Items.AddRange(items.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsTopMost(IntPtr hWnd) {
|
private static void TopMostFailed(WindowInfo window) {
|
||||||
IntPtr flags = Win32.GetWindowLongPtr(hWnd, Win32.GWL_EXSTYLE);
|
MessageBoxButtons buttons = MessageBoxButtons.OK;
|
||||||
return (flags.ToInt32() & Win32.WS_EX_TOPMOST) > 0;
|
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={window.Handle}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ActionTimeout {
|
private class ActionTimeout {
|
||||||
|
@ -306,53 +330,23 @@ namespace TopMostFriend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool SetTopMost(IntPtr hWnd, bool state) {
|
|
||||||
Win32.SetWindowPos(
|
|
||||||
hWnd, new IntPtr(state ? Win32.HWND_TOPMOST : Win32.HWND_NOTOPMOST),
|
|
||||||
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 false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state)
|
|
||||||
Win32.SwitchToThisWindow(hWnd, false);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActionTimeout IconTimeout;
|
private static ActionTimeout IconTimeout;
|
||||||
|
|
||||||
public static void ToggleForegroundWindow() {
|
public static void ToggleForegroundWindow() {
|
||||||
IntPtr hWnd = Win32.GetForegroundWindow();
|
WindowInfo window = WindowInfo.GetForegroundWindow();
|
||||||
|
|
||||||
if (ToggleWindow(hWnd)) {
|
if(window.ToggleTopMost()) {
|
||||||
if(Settings.Get(TOGGLE_BALLOON_SETTING, false)) {
|
if(Settings.Get(TOGGLE_BALLOON_SETTING, false)) {
|
||||||
string title = Win32.GetWindowTextString(hWnd);
|
string title = window.Title;
|
||||||
|
|
||||||
SysIcon?.ShowBalloonTip(
|
SysIcon?.ShowBalloonTip(
|
||||||
2000, IsTopMost(hWnd) ? @"Always on top" : @"No longer always on top",
|
2000, window.IsTopMost ? @"Always on top" : @"No longer always on top",
|
||||||
string.IsNullOrEmpty(title) ? @"Window has no title." : title,
|
string.IsNullOrEmpty(title) ? @"Window has no title." : title,
|
||||||
ToolTipIcon.Info
|
ToolTipIcon.Info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SysIcon != null && Settings.Get(SHOW_HOTKEY_ICON, true)) {
|
if(SysIcon != null && Settings.Get(SHOW_HOTKEY_ICON, true)) {
|
||||||
Icon icon = GetWindowIcon(hWnd);
|
Icon icon = window.Icon;
|
||||||
|
|
||||||
if(icon != null) {
|
if(icon != null) {
|
||||||
IconTimeout?.Cancel();
|
IconTimeout?.Cancel();
|
||||||
|
@ -360,78 +354,22 @@ namespace TopMostFriend {
|
||||||
IconTimeout = new ActionTimeout(() => SysIcon.Icon = OriginalIcon, 2000);
|
IconTimeout = new ActionTimeout(() => SysIcon.Icon = OriginalIcon, 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
TopMostFailed(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool ToggleWindow(IntPtr hWnd) {
|
private static void CtxMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
|
||||||
return SetTopMost(hWnd, !IsTopMost(hWnd));
|
LastSelectedItem = e.ClickedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Icon GetWindowIcon(IntPtr hWnd) {
|
private static void CtxMenu_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
|
||||||
IntPtr hIcon = Win32.SendMessage(hWnd, Win32.WM_GETICON, Win32.ICON_SMALL2, 0);
|
if(e.CloseReason == ToolStripDropDownCloseReason.ItemClicked && LastSelectedItem == RefreshButton)
|
||||||
|
e.Cancel = true;
|
||||||
if(hIcon == IntPtr.Zero) {
|
|
||||||
hIcon = Win32.SendMessage(hWnd, Win32.WM_GETICON, Win32.ICON_SMALL, 0);
|
|
||||||
|
|
||||||
if(hIcon == IntPtr.Zero) {
|
|
||||||
hIcon = Win32.SendMessage(hWnd, Win32.WM_GETICON, Win32.ICON_BIG, 0);
|
|
||||||
|
|
||||||
if(hIcon == IntPtr.Zero) {
|
|
||||||
hIcon = Win32.GetClassLongPtr(hWnd, Win32.GCL_HICON);
|
|
||||||
|
|
||||||
if (hIcon == IntPtr.Zero)
|
|
||||||
hIcon = Win32.GetClassLongPtr(hWnd, Win32.GCL_HICONSM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<WindowEntry> GetWindowList() {
|
|
||||||
Process[] procs = Process.GetProcesses();
|
|
||||||
Process self = Process.GetCurrentProcess();
|
|
||||||
|
|
||||||
foreach (Process proc in procs) {
|
|
||||||
if (!Settings.Get(LIST_SELF_SETTING, Debugger.IsAttached) && proc == self)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
IEnumerable<IntPtr> hwnds = proc.GetWindowHandles();
|
|
||||||
|
|
||||||
foreach (IntPtr ptr in hwnds) {
|
|
||||||
if (!Win32.IsWindowVisible(ptr))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
yield return new WindowEntry(proc, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WindowEntry {
|
|
||||||
public Process Process;
|
|
||||||
public IntPtr Window;
|
|
||||||
|
|
||||||
public WindowEntry(Process proc, IntPtr win) {
|
|
||||||
Process = proc;
|
|
||||||
Window = win;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SysIcon_MouseDown(object sender, MouseEventArgs e) {
|
private static void SysIcon_MouseDown(object sender, MouseEventArgs e) {
|
||||||
if (e.Button.HasFlag(MouseButtons.Right))
|
if((e.Button & MouseButtons.Right) > 0)
|
||||||
RefreshWindowList();
|
RefreshWindowList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<IntPtr> GetWindowHandles(this Process proc) {
|
|
||||||
IntPtr hwndCurr = IntPtr.Zero;
|
|
||||||
|
|
||||||
do {
|
|
||||||
hwndCurr = Win32.FindWindowEx(IntPtr.Zero, hwndCurr, null, null);
|
|
||||||
Win32.GetWindowThreadProcessId(hwndCurr, out uint procId);
|
|
||||||
|
|
||||||
if(proc.Id == procId)
|
|
||||||
yield return hwndCurr;
|
|
||||||
} while (hwndCurr != IntPtr.Zero);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("flashwave")]
|
[assembly: AssemblyCompany("flashwave")]
|
||||||
[assembly: AssemblyProduct("TopMostFriend")]
|
[assembly: AssemblyProduct("TopMostFriend")]
|
||||||
[assembly: AssemblyCopyright("flashwave 2020")]
|
[assembly: AssemblyCopyright("flashwave 2020-2021")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
|
||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
[assembly: AssemblyVersion("1.4.2.0")]
|
[assembly: AssemblyVersion("1.5.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.4.2.0")]
|
[assembly: AssemblyFileVersion("1.5.0.0")]
|
||||||
|
|
10
TopMostFriend/Properties/Resources.Designer.cs
generated
10
TopMostFriend/Properties/Resources.Designer.cs
generated
|
@ -70,6 +70,16 @@ namespace TopMostFriend.Properties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap arrow_refresh {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("arrow_refresh", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -121,6 +121,9 @@
|
||||||
<data name="about" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="about" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\about.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\about.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="arrow_refresh" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\arrow_refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
<data name="cog" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="cog" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\cog.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\cog.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
BIN
TopMostFriend/Resources/arrow_refresh.png
Normal file
BIN
TopMostFriend/Resources/arrow_refresh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 685 B |
|
@ -28,6 +28,7 @@ namespace TopMostFriend {
|
||||||
public readonly CheckBox FlToggleNotification;
|
public readonly CheckBox FlToggleNotification;
|
||||||
public readonly CheckBox FlShiftClickBlacklist;
|
public readonly CheckBox FlShiftClickBlacklist;
|
||||||
public readonly CheckBox FlShowHotkeyIcon;
|
public readonly CheckBox FlShowHotkeyIcon;
|
||||||
|
public readonly CheckBox FlShowWindowList;
|
||||||
|
|
||||||
public SettingsWindow() {
|
public SettingsWindow() {
|
||||||
Text = @"Top Most Friend Settings";
|
Text = @"Top Most Friend Settings";
|
||||||
|
@ -35,7 +36,7 @@ namespace TopMostFriend {
|
||||||
StartPosition = FormStartPosition.CenterScreen;
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||||
AutoScaleMode = AutoScaleMode.Dpi;
|
AutoScaleMode = AutoScaleMode.Dpi;
|
||||||
ClientSize = new Size(430, 278);
|
ClientSize = new Size(430, 298);
|
||||||
MinimizeBox = MaximizeBox = false;
|
MinimizeBox = MaximizeBox = false;
|
||||||
MinimumSize = MaximumSize = Size;
|
MinimumSize = MaximumSize = Size;
|
||||||
|
|
||||||
|
@ -72,17 +73,18 @@ namespace TopMostFriend {
|
||||||
GroupBox flagsGroup = new GroupBox {
|
GroupBox flagsGroup = new GroupBox {
|
||||||
Text = @"Flags",
|
Text = @"Flags",
|
||||||
Location = new Point(6, 76),
|
Location = new Point(6, 76),
|
||||||
Size = new Size(Width - 18, 110),
|
Size = new Size(Width - 18, 130),
|
||||||
};
|
};
|
||||||
|
|
||||||
GroupBox blackListGroup = new GroupBox {
|
GroupBox blackListGroup = new GroupBox {
|
||||||
Text = @"Blacklist",
|
Text = @"Blacklist",
|
||||||
Location = new Point(6, 186),
|
Location = new Point(6, 206),
|
||||||
Size = new Size(Width - 18, 55),
|
Size = new Size(Width - 18, 55),
|
||||||
};
|
};
|
||||||
|
|
||||||
Controls.AddRange(new Control[] {
|
Controls.AddRange(new Control[] {
|
||||||
applyButton, cancelButton, okButton, hotKeyGroup, flagsGroup, blackListGroup,
|
applyButton, cancelButton, okButton,
|
||||||
|
hotKeyGroup, flagsGroup, blackListGroup,
|
||||||
});
|
});
|
||||||
|
|
||||||
Label toggleForegroundLabel = new Label {
|
Label toggleForegroundLabel = new Label {
|
||||||
|
@ -184,8 +186,18 @@ namespace TopMostFriend {
|
||||||
AutoSize = true,
|
AutoSize = true,
|
||||||
TabIndex = 204,
|
TabIndex = 204,
|
||||||
};
|
};
|
||||||
|
FlShowWindowList = new CheckBox {
|
||||||
|
Text = @"Show list of open windows in the task bar context menu",
|
||||||
|
Location = new Point(10, 100),
|
||||||
|
Checked = Settings.Get(Program.SHOW_WINDOW_LIST, true),
|
||||||
|
AutoSize = true,
|
||||||
|
TabIndex = 205,
|
||||||
|
};
|
||||||
|
|
||||||
flagsGroup.Controls.AddRange(new[] { FlAlwaysAdmin, FlToggleNotification, FlShiftClickBlacklist, FlShowHotkeyIcon, });
|
flagsGroup.Controls.AddRange(new[] {
|
||||||
|
FlAlwaysAdmin, FlToggleNotification, FlShiftClickBlacklist,
|
||||||
|
FlShowHotkeyIcon, FlShowWindowList,
|
||||||
|
});
|
||||||
|
|
||||||
Button titleBlacklist = new Button {
|
Button titleBlacklist = new Button {
|
||||||
Size = new Size(120, 23),
|
Size = new Size(120, 23),
|
||||||
|
@ -217,6 +229,7 @@ namespace TopMostFriend {
|
||||||
Settings.Set(Program.TOGGLE_BALLOON_SETTING, FlToggleNotification.Checked);
|
Settings.Set(Program.TOGGLE_BALLOON_SETTING, FlToggleNotification.Checked);
|
||||||
Settings.Set(Program.SHIFT_CLICK_BLACKLIST, FlShiftClickBlacklist.Checked);
|
Settings.Set(Program.SHIFT_CLICK_BLACKLIST, FlShiftClickBlacklist.Checked);
|
||||||
Settings.Set(Program.SHOW_HOTKEY_ICON, FlShowHotkeyIcon.Checked);
|
Settings.Set(Program.SHOW_HOTKEY_ICON, FlShowHotkeyIcon.Checked);
|
||||||
|
Settings.Set(Program.SHOW_WINDOW_LIST, FlShowWindowList.Checked);
|
||||||
Program.SetForegroundHotKey(KeyCode);
|
Program.SetForegroundHotKey(KeyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Win32.cs" />
|
<Compile Include="Win32.cs" />
|
||||||
|
<Compile Include="WindowInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
@ -78,6 +79,7 @@
|
||||||
<None Include="Resources\door_in.png" />
|
<None Include="Resources\door_in.png" />
|
||||||
<None Include="Resources\about.png" />
|
<None Include="Resources\about.png" />
|
||||||
<None Include="Resources\cog.png" />
|
<None Include="Resources\cog.png" />
|
||||||
|
<None Include="Resources\arrow_refresh.png" />
|
||||||
<Content Include="TopMostFriend.ico" />
|
<Content Include="TopMostFriend.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -43,6 +43,8 @@ namespace TopMostFriend {
|
||||||
public const int ICON_BIG = 1;
|
public const int ICON_BIG = 1;
|
||||||
public const int ICON_SMALL2 = 2;
|
public const int ICON_SMALL2 = 2;
|
||||||
|
|
||||||
|
public delegate bool EnumWindowsProc([In] IntPtr hWnd, [In] int lParam);
|
||||||
|
|
||||||
[DllImport(@"user32")]
|
[DllImport(@"user32")]
|
||||||
public static extern bool SetProcessDPIAware();
|
public static extern bool SetProcessDPIAware();
|
||||||
|
|
||||||
|
@ -114,6 +116,9 @@ namespace TopMostFriend {
|
||||||
[DllImport(@"user32", SetLastError = true)]
|
[DllImport(@"user32", SetLastError = true)]
|
||||||
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||||
|
|
||||||
|
[DllImport(@"user32", SetLastError = true)]
|
||||||
|
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, int lParam);
|
||||||
|
|
||||||
[DllImport(@"kernel32", SetLastError = true, CharSet = CharSet.Auto)]
|
[DllImport(@"kernel32", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
public static extern ushort GlobalAddAtom(string lpString);
|
public static extern ushort GlobalAddAtom(string lpString);
|
||||||
|
|
||||||
|
|
98
TopMostFriend/WindowInfo.cs
Normal file
98
TopMostFriend/WindowInfo.cs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TopMostFriend {
|
||||||
|
public class WindowInfo {
|
||||||
|
public IntPtr Handle { get; }
|
||||||
|
public Process Owner { get; }
|
||||||
|
|
||||||
|
public string Title => Win32.GetWindowTextString(Handle);
|
||||||
|
|
||||||
|
public long Flags => Win32.GetWindowLongPtr(Handle, Win32.GWL_EXSTYLE).ToInt32();
|
||||||
|
public bool IsTopMost {
|
||||||
|
get => (Flags & Win32.WS_EX_TOPMOST) > 0;
|
||||||
|
set {
|
||||||
|
Win32.SetWindowPos(
|
||||||
|
Handle, new IntPtr(value ? Win32.HWND_TOPMOST : Win32.HWND_NOTOPMOST),
|
||||||
|
0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_SHOWWINDOW
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsOwnWindow
|
||||||
|
=> Owner == Process.GetCurrentProcess();
|
||||||
|
|
||||||
|
public Icon Icon {
|
||||||
|
get {
|
||||||
|
IntPtr icon = Win32.SendMessage(Handle, Win32.WM_GETICON, Win32.ICON_SMALL2, 0);
|
||||||
|
|
||||||
|
if(icon == IntPtr.Zero) {
|
||||||
|
icon = Win32.SendMessage(Handle, Win32.WM_GETICON, Win32.ICON_SMALL, 0);
|
||||||
|
|
||||||
|
if(icon == IntPtr.Zero) {
|
||||||
|
icon = Win32.SendMessage(Handle, Win32.WM_GETICON, Win32.ICON_BIG, 0);
|
||||||
|
|
||||||
|
if(icon == IntPtr.Zero) {
|
||||||
|
icon = Win32.GetClassLongPtr(Handle, Win32.GCL_HICON);
|
||||||
|
|
||||||
|
if(icon == IntPtr.Zero)
|
||||||
|
icon = Win32.GetClassLongPtr(Handle, Win32.GCL_HICONSM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return icon == IntPtr.Zero ? null : Icon.FromHandle(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image IconBitmap
|
||||||
|
=> Icon?.ToBitmap();
|
||||||
|
|
||||||
|
public WindowInfo(int handle)
|
||||||
|
: this(new IntPtr(handle)) {}
|
||||||
|
|
||||||
|
public WindowInfo(IntPtr handle)
|
||||||
|
: this(handle, FindOwner(handle)) {}
|
||||||
|
|
||||||
|
public WindowInfo(IntPtr handle, Process owner) {
|
||||||
|
Handle = handle;
|
||||||
|
Owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SwitchTo() {
|
||||||
|
Win32.SwitchToThisWindow(Handle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToggleTopMost() {
|
||||||
|
bool expected = !IsTopMost;
|
||||||
|
IsTopMost = expected;
|
||||||
|
bool success = IsTopMost == expected;
|
||||||
|
if(expected && success)
|
||||||
|
SwitchTo();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Process FindOwner(IntPtr hWnd) {
|
||||||
|
Win32.GetWindowThreadProcessId(hWnd, out uint procId);
|
||||||
|
return Process.GetProcessById((int)procId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WindowInfo GetForegroundWindow() {
|
||||||
|
return new WindowInfo(Win32.GetForegroundWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<WindowInfo> GetAllWindows(bool includeHidden = false) {
|
||||||
|
List<IntPtr> windows = new List<IntPtr>();
|
||||||
|
Win32.EnumWindows(new Win32.EnumWindowsProc((hWnd, lParam) => {
|
||||||
|
if(includeHidden || Win32.IsWindowVisible(hWnd))
|
||||||
|
windows.Add(hWnd);
|
||||||
|
return true;
|
||||||
|
}), 0);
|
||||||
|
return windows.Select(w => new WindowInfo(w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue