v1.5.0 - 2021-02-04

This commit is contained in:
flash 2022-08-26 01:59:00 +02:00
parent fbfe5c6837
commit 5a51414469
11 changed files with 263 additions and 185 deletions

View file

@ -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.

View file

@ -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,

View file

@ -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);
}
} }
} }

View file

@ -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")]

View file

@ -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>

View file

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

View file

@ -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);
} }

View file

@ -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>

View file

@ -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);

View 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));
}
}
}