From f3b11d6f72f735400a061ec7e8d2d3982f6f64e8 Mon Sep 17 00:00:00 2001 From: Sean Kaim Date: Fri, 14 Apr 2017 17:25:40 -0400 Subject: [PATCH] Fix for #434 - SysTray SafeHandle --- mRemoteV1/App/NativeMethods.cs | 28 ++++++++-- mRemoteV1/Tools/Tools.SystemMenu.cs | 82 +++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/mRemoteV1/App/NativeMethods.cs b/mRemoteV1/App/NativeMethods.cs index 41127e1ee..44aa0b96f 100644 --- a/mRemoteV1/App/NativeMethods.cs +++ b/mRemoteV1/App/NativeMethods.cs @@ -1,6 +1,9 @@ using System; using System.Drawing; +using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; +using System.Text; + #pragma warning disable 169 namespace mRemoteNG.App @@ -42,11 +45,17 @@ namespace mRemoteNG.App internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, System.Text.StringBuilder lParam); + internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr SendMessage([In] IntPtr hWnd, [In] uint msg, [Out] StringBuilder wParam, [In] IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer); - + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern bool SetForegroundWindow(IntPtr hWnd); @@ -76,6 +85,10 @@ namespace mRemoteNG.App [DllImport("user32", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] internal static extern bool SetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + [DllImport("kernel32", SetLastError = true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + internal static extern bool CloseHandle(IntPtr handle); #endregion #region Structures @@ -286,7 +299,10 @@ namespace mRemoteNG.App public const int WA_ACTIVE = 0x1; /// - /// + /// Sent to both the window being activated and the window being deactivated. + /// If the windows use the same input queue, the message is sent synchronously, first to the window procedure of the + /// top-level window being deactivated, then to the window procedure of the top-level window being activated. If the + /// windows use different input queues, the message is sent asynchronously, so the window is activated immediately. /// public const int WA_CLICKACTIVE = 0x2; #endregion @@ -455,6 +471,12 @@ namespace mRemoteNG.App public const int VK_C = 0x67; #endregion + #region EM + public const uint ECM_FIRST = 0x1500; + public const uint EM_SETCUEBANNER = ECM_FIRST + 1; + public const uint EM_GETCUEBANNER = ECM_FIRST + 2; + #endregion + #region LB public const int LB_ERR = -1; public const int LB_SELECTSTRING = 0x18C; diff --git a/mRemoteV1/Tools/Tools.SystemMenu.cs b/mRemoteV1/Tools/Tools.SystemMenu.cs index fdee011fe..227c242a7 100644 --- a/mRemoteV1/Tools/Tools.SystemMenu.cs +++ b/mRemoteV1/Tools/Tools.SystemMenu.cs @@ -1,54 +1,92 @@ using System; using System.Drawing; - +using mRemoteNG.App; +using Microsoft.Win32.SafeHandles; +// ReSharper disable MemberCanBeMadeStatic.Global namespace mRemoteNG.Tools { - public class SystemMenu - { + public sealed class SystemMenu : SafeHandleZeroOrMinusOneIsInvalid, IDisposable + { [Flags] public enum Flags { - MF_STRING = App.NativeMethods.MF_STRING, - MF_SEPARATOR = App.NativeMethods.MF_SEPARATOR, - MF_BYCOMMAND = App.NativeMethods.MF_BYCOMMAND, - MF_BYPOSITION = App.NativeMethods.MF_BYPOSITION, - MF_POPUP = App.NativeMethods.MF_POPUP, - WM_SYSCOMMAND = App.NativeMethods.WM_SYSCOMMAND + MF_STRING = NativeMethods.MF_STRING, + MF_SEPARATOR = NativeMethods.MF_SEPARATOR, + MF_BYCOMMAND = NativeMethods.MF_BYCOMMAND, + MF_BYPOSITION = NativeMethods.MF_BYPOSITION, + MF_POPUP = NativeMethods.MF_POPUP, + WM_SYSCOMMAND = NativeMethods.WM_SYSCOMMAND } - - public IntPtr SystemMenuHandle; - public IntPtr FormHandle; - - public SystemMenu(IntPtr Handle) + + private bool disposed; + internal IntPtr SystemMenuHandle; + private readonly IntPtr FormHandle; + + public SystemMenu(IntPtr Handle) :base(true) { FormHandle = Handle; - SystemMenuHandle = App.NativeMethods.GetSystemMenu(FormHandle, false); - } + SystemMenuHandle = NativeMethods.GetSystemMenu(FormHandle, false); + SetHandle(SystemMenuHandle); + } public void Reset() { - SystemMenuHandle = App.NativeMethods.GetSystemMenu(FormHandle, true); + SystemMenuHandle = NativeMethods.GetSystemMenu(FormHandle, true); } public void AppendMenuItem(IntPtr ParentMenu, Flags Flags, IntPtr ID, string Text) { - App.NativeMethods.AppendMenu(ParentMenu, (int)Flags, ID, Text); + NativeMethods.AppendMenu(ParentMenu, (int)Flags, ID, Text); } public IntPtr CreatePopupMenuItem() { - return App.NativeMethods.CreatePopupMenu(); + return NativeMethods.CreatePopupMenu(); } public bool InsertMenuItem(IntPtr SysMenu, int Position, Flags Flags, IntPtr SubMenu, string Text) { - return App.NativeMethods.InsertMenu(SysMenu, Position, (int)Flags, SubMenu, Text); + return NativeMethods.InsertMenu(SysMenu, Position, (int)Flags, SubMenu, Text); } public IntPtr SetBitmap(IntPtr Menu, int Position, Flags Flags, Bitmap Bitmap) { - return new IntPtr(Convert.ToInt32(App.NativeMethods.SetMenuItemBitmaps(Menu, Position, (int)Flags, Bitmap.GetHbitmap(), Bitmap.GetHbitmap()))); + return new IntPtr(Convert.ToInt32(NativeMethods.SetMenuItemBitmaps(Menu, Position, (int)Flags, Bitmap.GetHbitmap(), Bitmap.GetHbitmap()))); } - } + + protected override bool ReleaseHandle() + { + return NativeMethods.CloseHandle(SystemMenuHandle); + } + + + /* If we don't have the finalizer, then we get this warning: https://msdn.microsoft.com/library/ms182329.aspx (CA2216: Disposable types should declare finalizer) + * If we DO have the finalizer, then we get this warning: https://msdn.microsoft.com/library/ms244737.aspx (CA1063: Implement IDisposable correctly) + * + * Since the handle is likely going to be in use for the entierty of the process, the finalizer isn't very important since when we're calling it + * the process is likely exiting. Leaks would be moot once it exits. CA2216 is the lesser of 2 evils as far as I can tell. Suppress. + ~SystemMenu() + { + Dispose(false); + } + */ + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")] + public new void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected override void Dispose(bool disposing) + { + if (disposed) return; + if (!disposing) return; + + ReleaseHandle(); + + disposed = true; + } + } } \ No newline at end of file