上位机练习小项目(2)登陆功能 与 (3)后台只能运行一个程序实例

软件学习记录:

(2) 登陆功能

  1. 需求分析:首先是登陆验证功能,需要用户类作为模型。
    1. 考虑用户类的功能:需要考虑登陆用户能够访问哪些功能界面与业务功能
    2. 所以为用户类设计属性
  2. 设计思路:
    1. 登陆页面需要使用MySql数据库,所以配置Sql连接
    2. 用户类的字段需要涵盖 用户名,密码,各个功能页面与自动或者手动的访问权限标记字段。

实际设计如下:

软件代码实现思路如下:

  1. 用户类
    /// <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; }
    }
  1. 登陆界面。目前不需要验证码功能,暂时设计如下:

    image-20230314211710558

    登陆界面:分析应该需要完成三个功能函数

    1. 当窗体加载的时候,登陆用户框加载所有的用户名称
    2. 点击登陆按钮的时候触发,将登陆名与密码传入实例化的SysAdmins模型类中,进行登陆验证。登陆验证的流程大致如下:
      1. 分别检测用户名与密码栏是否为空,不为空进入下一步;为空退出。
      2. 将登陆界面的账户与密码(需要使用md5进行加密)信息传入实例中,调用登陆验证函数private SysAdmins AdminCheck(SysAdmins objAdmin)。
      3. 使用DataSet接收返回结果,如果数据不为空,那么将返回的数据传入SysAdmins实例中;如果为空,返回null.
      4. 判断上层返回的数据是否为null。如果是,提示登陆账户名或者密码不正确;如果返回不为null,说明登陆成功,跳转到功能主界面
    3. 退出功能:调用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 的用法(参考网上的案例):

  1. 后台仅能运行一个程序

    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;
        }
    }
    
  2. 线程排队(交替运行)

    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();
                }
            }
        }
    }
posted @ 2023-03-14 22:20  聆听微风  阅读(249)  评论(0编辑  收藏  举报