我想在所有Windows的系统菜单(右键单击标题Bar或单击图标时打开的菜单)中添加“始终在顶部”菜单。
有一些这样做的应用程序,但是我找不到有用的源代码或免费应用程序,而不仅仅是它们自己的所有窗口。还有其他实现此功能的方法(Autohotkeys或属于系统托盘中的小程序,让您选择应该保持在顶部的窗口),但是我正在寻找一种更加流利和直观的方式。
思想上,我会在标题栏中添加一个小别针按钮,但我认为这更多的是涉及,所以我现在坚持菜单变体。
使用
AddMenuItems()
ApplicationExit
活动中,可能的工作可能会召唤
GetMenu(hMainWindowHandle, true)
,其中true表示还原菜单。
public static class AlwaysOnTop {
static AlwaysOnTop() {
Application.ApplicationExit += delegate {
try {
foreach (DictionaryEntry de in htThreads) {
Hook h = (Hook) de.Value;
RemoveMenu(h.hMenu, h.uniqueId, 0);
//DeleteMenu(h.hMenu, h.uniqueId, 0);
UnhookWinEvent(h.hWinEventHook);
}
} catch {
}
};
}
private const int EVENT_OBJECT_INVOKED = 0x8013;
private const int OBJID_SYSMENU = -1;
private const int WINEVENT_OUTOFCONTEXT = 0;
private const int MF_STRING = 0x00000000;
private const int HWND_TOPMOST = -1;
private const int HWND_NOTOPMOST = -2;
private const int SWP_NOMOVE = 0x0002;
private const int SWP_NOSIZE = 0x0001;
private const uint MF_UNCHECKED = 0x00000000;
private const uint MF_CHECKED = 0x00000008;
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool AppendMenu(IntPtr hMenu, uint uFlags, uint uIDNewItem, String lpNewItem);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll",SetLastError=true)]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern bool CheckMenuItem(IntPtr hMenu, uint uIDCheckItem, uint uCheck);
[DllImport("user32.dll")]
private static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
//[DllImport("user32.dll")]
//private static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
private static Hashtable htThreads = new Hashtable();
private static WinEventProc CallWinEventProc = new WinEventProc(EventCallback);
private delegate void WinEventProc(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
private static void EventCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
//callback function, called when message is intercepted
if (iEvent == EVENT_OBJECT_INVOKED) {
if (idObject == OBJID_SYSMENU) {
Hook h = (Hook) htThreads[(uint) dwEventThread];
if (h != null && h.uniqueId == idChild) {
bool b = !h.Checked;
if (b)
SetWindowPos(h.hMainWindowHandle, (IntPtr) HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
else
SetWindowPos(h.hMainWindowHandle, (IntPtr) HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
CheckMenuItem(h.hMenu, h.uniqueId, (b ? MF_CHECKED : MF_UNCHECKED));
h.Checked = b;
}
}
}
}
private class Hook {
public uint uniqueId = 1001;
public IntPtr hWinEventHook;
public IntPtr hMenu;
public IntPtr hMainWindowHandle;
public bool Checked;
}
public static void AddMenuItems() {
Process[] arr = Process.GetProcesses();
foreach (Process p in arr) {
if (p.MainWindowHandle == IntPtr.Zero)
continue;
if (p.ProcessName != "mspaint") // <-- remove or change this line
continue;
IntPtr hMenu = GetSystemMenu(p.MainWindowHandle, false);
if (hMenu == IntPtr.Zero)
continue;
bool b = AppendMenu(hMenu, MF_STRING, 1001, "Always On Top");
uint pid = 0;
uint tid = GetWindowThreadProcessId(p.MainWindowHandle, out pid);
Hook h = (Hook) htThreads[tid];
if (h == null) {
h = new Hook();
h.hMenu = hMenu;
h.hWinEventHook = SetWinEventHook(EVENT_OBJECT_INVOKED, EVENT_OBJECT_INVOKED, IntPtr.Zero, CallWinEventProc, pid, tid, WINEVENT_OUTOFCONTEXT);
h.hMainWindowHandle = p.MainWindowHandle;
htThreads[tid] = h;
}
}
}
}
这里是使用ToolStripDropDown
而不是默认窗口菜单的示例。可以通过设置背景颜色,字体并在需要时添加一些图标来使其看起来更像默认的窗口菜单。 隐藏默认窗口菜单的难以置信的是,它更加困难。也许有更好的方法。隐藏菜单的失败尝试留在下面的代码中。右键单击字幕栏时仍显示默认菜单。
public class FormCustomMenu : Form {
WindowMenu WindowMenu = new WindowMenu();
public FormCustomMenu() {
//this.ShowIcon = false;
}
private const int WM_INITMENU = 0x116;
private const int WM_INITMENUPOPUP = 0x117;
private const int WM_SYSCOMMAND = 0x112;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SYSCOMMAND) { //WM_INITMENU || m.Msg == WM_INITMENUPOPUP) {}
Point pt = Cursor.Position;
int h = SystemInformation.CaptionHeight;
Rectangle r = new Rectangle(this.Location, new Size(h, h));
if (!r.Contains(pt) || Cursor.Current != Cursors.Default)
base.WndProc(ref m);
else {
Rectangle r2 = RectangleToScreen(this.ClientRectangle);
WindowMenu.Show(r2.Location);
}
}
else {
base.WndProc(ref m);
}
}
/*
Failed attempts at hiding the default window menu.
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
//IntPtr hMenu = GetSystemMenu(Handle, false);
//SendMessage(hMenu, WM_SETREDRAW, (IntPtr) 0, (IntPtr) 0);
//int count = GetMenuItemCount(hMenu);
//for (int i = count - 1; i >= 3; i--)
// RemoveMenu(hMenu, (uint) i, MF_BYPOSITION);
}
[DllImport("user32.dll")]
public static extern int GetMenuItemCount(IntPtr hMenu);
[DllImport("user32.dll")]
private static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
//[DllImport("user32.dll")]
//public static extern IntPtr DestroyMenu(IntPtr hMenu);
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
private const int MF_BYPOSITION = 0x00000400;
private const int WM_SETREDRAW = 11;
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, IntPtr wParam, IntPtr lParam);
// this also removes the min/max/close buttons:
//private const int WS_SYSMENU = 0x80000;
//protected override CreateParams CreateParams {
// get {
// var p = base.CreateParams;
// p.Style = p.Style & ~WS_SYSMENU;
// return p;
// }
//}
*/
}
public class WindowMenu : ToolStripDropDown {
public WindowMenu() {
Items.Add(new ToolStripMenuItem("Custom1"));
}
}