SlunkCrypt/gui/Utilities/SystemMenu.cs

187 lines
6.6 KiB
C#

/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* 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
{
using ItemInfo = Tuple<string, SystemMenu.SystemMenuActionHandler>;
class SystemMenu : IDisposable
{
private const int WM_SYSCOMMAND = 0x112;
public delegate void SystemMenuActionHandler(SystemMenu sender, uint menuId);
private readonly HwndSource m_hwndSource;
private readonly Dictionary<uint, ItemInfo> m_menuItems = new Dictionary<uint, ItemInfo>();
private bool m_firstItem = true;
private uint m_nextMenuId = 100;
private volatile bool m_disposed = false;
// =============================================================================
// Constructor
// =============================================================================
public SystemMenu(Window window)
{
if (ReferenceEquals(window, null))
{
throw new ArgumentNullException("Window 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, SystemMenuActionHandler handler)
{
if (m_disposed)
{
throw new ObjectDisposedException("Already disposed!");
}
if (ReferenceEquals(handler, null))
{
throw new ArgumentNullException("SystemMenuActionHandler must not be null!");
}
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, Tuple.Create(captionStr, handler));
return menuItemId;
}
}
return 0;
}
public bool ModifyMenu(uint menuId, bool isChecked)
{
if (m_disposed)
{
throw new ObjectDisposedException("Already disposed!");
}
m_hwndSource.Dispatcher.VerifyAccess();
ItemInfo itemInfo;
if (m_menuItems.TryGetValue(menuId, out itemInfo))
{
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, itemInfo.Item1);
}
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 = (uint)wParam.ToInt32();
ItemInfo itemInfo;
if (m_menuItems.TryGetValue(menuId, out itemInfo))
{
itemInfo.Item2(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
{
[Flags]
public enum MenuFlags : uint
{
MF_STRING = 0x0000,
MF_UNCHECKED = 0x0000,
MF_BYCOMMAND = 0x0000,
MF_CHECKED = 0x0008,
MF_BYPOSITION = 0x0400,
MF_SEPARATOR = 0x0800
}
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool AppendMenu(IntPtr hMenu, MenuFlags uFlags, UIntPtr uIDNewItem, string lpNewItem);
[DllImport("user32.dll")]
public static extern bool ModifyMenu(IntPtr hMenu, uint uPosition, MenuFlags uFlags, UIntPtr uIDNewItem, string lpNewItem);
}
}
}