CDTray, 打開,關(guān)閉光驅(qū)的系統(tǒng)托盤程序
今天買了一臺內(nèi)置刻錄機,安裝完成后發(fā)現(xiàn)機箱提供的面板設(shè)計不太合理:光驅(qū)門打開后,按鈕就根本沒有用了,因為它附著在門上,與光驅(qū)本身提供的開倉按鈕根本連不上了,于是我只能用手推動光盤托才能把光驅(qū)門給關(guān)上.
郁悶之余,寫了一個小工具來解決這個問題:
最核心的東西--開關(guān)光驅(qū)門,其實很簡單:
public static void Open(string driveLetter)
{
string returnString = "";
mciSendStringA("set cdaudio!" + driveLetter + " door open", returnString, 0, 0);
}
public static void Close(string driveLetter)
{
string returnString = "";
mciSendStringA("set cdaudio!" + driveLetter + " door closed", returnString, 0, 0);
}
[DllImport("winmm.dll", EntryPoint = "mciSendStringA")]
public static extern void mciSendStringA(string lpstrCommand, string lpstrReturnString, long uReturnLength, long hwndCallback);
上面的代碼是從網(wǎng)上搜索得到的.我本來想判斷一下光驅(qū)本身的狀態(tài)--是開還是關(guān),但是搜索了十來分鐘,沒找到相關(guān)資料,覺得這個也不是特別有必要,于是放棄了.
好了,下面就要獲取系統(tǒng)里所有光驅(qū)了,其實很簡單,一行代碼就搞定:
public static string[] GetCDRoms()
{
return DriveInfo.GetDrives().Where(x => x.DriveType == DriveType.CDRom).Select(x => x.Name).ToArray();
}
為了讓程序用起來更方便,我希望它能設(shè)置為隨Windows自動啟動.這個也沒什么困難的,就是操作注冊表而已,于是我從原來寫的代碼里扒出了一段,重新加工了一下:
class AutoLaunch
{
string registryKey;
public AutoLaunch(string registryKey)
{
this.registryKey = registryKey;
}
string GetRunningExePath()
{
return Application.ExecutablePath;
}
public bool Enabled
{
get
{
try
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software");
key = key.OpenSubKey("Microsoft");
key = key.OpenSubKey("Windows");
key = key.OpenSubKey("CurrentVersion");
key = key.OpenSubKey("Run", true);
string cmdline = key.GetValue(registryKey) as string;
if (cmdline == null)
{
return false;
}
else
{
if (!string.Equals(cmdline, GetRunningExePath(), StringComparison.OrdinalIgnoreCase))
key.SetValue(registryKey, GetRunningExePath());
return true;
}
}
catch (NullReferenceException)
{
return false;
}
}
set
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software");
key = key.OpenSubKey("Microsoft");
key = key.OpenSubKey("Windows");
key = key.OpenSubKey("CurrentVersion");
key = key.OpenSubKey("Run", true);
if (value)
{
key.SetValue(registryKey, GetRunningExePath());
}
else
{
key.DeleteValue(registryKey, false);
}
}
}
}
好了,現(xiàn)在可以做界面了.由于程序非常的簡單,我選擇了使用Windows Forms,而且決定一個窗口都不要,用戶能看到的全部界面就是通知區(qū)域的一個圖標.
那好的,在一個空白的Form上扔一個NotifyIcon,和一個ContextMenuStrip,并把NotifyIcon的ContextMenuStrip屬性設(shè)置好.
然后就是我們的代碼了,用于在Form的構(gòu)造時,動態(tài)生成菜單內(nèi)容:
AutoLaunch autoLaunch;
public Form1()
{
InitializeComponent();
Init();
}
private void Init()
{
ShowInTaskbar = false;
WindowState = FormWindowState.Minimized;
autoLaunch = new AutoLaunch("CDTool_deerchao");
LoadMenu();
}
private void LoadMenu()
{
var menuItems = new List<ToolStripItem>();
menuItems.Add(new ToolStripMenuItem
{
Text = "By deerchao",
});
menuItems.Add(new ToolStripSeparator());
foreach (var cd in CDDrive.GetCDRoms())
{
menuItems.AddRange(LoadMenu(cd).Cast<ToolStripItem>());
menuItems.Add(new ToolStripSeparator());
}
var autoStart = new ToolStripMenuItem("&Start with Windows", null, (s, e) => autoLaunch.Enabled = ((ToolStripMenuItem)s).Checked)
{
CheckOnClick = true,
Checked = autoLaunch.Enabled,
};
var exit = new ToolStripMenuItem("E&xit", null, (s, e) => Application.Exit());
menuItems.Add(autoStart);
menuItems.Add(exit);
contextMenu.Items.AddRange(menuItems.ToArray());
}
private IEnumerable<ToolStripMenuItem> LoadMenu(string cd)
{
var open = new ToolStripMenuItem("&Open " + cd.Substring(0, 2), null, (s, e) => CDDrive.Open(cd));
var close = new ToolStripMenuItem("&Close " + cd.Substring(0, 2), null, (s, e) => CDDrive.Close(cd));
yield return open;
yield return close;
}
很好,現(xiàn)在運行起來就一切OK了.
但是,追求完美的我們怎么會如此輕易罷休呢..我們可以看到,代碼里多處用到了Linq擴展方法,比如Where, Cast, Select, ToArray等. 而Linq則是.Net 3.5里才有的新東西..那意味著這個程序的用戶,要去下載200多M的.Net 3.5安裝程序,然后花兩個小時來安裝它,最后才能使用這個不到200K的小工具...
怎么辦?打造山塞版的Linq吧,還好,我們使用到的Linq特性并不多,只要幾十行代碼就能完成:
using System.Collections;
using System.Collections.Generic;
namespace System.Linq
{
delegate TResult Func<TSource, TResult>(TSource source);
static class Enumerable
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Predicate<T> predict)
{
foreach (var v in source)
if (predict(v))
yield return v;
}
public static IEnumerable<TDest> Cast<TDest>(this IEnumerable source)
{
foreach (var v in source)
yield return (TDest)v;
}
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> mapping)
{
foreach (var v in source)
yield return mapping(v);
}
public static List<T> ToList<T>(this IEnumerable<T> source)
{
return new List<T>(source);
}
public static T[] ToArray<T>(this IEnumerable<T> source)
{
return ToList(source).ToArray();
}
}
}
namespace System.Runtime.CompilerServices
{
class ExtensionAttribute : Attribute
{
}
}
OK,把針對的.Net Framework版本改成2.0,砍掉不必要的引用項,重新編譯,搞定!
現(xiàn)在唯一讓我不爽的就是沒有一個好圖標了...好吧,我們從微軟那里借一個來吧.操起剛Google來的圖標編輯工具IcoFx,在File\Extract里選擇C:\Windows\System32\imagers.dll,從里邊挑選一個光盤圖標,保存.
在項目里設(shè)置好圖標以后,重新編譯,That's All!
你可以從這里下載全部源代碼.