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源码下载

posted on 2008-05-02 14:48  [刚子]  阅读(2494)  评论(10)    收藏  举报

导航