QQ登录器的分层实现之四(登录的实现以及快捷键的注册)
不好意思啊,前两天一直在忙,都没有时间更新博客,上期我们讨论到地XML存储文件的读写,为我们今天的登录实现奠定了基础。QQ的登录,在前面我们说过了,只是启动进程,在Process.Start()里加入启动参数而已,并没有大家想的那么复杂,我们在软件的界面层里操作,下面我们一睹为快。
这里的界面,我仿造了来自网上的一款QQ登录器的界面,并加入了一些皮肤。

我们先来写添加帐号,由于添加帐号和修改帐号的所需要显示的内容都是一样的,所以我们用继承的方式来重用相同的界面元素,在Login程里新建一个AccountEditForm窗体,加入"昵称","QQ号码","密码","是否隐身","快捷键"等元素,如下图:

然后再加入一个AddAccountForm和一个UpdateAccountForm,分别继承于它,类关系图如下:

先在LoginForm类中添加一个实体对象用于存帐号数据:List<Account> accounts;
然后在该窗体的构造方法中给其赋值:
accounts = new List<Account>();
//读取存储文件,注册热键
try
{
if (!File.Exists(Application.StartupPath + "data.xml"))
{
Config.CreateXmlFile(Application.StartupPath);
}
accounts = Config.GetAllAccount(Application.StartupPath);
}
catch (Exception er)
{

MessageBox.Show("出错\n/r" + er.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
我们需要在窗体间传值,把现有的帐号数据集传到AddAccountForm类中,以作处理
双击LoginForm中的“增加帐号”按钮,在里面输入:
AddAccountForm myAddAccountForm = new AddAccountForm(accounts);
myAddAccountForm.ShowDialog();
在添加帐号的类中,我们声明一个Account类型的私有成员private List<Account> items;然后在构造方法中给它赋值:
public AddAccountForm(List<Account> accounts)
: this()
{
items = accounts;
}
这样,我就把主窗体中的帐号对象accounts传递到了添加窗体,当用户点确定按钮时,就在该数据集中添加一个数据对象:
Account account = new Account();
if (txtName.Text.Trim().Length == 0)
{
account.Name = this.txtNumber.Text.Trim();
}
else
{
account.Name = txtName.Text.Trim();
}
account.Number = this.txtNumber.Text.Trim();
account.Password =Config.HashBase64(txtPwd.Text.Trim());
account.IsStealth = checkBox1.Checked;
account.KeyId=Config.GetMaxKeyId(Application.StartupPath);
account.FlashKey = txtFlashKey.Text.Trim();

//在写操作前应先判断配置文件是否存在
try
{
//Config myConfig = new Config(Application.StartupPath);
//myConfig.AddAccount(account);
Config.AddAccount(Application.StartupPath,account);
items.Add(account);
MessageBox.Show("添加QQ号" + this.txtNumber.Text.Trim() + "成功", "提示");
this.DialogResult = DialogResult.OK;
this.Close();
}
catch (Exception er)
{

MessageBox.Show("添加QQ号" + this.txtNumber.Text.Trim() + "失败,原因:" + er.Message, "提示");
}
在确定按钮的事件时里,我们调用了XML读写文件类的AddAccount方法,把帐号信息写入的文件中,而此时也把帐号添加到了数据集里。主窗体可以立即显示出来,而不必再次读取数据文件。修改帐号信息的处理跟添加时的处理是一样的,在这里我们就不再重复了。下面看看删除操作。
在删除帐号时,我们可以在ListView中选择多个帐号,点删除。当点击“删除”按钮时,系统会遍历数据集,把选中的帐号存到另一数据集中,然后调用XML读写文件类Config.cs的DeleteAccount方法执行删除操作
try
{

if (MessageBox.Show("确定要删除选中的帐号吗?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
return;
ListView.CheckedIndexCollection indexs = listView1.CheckedIndices;
List<Account> accountItems = new List<Account>();
foreach (int index in indexs)
{
accountItems.Add(accounts[index]);
}
Config.DeleteAccount(Application.StartupPath,accountItems);
MessageBox.Show("删除成功!");
accounts = Config.GetAllAccount(Application.StartupPath);
BindToListView();
}
catch (Exception er)
{

MessageBox.Show("删除失败!" + er.Message);

}
好了,万事俱备,只欠东风,现在我们已经可以对帐号执行增删改查了,下一步就是大家所期待的如何实现登录了。在ListView中选中多个帐号后,点"登录QQ"按钮,系统先检查用户设置的QQ路径是否存在,如果不存在则提示重新设置QQ路径,如果存在,则开始遍历选中的帐号,分别执行QQ进程:
//取得勾选中的项
string qqPath = Config.GetQQPath(Application.StartupPath);
if (!File.Exists(qqPath))
{
MessageBox.Show("QQ路径不存在,请重新设置!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
ListView.CheckedIndexCollection indexs = listView1.CheckedIndices;

foreach (int i in indexs)
{
//启动QQ
Process.Start(qqPath,"/START QQUIN:"+accounts[i].Number+" PWDHASH:"+accounts[i].Password+" /STAT:"+(accounts[i].IsStealth?"40":"41"));
//写入最后登录日期
accounts[i].LastLoginTime = DateTime.Now;
Config.UpdateAccount(Application.StartupPath,accounts[i]);
}
到此,多用用户的QQ登录方法已经基本实现。在前面的章节中我们提到,该工具的一大特点就是加了快捷键,在用户看不到软件界面时,只需按一下快捷键即可启动自己需要的QQ。下面我们继续讨论快捷键的实现。
在Login工程中我们新建一个类"FlashKey.cs",输入如下代码:
//如果函数执行成功,返回值不为0。
//如果函数执行失败,返回值为0。要得到扩展错误信息,调用GetLastError。
[DllImport("user32.dll", SetLastError = true)]
public static extern bool RegisterHotKey(
IntPtr hWnd, //要定义热键的窗口的句柄
int id, //定义热键ID(不能与其它ID重复)
KeyModifiers fsModifiers, //标识热键是否在按Alt、Ctrl、Shift、Windows等键时才会生效
Keys vk //定义热键的内容
);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnregisterHotKey(
IntPtr hWnd, //要取消热键的窗口的句柄
int id //要取消热键的ID
);

//定义了辅助键的名称(将数字转变为字符以便于记忆,也可去除此枚举而直接使用数值)
[Flags()]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Ctrl = 2,
Shift = 4,
WindowsKey = 8
}
当然,命名空间:
using System.Windows.Forms;
using System.Runtime.InteropServices;
是不能忘记的。我们后面的注册和注销快捷键就得能过该类来实现了。
在主窗体开始构造时我们开始注册快捷键:
//类型转换
keysConv = new KeysConverter();
foreach (Account item in accounts)
{
if (item.FlashKey.Length != 0)
{
FlashKeyEx.RegisterHotKey(this.Handle, item.KeyId, FlashKeyEx.KeyModifiers.Ctrl, (Keys)keysConv.ConvertFromString(item.FlashKey));
}
}
注意我们这里的快捷键ID我们是存在XML文件中的,为了防止该ID重复,每添加一个帐号时,都调用Config类的GetMaxKeyId方法算出一个最大的ID,
/// <summary>
/// 取得最大键值
/// </summary>
/// <param name="xmlFilePath"></param>
/// <returns></returns>
public static int GetMaxKeyId(string xmlFilePath)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlFilePath + "data.xml");
XmlNodeList nodes = xmlDoc.SelectSingleNode("Account").ChildNodes;
ArrayList idList = new ArrayList();
int maxId=1000;
foreach (XmlNode xn in nodes)
{
if (xn.Name == "Number")
{
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode chile in xnl)
{
if (chile.Name == "KeyId")
{
idList.Add(chile.InnerText);
}
}
}
}
int i = 0;
while (i < idList.Count)
{
if (i + 1 == idList.Count)
{
maxId = Convert.ToInt32(idList[i]);
return maxId + 1;
}
if (Convert.ToInt32(idList[i]) < Convert.ToInt32(idList[i + 1]))
{
idList.RemoveAt(i);
i = 0;
}
}
return maxId+1;
}
当程序退出时,我们需要注销这些快捷键,在Form的FormClosing事件中输入:
try
{
KeysConverter keysConv = new KeysConverter();
foreach (Account item in accounts)
{
if (item.FlashKey.Length != 0)
{
//注销热键
FlashKeyEx.UnregisterHotKey(this.Handle, item.KeyId);
}
}
}
catch (Exception er)
{

MessageBox.Show("取消热键出错\n/r" + er.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
注册了快捷键后,就要进行关注用户的按键了,重写Form的WndProc方法:
const int WM_HOTKEY = 0x0312;

//按快捷键
if (m.Msg == WM_HOTKEY)
{
foreach (Account item in accounts)
{
if (item.KeyId == m.WParam.ToInt32())
{
//启动QQ
string qqPath = Config.GetQQPath(Application.StartupPath);
Process.Start(qqPath, "/START QQUIN:" + item.Number + " PWDHASH:" + item.Password + " /STAT:" + (item.IsStealth ? "40" : "41"));
//写入最后登录日期
item.LastLoginTime = DateTime.Now;
Config.UpdateAccount(Application.StartupPath, item);
//MessageBox.Show("Ctrl + " + item.FlashKey);
}
}
}
这些注册和注销快捷键都是系统启动和退出时的动作,为使我们新增加或更改的帐号快捷键也生效,就得在增加完帐号后把增加的快捷键也注册了:
AddAccountForm myAddAccountForm = new AddAccountForm(accounts);

if (myAddAccountForm.ShowDialog() == DialogResult.OK)
{
//注册热键
FlashKeyEx.RegisterHotKey(this.Handle, accounts[accounts.Count - 1].KeyId, FlashKeyEx.KeyModifiers.Ctrl, (Keys)keysConv.ConvertFromString(accounts[accounts.Count - 1].FlashKey));
BindToListView();
}
如果更新了快捷键,则应把原来的先注销才重新注册。当然,在我们删除帐号时也应该同时注销相应的热键,这里就不多说了。
到这里,这个小工具已经基本实现第一篇中提到的需求,如果要再完善一点,就是在这个基础上加上语音提示,软件的运行进度和操作都有语音提示,这样才能够完全让盲人朋友放心使用。微软提供了一套语音识别和朗读的SDK开发包SAPI,感兴趣的朋友可以到微软件网站下载,暂时这就不加这部份的功能了。这里附上源码给大家分析,欢迎大家把它完善起来。在软件的结构设计方面做的也不够,还请多多指点,多多提意见。谢谢啦!
MQQlogin源码下载
这里的界面,我仿造了来自网上的一款QQ登录器的界面,并加入了一些皮肤。

我们先来写添加帐号,由于添加帐号和修改帐号的所需要显示的内容都是一样的,所以我们用继承的方式来重用相同的界面元素,在Login程里新建一个AccountEditForm窗体,加入"昵称","QQ号码","密码","是否隐身","快捷键"等元素,如下图:

然后再加入一个AddAccountForm和一个UpdateAccountForm,分别继承于它,类关系图如下:

先在LoginForm类中添加一个实体对象用于存帐号数据:List<Account> accounts;
然后在该窗体的构造方法中给其赋值:















双击LoginForm中的“增加帐号”按钮,在里面输入:
AddAccountForm myAddAccountForm = new AddAccountForm(accounts);
myAddAccountForm.ShowDialog();
在添加帐号的类中,我们声明一个Account类型的私有成员private List<Account> items;然后在构造方法中给它赋值:
public AddAccountForm(List<Account> accounts)
: this()
{
items = accounts;
}
这样,我就把主窗体中的帐号对象accounts传递到了添加窗体,当用户点确定按钮时,就在该数据集中添加一个数据对象:

































在删除帐号时,我们可以在ListView中选择多个帐号,点删除。当点击“删除”按钮时,系统会遍历数据集,把选中的帐号存到另一数据集中,然后调用XML读写文件类Config.cs的DeleteAccount方法执行删除操作









































在Login工程中我们新建一个类"FlashKey.cs",输入如下代码:


























using System.Windows.Forms;
using System.Runtime.InteropServices;
是不能忘记的。我们后面的注册和注销快捷键就得能过该类来实现了。
在主窗体开始构造时我们开始注册快捷键:




































































































到这里,这个小工具已经基本实现第一篇中提到的需求,如果要再完善一点,就是在这个基础上加上语音提示,软件的运行进度和操作都有语音提示,这样才能够完全让盲人朋友放心使用。微软提供了一套语音识别和朗读的SDK开发包SAPI,感兴趣的朋友可以到微软件网站下载,暂时这就不加这部份的功能了。这里附上源码给大家分析,欢迎大家把它完善起来。在软件的结构设计方面做的也不够,还请多多指点,多多提意见。谢谢啦!
MQQlogin源码下载