上位机练习小项目(2)登陆功能 与 (3)后台只能运行一个程序实例
软件学习记录:
(2) 登陆功能
- 需求分析:首先是登陆验证功能,需要用户类作为模型。
- 考虑用户类的功能:需要考虑登陆用户能够访问哪些功能界面与业务功能
- 所以为用户类设计属性
- 设计思路:
- 登陆页面需要使用MySql数据库,所以配置Sql连接
- 用户类的字段需要涵盖 用户名,密码,各个功能页面与自动或者手动的访问权限标记字段。
实际设计如下:
软件代码实现思路如下:
- 用户类
/// <summary>
/// 用户类
/// </summary>
public class SysAdmins
{
/// <summary>
/// 用户名称
/// </summary>
public string LoginName { get; set; }
/// <summary>
/// 用户密码
/// </summary>
public string LoginPwd { get; set; }
/// <summary>
/// 手动控制权限
/// </summary>
public bool HandCtrl { get; set; }
/// <summary>
/// 自动控制权限
/// </summary>
public bool AutoCtrl { get; set; }
/// <summary>
/// 系统设置页面访问权限
/// </summary>
public bool SysSet { get; set; }
/// <summary>
/// 日志页面访问权限
/// </summary>
public bool SysLog { get; set; }
/// <summary>
/// 统计页面访问权限
/// </summary>
public bool Report { get; set; }
/// <summary>
/// 统计报表页面访问权限
/// </summary>
public bool Trend { get; set; }
/// <summary>
/// 用户管理页面访问权限
/// </summary>
public bool UserManage { get; set; }
}
-
登陆界面。目前不需要验证码功能,暂时设计如下:
登陆界面:分析应该需要完成三个功能函数
- 当窗体加载的时候,登陆用户框加载所有的用户名称
- 点击登陆按钮的时候触发,将登陆名与密码传入实例化的SysAdmins模型类中,进行登陆验证。登陆验证的流程大致如下:
- 分别检测用户名与密码栏是否为空,不为空进入下一步;为空退出。
- 将登陆界面的账户与密码(需要使用md5进行加密)信息传入实例中,调用登陆验证函数private SysAdmins AdminCheck(SysAdmins objAdmin)。
- 使用DataSet接收返回结果,如果数据不为空,那么将返回的数据传入SysAdmins实例中;如果为空,返回null.
- 判断上层返回的数据是否为null。如果是,提示登陆账户名或者密码不正确;如果返回不为null,说明登陆成功,跳转到功能主界面
- 退出功能:调用Close()方法
大致代码如下:
第一部分:FrmLogin_Load中加载所有的用户名称
/// <summary> /// 初始化用户列表 /// </summary> private void InitialUserList() { string sql = "select * from SysAdmins"; MySqlDataReader dr = MySQLHelper.GetReader(sql); List<string> UserList = new List<string>(); while(dr.Read()) { UserList.Add(dr["LoginName"].ToString()); } if (UserList.Count>0) { this.cmb_User.DataSource = UserList; this.cmb_User.SelectedIndex = 0; } else { MessageBox.Show("用户列表为空"); }
第二部分:登陆验证功能:
private void btn_Login_Click(object sender, EventArgs e) { // 数据验证 if (this.cmb_User.Text.Length == 0) { MessageBox.Show("请选择用户名!"); this.cmb_User.Focus(); return; } if (this.tb_LgnPwd.Text.Length == 0) { MessageBox.Show("请填写密码!"); this.cmb_User.Focus(); return; } SysAdmins objAdmin = new SysAdmins() { LoginName = this.cmb_User.Text.Trim(), LoginPwd = Register.Encrypt(this.tb_LgnPwd.Text.Trim()) }; // 登陆验证 objAdmin = AdminCheck(objAdmin); if (objAdmin != null) { // 设置DialogResult this.DialogResult = DialogResult.OK; CommonMethods.objAdmin = objAdmin; } else { MessageBox.Show("用户名或者密码错误"); } }
验证函数:
private SysAdmins AdminCheck(SysAdmins objAdmin) { string sql = "Select * from SysAdmins where LoginName='{0}' and LoginPwd='{1}'"; sql = string.Format(sql, objAdmin.LoginName, objAdmin.LoginPwd); DataSet ds = MySQLHelper.GetDataSet(sql); if (ds!=null&& ds.Tables.Count>0 && ds.Tables[0].Rows.Count>0) { DataTable dt = ds.Tables[0]; objAdmin.HandCtrl = (dt.Rows[0]["HandCtrl"].ToString() == "1"); objAdmin.AutoCtrl = (dt.Rows[0]["AutoCtrl"].ToString() == "1"); objAdmin.SysSet = (dt.Rows[0]["SysSet"].ToString() == "1"); objAdmin.SysLog = (dt.Rows[0]["SysLog"].ToString() == "1"); objAdmin.Report = (dt.Rows[0]["Report"].ToString() == "1"); objAdmin.Trend = (dt.Rows[0]["Trend"].ToString() == "1"); objAdmin.UserManage = (dt.Rows[0]["UserManage"].ToString() == "1"); return objAdmin; } else { return null; } }
md5加密函数:
static string key = "xxxxxxxx"; static string md5Begin = "Hello"; static string md5End = "World"; public static string Encrypt(string str) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[] inputByteArray = Encoding.Default.GetBytes(str); des.Key = ASCIIEncoding.ASCII.GetBytes(key); des.IV = ASCIIEncoding.ASCII.GetBytes(key); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write); cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock(); StringBuilder ret = new StringBuilder(); foreach (byte b in ms.ToArray()) { ret.AppendFormat("{0:X2}", b); } ret.ToString(); return ret.ToString(); }
第三部分: 关闭系统功能
private void btn_Exit_Click(object sender, EventArgs e) { this.Close(); }
Mutex 互斥锁:(3) 同一时间仅启动一个应用程序
Mutex
互斥锁(Mutex),用于多线程中防止两条线程同时对一个公共资源进行读写的机制。
Mutex可以保证当两个或者两个以上的线程同时访问共享资源的时候,操作系统使用某种机制来保证每次仅仅有一个线程来使用资源
如果no.1线程获取了Mutex,在使用资源的时候。no.2线程想要访问资源,但是此时无法获取Mutex,将会处于挂起的状态。
函数的构造原型如下:
public Mutex (bool initiallyOwned, string name, out bool createdNew);
Mutex的对象有两种状态: signaled 和 nonsignaled
通过new来实例化的Mutex类,会在系统中检测互斥量的name是否被占据;若没有占有,那么会创建name的变量来获取此互斥量的使用权,并且当前的createdNew=true。
initiallyOwned:它的作用是是否允许线程是否能够获取到此互斥量的初始化所有权。如果希望后台仅仅只有一个程序运行,那么应当设置为false
Mutex 的用法(参考网上的案例):
-
后台仅能运行一个程序
class Program { // 第一个程序 const string name = "OnlyOne"; private static Mutex m; static void Main(string[] args) { // 本程序是否是 Mutex 的拥有者 bool firstInstance; m = new Mutex(false,name,out firstInstance); ///第一次运行的时候 firstInstance 被返回了true;第二次运行的时候,被返回了false。 if (!firstInstance) { Console.WriteLine("程序已在运行!按下回车键退出!"); Console.ReadKey(); return; } Console.WriteLine("程序已经启动"); Console.WriteLine("按下回车键退出运行"); Console.ReadKey(); m.ReleaseMutex(); m.Close(); return; } }
-
线程排队(交替运行)
waitone()等待别的线程释放互斥量,ReleaseMutex()释放当前线程的互斥量
class Program { // 第一个程序 const string name = "OnlyOne"; private static Mutex m; static void Main(string[] args) { // wc 还有没有位置 bool firstInstance; m = new Mutex(true,name,out firstInstance); // 已经有人在上wc if (!firstInstance) { // 等待运行的实例退出,此进程才能运行。 Console.WriteLine("排队等待"); m.WaitOne(); GoWC(); return; } GoWC(); return; } private static void GoWC() { Console.WriteLine(" 开始上wc"); Thread.Sleep(1000); Console.WriteLine(" 开门"); Thread.Sleep(1000); Console.WriteLine(" 关门"); Thread.Sleep(1000); Console.WriteLine(" xxx"); Thread.Sleep(1000); Console.WriteLine(" 开门"); Thread.Sleep(1000); Console.WriteLine(" 离开wc"); m.ReleaseMutex(); Thread.Sleep(1000); Console.WriteLine(" 洗手"); } }
在本软件中的Program.cs代码如下:
设计思路:第一个线程的mutex.WaitOne一定是true,一定接收到信号。第二个线程在执行到这一步的时候得到没有接收到信号,所以是false
static class Program
{
public static Mutex mutex;
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 判断程序是否运行
mutex = new Mutex(true, "OnlyRun");
if (!mutex.WaitOne(0,false))
{
MessageBox.Show("运动控制已经运行","运行提示",MessageBoxButtons.OK);
Application.Exit();
return;
}
else
{
FrmLogin objFrm = new FrmLogin();
objFrm.TopMost = true;
if (objFrm.ShowDialog()==DialogResult.OK)
{
Application.Run(new FrmMain());
}
else
{
Application.Exit();
}
}
}
}