/******************************************************************************/ /* SlunkCrypt, by LoRd_MuldeR */ /* This work has been released under the CC0 1.0 Universal license! */ /******************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; namespace com.muldersoft.slunkcrypt.gui.utils { class SystemMenu : IDisposable { private const int WM_SYSCOMMAND = 0x112; public delegate void SystemMenuActionHandler(SystemMenu sender, uint menuId); private readonly SystemMenuActionHandler m_handler; private readonly HwndSource m_hwndSource; private readonly Dictionary m_menuItems = new Dictionary(); private bool m_firstItem = true; private uint m_nextMenuId = 100; private volatile bool m_disposed = false; // ============================================================================= // Constructor // ============================================================================= public SystemMenu(Window window, SystemMenuActionHandler handler) { if (ReferenceEquals(window, null)) { throw new ArgumentNullException("Window must not be null!"); } if (ReferenceEquals(m_handler = handler, null)) { throw new ArgumentNullException("SystemMenuActionHandler must not be null!"); } window.Dispatcher.VerifyAccess(); m_hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle); m_hwndSource.AddHook(WndProcHook); } // ============================================================================= // Public methods // ============================================================================= public uint AppendMenu(string captionStr) { if (m_disposed) { throw new ObjectDisposedException("Already disposed!"); } m_hwndSource.Dispatcher.VerifyAccess(); IntPtr hSysMenu = GetSystemMenu(m_hwndSource.Handle, false); if (hSysMenu != IntPtr.Zero) { if (m_firstItem) { if (AppendMenu(hSysMenu, Native.MenuFlags.MF_SEPARATOR, 0, string.Empty)) { m_firstItem = false; } } if (AppendMenu(hSysMenu, Native.MenuFlags.MF_STRING, m_nextMenuId, captionStr)) { uint menuItemId = m_nextMenuId++; m_menuItems.Add(menuItemId, captionStr); return menuItemId; } } return 0; } public bool ModifyMenu(uint menuId, bool isChecked) { if (m_disposed) { throw new ObjectDisposedException("Already disposed!"); } m_hwndSource.Dispatcher.VerifyAccess(); string captionStr; if (m_menuItems.TryGetValue(menuId, out captionStr)) { IntPtr hSysMenu = GetSystemMenu(m_hwndSource.Handle, false); if (hSysMenu != IntPtr.Zero) { return ModifyMenu(hSysMenu, menuId, Native.MenuFlags.MF_BYCOMMAND | (isChecked ? Native.MenuFlags.MF_CHECKED : Native.MenuFlags.MF_UNCHECKED), menuId, captionStr); } return false; } else { throw new ArgumentException("Menu item ID is unknown!"); } } // ============================================================================= // Internal methods // ============================================================================= private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_SYSCOMMAND) { uint menuId; if (m_menuItems.ContainsKey(menuId = (uint) wParam.ToInt32())) { m_handler(this, menuId); handled = true; } } return IntPtr.Zero; } private IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert) { try { return Native.GetSystemMenu(hWnd, bRevert); } catch { return IntPtr.Zero; } } private bool AppendMenu(IntPtr hMenu, Native.MenuFlags uFlags, uint uIDNewItem, string lpNewItem) { try { return Native.AppendMenu(hMenu, uFlags, new UIntPtr(uIDNewItem), lpNewItem); } catch { return false; } } private bool ModifyMenu(IntPtr hMenu, uint uPosition, Native.MenuFlags uFlags, uint uIDNewItem, string lpNewItem) { return Native.ModifyMenu(hMenu, uPosition, uFlags, new UIntPtr(uIDNewItem), lpNewItem); } public void Dispose() { if (!m_disposed) { m_disposed = true; m_hwndSource.RemoveHook(WndProcHook); } } // ============================================================================= // Event handlers // ============================================================================= private class Native { private const string DLL_USER32 = "user32.dll"; [Flags] public enum MenuFlags : uint { MF_STRING = 0x0000, MF_UNCHECKED = 0x0000, MF_BYCOMMAND = 0x0000, MF_CHECKED = 0x0008, MF_BYPOSITION = 0x0400, MF_SEPARATOR = 0x0800 } [DllImport(DLL_USER32, CharSet = CharSet.Unicode, EntryPoint = "GetSystemMenu", ExactSpelling = true)] public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); [DllImport(DLL_USER32, CharSet = CharSet.Unicode, EntryPoint = "AppendMenuW", ExactSpelling = true)] public static extern bool AppendMenu(IntPtr hMenu, MenuFlags uFlags, UIntPtr uIDNewItem, string lpNewItem); [DllImport(DLL_USER32, CharSet = CharSet.Unicode, EntryPoint = "ModifyMenuW", ExactSpelling = true)] public static extern bool ModifyMenu(IntPtr hMenu, uint uPosition, MenuFlags uFlags, UIntPtr uIDNewItem, string lpNewItem); } } }