蛋疼的让C#控制台程序(Console)像窗体(Winform)一样(关闭,托盘图标,事件响应,手动退出等)

前言:
你可以把这篇文章定义为一篇蛋疼的文章,应为这个东西不怎么实用,还费神,没事折腾这做什么。恩,的确,蛋疼。我也同意,就算蛋疼也有它的小众范围,当你不想做webservers,winform等,就想蛋疼的拿控制台来做服务,做程序,行吗?行,但是控制台一点关闭就退出了,有时会点错,控制台没有托盘图标,想最小化到托盘图标等,还有什么蛋疼的想法,来吧,让我们来实现他们。

需要了解:
console application是控制台程序。
控制台不是winform,我们不能设置它的关闭事件等。
控制台虽然可以通过添加引用来增加托盘图标,但是托盘图标没有事件。
哇,怎么都是不能,那不是不能实现。

所以你还需要了解:
我们可以通过引用外部dll的api来捕获到关闭事件等。
我们可以给控制台添加消息事件循环来捕获事件响应让托盘图标可以触发事件。

那么我们的思路是:
禁用关闭按钮,让用户在控制台输入exit进行退出,当控制台被其他事件关闭时可以进行处理。
用Application.DoEvents()来捕获消息事件处理,但是要用死循环来控制,那么我们怎么监听输入呢?
我们在开一个线程用来监听输入。
蛋疼的可以,搞个这还这么麻烦!

别慌,还需要你解决的问题:
先了解我给出的代码实现了什么。实现了禁用关闭按钮,托盘图标的添加和事件的处理。
你要做的是什么,当然你可以不做,如果你也想蛋疼一下,就来解决下这个问题吧。
退出控制台时,托盘图标没有消失,唉,这是bug,怎么解决?
捕获关闭事件,在要关闭时清除托盘图标。
先告诉你可以实现,我以实验成功,这里没有放出来是让你也蛋疼下。

好了,代码如下:
/*
* 控制台禁用关闭按钮并最小化到系统托盘演示
*
* 通过ConsoleWin32类来进行控制
* 添加引用 System.Runtime.InteropServices; 和 System.Threading; 用于禁用关闭按钮
* 添加引用 System.Drawing; 和 System.Windows.Forms; 用于系统托盘
*
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;

namespace Tray_beta_1
{
class Program
{
static bool _IsExit = false;

static void Main(string[] args)
{
Console.Title
= "TestConsoleLikeWin32";
ConsoleWin32Helper.ShowNotifyIcon();
ConsoleWin32Helper.DisableCloseButton(Console.Title);

Thread threadMonitorInput
= new Thread(new ThreadStart(MonitorInput));
threadMonitorInput.Start();

while (true)
{
Application.DoEvents();
if (_IsExit)
{
break;
}
}
}

static void MonitorInput()
{
while (true)
{
string input = Console.ReadLine();
if (input == "exit")
{
_IsExit
= true;
Thread.CurrentThread.Abort();
}
}
}
}

class ConsoleWin32Helper
{
static ConsoleWin32Helper()
{
_NotifyIcon.Icon
= new Icon(@"G:\BruceLi Test\ConsoleAppTest\ConsoleApps\Tray\small.ico");
_NotifyIcon.Visible
= false;
_NotifyIcon.Text
= "tray";

ContextMenu menu
= new ContextMenu();
MenuItem item
= new MenuItem();
item.Text
= "右键菜单,还没有添加事件";
item.Index
= 0;

menu.MenuItems.Add(item);
_NotifyIcon.ContextMenu
= menu;

_NotifyIcon.MouseDoubleClick
+= new MouseEventHandler(_NotifyIcon_MouseDoubleClick);

}

static void _NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Console.WriteLine(
"托盘被双击.");
}

#region 禁用关闭按钮
[DllImport(
"User32.dll", EntryPoint = "FindWindow")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport(
"user32.dll", EntryPoint = "GetSystemMenu")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, IntPtr bRevert);

[DllImport(
"user32.dll", EntryPoint = "RemoveMenu")]
static extern IntPtr RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);

/// <summary>
/// 禁用关闭按钮
/// </summary>
/// <param name="consoleName">控制台名字</param>
public static void DisableCloseButton(string title)
{
//线程睡眠,确保closebtn中能够正常FindWindow,否则有时会Find失败。。
Thread.Sleep(100);

IntPtr windowHandle
= FindWindow(null, title);
IntPtr closeMenu
= GetSystemMenu(windowHandle, IntPtr.Zero);
uint SC_CLOSE = 0xF060;
RemoveMenu(closeMenu, SC_CLOSE,
0x0);
}
public static bool IsExistsConsole(string title)
{
IntPtr windowHandle
= FindWindow(null, title);
if (windowHandle.Equals(IntPtr.Zero)) return false;

return true;
}
#endregion

#region 托盘图标
static NotifyIcon _NotifyIcon = new NotifyIcon();
public static void ShowNotifyIcon()
{
_NotifyIcon.Visible
= true;
_NotifyIcon.ShowBalloonTip(
3000, "", "我是托盘图标,用右键点击我试试,还可以双击看看。", ToolTipIcon.None);
}
public static void HideNotifyIcon()
{
_NotifyIcon.Visible
= false;
}

#endregion
}
}


END:
来建立个项目把代码copy进去也蛋疼下吧!

附加题:
还有一个小蛋疼的地方,就是后台运行,从任务栏隐藏,只留托盘图标,自己思考下吧,都可以实现。
posted @ 2011-04-28 12:58  布鲁斯.李  阅读(14057)  评论(26编辑  收藏  举报