SlunkCrypt/gui/Utilities/SystemMenu.cs

187 lines
6.8 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
{
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<uint, string> m_menuItems = new Dictionary<uint, string>();
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);
}
}
}