I have a small console application that runs a very long task and I would like to hide the console window when I click on the minimize button but keep a notify icon in the system tray so that I would know when the task is completed or something went wrong.
Well, I did a lot of research mostly here on SE and I came up with the following code:
using System;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace iTray
{
class Program
{
#region pInvoke
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public Point ptMinPosition;
public Point ptMaxPosition;
public Rectangle rcNormalPosition;
}
private enum ShowWindowCommands
{
Hide = 0,
Normal = 1,
ShowMinimized = 2,
Maximize = 3,
ShowMaximized = 3,
ShowNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActive = 7,
ShowNA = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimize = 11
}
[DllImport("user32.dll")]
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
private const uint SC_CLOSE = 0xF060;
private const uint MF_ENABLED = 0x00000000;
private const uint MF_DISABLED = 0x00000002;
#endregion
private static NotifyIcon Tray = default(NotifyIcon);
private static IntPtr Me = default(IntPtr);
static void Main(string[] args)
{
Console.Title = "Minimize Console To Tray";
// Get The Console Window Handle
Me = GetConsoleWindow();
// Disable Close Button (X)
EnableMenuItem(GetSystemMenu(Me, false), SC_CLOSE, (uint)(MF_ENABLED | MF_DISABLED));
MenuItem mExit = new MenuItem("Exit", new EventHandler(Exit));
ContextMenu Menu = new ContextMenu(new MenuItem[] { mExit });
Tray = new NotifyIcon()
{
Icon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("iTray.Cheshire.ico")),
Visible = true,
Text = Console.Title,
ContextMenu = Menu
};
Tray.DoubleClick += new EventHandler(DoubleClick);
// Detect When The Console Window is Minimized and Hide it
Task.Factory.StartNew(() =>
{
while (true)
{
WINDOWPLACEMENT wPlacement = new WINDOWPLACEMENT();
GetWindowPlacement(Me, ref wPlacement);
if (wPlacement.showCmd == (int)ShowWindowCommands.ShowMinimized)
ShowWindow(Me, (int)ShowWindowCommands.Hide);
// 1 ms Delay to Avoid High CPU Usage
Wait(1);
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Application.Run();
}
private static void DoubleClick(object sender, EventArgs e)
{
ShowWindow(Me, (int)ShowWindowCommands.Restore);
}
private static void Exit(object sender, EventArgs e)
{
Tray.Dispose();
Application.Exit();
}
private static void Wait(int timeout)
{
using (AutoResetEvent AREv = new AutoResetEvent(false))
AREv.WaitOne(timeout, true);
}
}
}
The code is working fine, but I need to know your opinions whether the are some improvements to be made especially on the infinite loop part.