Fix for #434 - SysTray SafeHandle

This commit is contained in:
Sean Kaim
2017-04-14 17:25:40 -04:00
parent 69f310c02c
commit f3b11d6f72
2 changed files with 85 additions and 25 deletions

View File

@@ -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;
/// <summary>
///
/// 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.
/// </summary>
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;

View File

@@ -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;
}
}
}