C# Winform下一个热插拔的MIS/MRP/ERP框架11(启航)

  初学时,有了想法却完全不知道该从何下指,此序列将抛砖引玉,与大家共同学习进步。

  一个程序的初始,必然是启动。

  我的要求:

  1、应用程序保持单例;

  2、从配置文件加载一些基础数据进行初始化;

  3、显示软件的LOGO页面;

  4、判断应用程序是否有更新;

  4、进入用户登录界面;

  5、用户成功登录后显示主界面。

  

  如上图,基本达成目标,这个项目也是很简单的结构:

看窗体名称就知道各自的用途了。主要的Program.cs代码如下:

    static class Program
    {
        public static bool 切换用户 = false;

        /// <summary>
        /// 主体框架应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            string 实例标识 = Assembly.GetExecutingAssembly().GetName().Name + "_SingtonB20";
            Mutex mutex = new Mutex(true, 实例标识, out bool 单实例);
            if (!单实例)
            {
                唤醒进程(实例标识);
                return;
            }

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.DoEvents();

            //初始化全局DS
            if (GlbInfo.DSGlb == null) GlbInfo.DSGlb = new System.Data.DataSet();
            else
            {
                GlbInfo.DSGlb.Clear();
                GlbInfo.DSGlb.Tables.Clear();
            }

            //加载全局多语言资源
            try
            {
                AppInit.CreateDT_MulLang(AppInfo.GetFile(AppInfo.AppFile.MulMessage), "Message_Core");
                AppInit.CreateDT_MulLang(AppInfo.GetFile(AppInfo.AppFile.MulSurface), "Surface_Core");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                Environment.Exit(0);
            }

            //展示闪屏窗体
            Fm20Splash 闪屏窗 = new Fm20Splash();
            闪屏窗.ShowDialog();

            //系统检测正常则显示登录界面,否则退出应用
            if (闪屏窗.DialogResult == DialogResult.OK)
            {
                显示登录:
                切换用户 = false;
                Fm20Login fmLogin = new Fm20Login();
                fmLogin.ShowDialog();
                if (fmLogin.DialogResult == DialogResult.OK)
                {
                    Application.Run(new Fm20Main());
                }
                else
                {
                    Application.Exit();
                }
                //当关闭主程序的时候会执行这个代码,在关闭主程序的时候需要给IsLogin 设置成true
                //重新回到登录窗口.
                if (切换用户)
                {
                    goto 显示登录;
                }

            }
            else
            {
                Application.Exit();
            }

        }
        public static void 唤醒进程(string 进程名称)
        {
            try
            {
                Process proc = Process.GetCurrentProcess();

                string assemblyName = Assembly.GetExecutingAssembly().GetName().Name + 进程名称;

                foreach (Process 进程 in Process.GetProcessesByName(进程名称))
                {
                    if (proc.Id != 进程.Id)                    
                    {
                        IntPtr hWnd = 进程.MainWindowHandle;
                        if (WinAPI.IsIconic(hWnd))
                        {
                            WinAPI.ShowWindowAsync(hWnd, WinAPIConst.SW_RESTORE);
                        }
                        WinAPI.SetForegroundWindow(hWnd);
                        return;
                    }
                }
            }
            catch
            {
            }
        }

    }
Program.cs

一、第一个被运行的窗体(闪屏窗口):

    /// <summary>
    /// Splash闪屏窗体
    /// </summary>
    public partial class Fm20Splash : Fm11Base
    {
        /// <summary>
        /// 创建闪屏窗体
        /// </summary>
        public Fm20Splash()
        {
            InitializeComponent();
            OINI oINI = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
            GlbInfo.Language = oINI.ReadString("Global", "CurrentLang", "LL");
            string licCompany = oINI.ReadString("Global", "License_To_" + GlbInfo.Language, "所有人");
            LabLicTo.Text = MultiLang.Surface(this, "LabLicTo", "授权给: ") + licCompany;
            if (GlbInfo.Language == "LL") LabSysTitle.Text = "IMES (智能制造执行系统)"; else LabSysTitle.Text= "IMES (Intelligent Manufacturing Execution System)";
        }

        private void Fm20Splash_MouseDown(object sender, MouseEventArgs e)
        {
            WinAPI.ReleaseCapture();
            WinAPI.SendMessage(Handle, WinAPIConst.WM_SYSCOMMAND, WinAPIConst.SC_MOVE + WinAPIConst.HTCAPTION, 0);
        }

        private static bool Delay(int delayTime)
        {
            DateTime now = DateTime.Now;
            int s;
            do
            {
                TimeSpan spand = DateTime.Now - now;
                s = spand.Seconds;
                Application.DoEvents();
            }
            while (s < delayTime);
            return true;
        }

        private void Fm20Splash_Shown(object sender, EventArgs e)
        {
            //开始检测系统配置是否符合要求
            //检测1:FRAMEWORK
            //检测2:检查组件资源
            #region 检测1:检测更新
            SetMainStatus(MultiLang.Surface(this, "ChkAppVersion","正在检查系统版本..."));
            Delay(1);

            string localPath = AppDomain.CurrentDomain.BaseDirectory;
            localPath = localPath.Substring(0, localPath.Length - 1);
            string updateTxtFile = localPath + @"UpdateLog.txt";
            //当更新指引文件不存在(如调试时),将跳过自动更新检测.
            if (File.Exists(updateTxtFile))
            {
                string[] allLines = File.ReadAllLines(localPath + @"UpdateLog.txt");

                string remotePath = string.Empty;
                foreach (string line in allLines)
                {
                    if (line.IndexOf("UpdatePath=") >= 0)
                    {
                        remotePath = line.Substring(line.IndexOf("UpdatePath=") + 11);
                        break;
                    }
                }

                if (NeedUpdate(localPath, remotePath))
                {
                    File.Delete(localPath + "\" + @"A19.exe");
                    File.Copy(remotePath + "\" + @"A19.exe", localPath + "\" + @"A19.exe", true);

                    string updFile = AppDomain.CurrentDomain.BaseDirectory + @"A19.exe";
                    Process.Start(updFile);
                    DialogResult = DialogResult.Cancel;
                    Application.Exit();
                }
            }
            #endregion

            #region 清理过期插件版本,只保留最近的一项
            //根目录下不存在DLL或EXE原文件,则查找日期最后面的文件作为加载文件
            //查找M开头的exe和dll文件,规划M2-M8为模组EXE命名区域.如M22,M88
            var modFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory).Select(f => new FileInfo(f)).Where(f => ((f.Name.IndexOf("M") == 0 ) && f.Name.IndexOf("--") == 3) && (f.Name.IndexOf(".exe") >= 0 || f.Name.IndexOf(".dll") >= 0)).OrderBy(f => f.Name);
            string prevFileSpl = string.Empty;
            FileInfo ofi = null;
            foreach (var s in modFiles)
            {
                if (!string.IsNullOrEmpty(prevFileSpl))
                {
                    if (s.Name.Substring(0, s.Name.IndexOf("--")) == prevFileSpl.Substring(0, prevFileSpl.IndexOf("--")))
                    {
                        if (s.LastWriteTime >= ofi.LastWriteTime)
                        {
                            ofi.Delete();
                        }
                        else
                        {
                            s.Delete();
                        }
                    }
                }
                ofi = s;
                prevFileSpl = s.Name;
            }

            #endregion

            DialogResult = DialogResult.OK;
        }
        /// <summary>
        /// 判断程序是否需要更新
        /// </summary>
        /// <param name="localPath"></param>
        /// <param name="remotePath"></param>
        /// <returns></returns>
        private bool NeedUpdate(string localPath, string remotePath)
        {
            try
            {
                FileInfo fiUL = new FileInfo(localPath + @"UpdateLog.txt");
                FileInfo fiUR = new FileInfo(remotePath + @"UpdateLog.txt");
                if (fiUL.LastWriteTime == fiUR.LastWriteTime)
                {
                    SetMainStatus(MultiLang.Surface(this, "ChkAppVersion","正在检查系统版本...") + "OK!");
                    return false;
                }
                else
                {
                    return true;
                }
            }
            catch (Exception)
            {
                //出错将返回false,比如:没有设置好更新路径。
                return false; 
            }
        }
    }
Fm20Splash.cs

它继承自一个基类窗体Fm11Base,默认会做一些例如加载多语言文本的事情,没这个需求直接继承Form也可以。

二、这个页面通过后(有更新的话会跳到自动更新程序,可以忽略),会进入到登录窗体:

    /// <summary>
    /// 登录窗体
    /// </summary>
    public partial class Fm20Login : Fm11Base
    {
        bool IsLogin = false;
        protected internal string SelAccountNO ;
        protected internal string SelAccountName ;
        private static readonly string DftAESKeyOfDlls = @"James/Wang/";

        private bool m_aeroEnabled;
        private static Fm20LoginTitle fm20LoginTitle;

        public string UserID
        {
            get { return TxtUserID.Text; }
            set { TxtUserID.Text = value; }
        }

        public Fm20Login()
        {
            m_aeroEnabled = false;
            InitializeComponent();
            fm20LoginTitle = new Fm20LoginTitle();

        }

        #region 无边框窗体处理
        /// <summary>
        /// 拖动无边框窗体
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnFormDrag(object sender, MouseEventArgs e)
        {
            WinAPI.ReleaseCapture();
            WinAPI.SendMessage(Handle, WinAPIConst.WM_SYSCOMMAND, WinAPIConst.SC_MOVE + WinAPIConst.HTCAPTION, 0);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                m_aeroEnabled = CheckAeroEnabled();
                CreateParams cp = base.CreateParams;
                if (!m_aeroEnabled)
                    cp.ClassStyle |= WinAPIConst.CS_DropSHADOW;
                return cp;
            }
        }

        private bool CheckAeroEnabled()
        {
            if (Environment.OSVersion.Version.Major >= 6)
            {
                int enabled = 0;
                WinAPI.DwmIsCompositionEnabled(ref enabled);
                return (enabled == 1) ? true : false;
            }
            return false;
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            switch (m.Msg)
            {
                case WinAPIConst.WM_NCPAINT:
                    if (m_aeroEnabled)
                    {
                        var v = 2;
                        WinAPI.DwmSetWindowAttribute(this.Handle, 2, ref v, 4);
                        WinAPIConst.MARGINS margins = new WinAPIConst.MARGINS()
                        {
                            bottomHeight = 1,
                            leftWidth = 1,
                            rightWidth = 1,
                            topHeight = 1
                        };
                        WinAPI.DwmExtendFrameIntoClientArea(this.Handle, ref margins);

                    }
                    break;
                default:
                    break;
            }

            if (m.Msg == WinAPIConst.WM_NCHITTEST && (int)m.Result == WinAPIConst.HTCLIENT)
                m.Result = (IntPtr)WinAPIConst.HTCAPTION;

        }

        #endregion

        private void MitClose_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
        }

        private void MitMini_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Minimized;
        }

        private void Fm20Login_Shown(object sender, EventArgs e)
        {
            if (!fm20LoginTitle.Visible)
            {
                fm20LoginTitle.Show(this);
            }
            else
            {
                fm20LoginTitle.BringToFront();
            }
            fm20LoginTitle.Left = this.Left + this.Width / 2 - fm20LoginTitle.Width / 2;
            fm20LoginTitle.Top = this.Top + SplPnlMain.Panel1.Height - fm20LoginTitle.Height / 2;
        }

        private void Fm20Login_Move(object sender, EventArgs e)
        {
            fm20LoginTitle.Left = this.Left + this.Width / 2 - fm20LoginTitle.Width / 2;
            fm20LoginTitle.Top = this.Top + SplPnlMain.Panel1.Height - fm20LoginTitle.Height / 2;
        }

        private void Fm20Login_Load(object sender, EventArgs e)
        {
            try
            {
                LabStsVersion.Text = "Version: " + this.ProductVersion;
                TstMILangLL.Checked = (GlbInfo.Language == "LL");
                TstMILangLI.Checked = !TstMILangLL.Checked;
                PicLogo.Image = Image.FromFile(AppInfo.GetPath(AppInfo.AppPath.Image) + @"AppLoginLd.png");
                //读取上一次记住的用户名
                OINI oPIniFile = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
                TxtUserID.Text = oPIniFile.ReadString("Login", "LastUserID", string.Empty);
                SetMainStatus("..");
            }
            catch (Exception)
            {
            }
        }

        private void TxtUserCode_TextChanged(object sender, EventArgs e)
        {
            CmdClearUser.Visible = !(string.IsNullOrEmpty(TxtUserID.Text.Trim()));
        }

        private void TxtUserCode_Enter(object sender, EventArgs e)
        {
            PicFeedUser.BackgroundImage= Properties.Resources.LoginUserB;
        }

        private void TxtUserCode_Leave(object sender, EventArgs e)
        {
            PicFeedUser.BackgroundImage = Properties.Resources.LoginUserH;
        }

        private void TxtUserPass_TextChanged(object sender, EventArgs e)
        {
            CmdClearPwd.Visible = !(string.IsNullOrEmpty(TxtUserPass.Text.Trim()));
        }

        private void TxtUserPass_Enter(object sender, EventArgs e)
        {
            PicFeedLock.BackgroundImage = Properties.Resources.LoginPwdB;
        }

        private void TxtUserPass_Leave(object sender, EventArgs e)
        {
            PicFeedLock.BackgroundImage = Properties.Resources.LoginPwdH;
        }

        private void CmdClearUser_Click(object sender, EventArgs e)
        {
            TxtUserID.Clear();
        }

        private void CmdClearPwd_Click(object sender, EventArgs e)
        {
            TxtUserPass.Clear();
        }
        private void SetLanguage(string tagLang)
        {
            try
            {
                OINI oINI = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
                oINI.WriteString("Global", "CurrentLang", tagLang);
                TstMILangLL.Checked = (tagLang == "LL");
                TstMILangLI.Checked = !TstMILangLL.Checked;
                GlbInfo.Language = tagLang;
                InitObjectsText(this);
            }
            catch (Exception)
            {
                throw;
            }
        }
        private void TstMILangLL_Click(object sender, EventArgs e)
        {
            SetLanguage("LL");
        }

        private void TstMILangLI_Click(object sender, EventArgs e)
        {
            SetLanguage("LI");
            
        }

        private void CmdLogin_Click(object sender, EventArgs e)
        {
            string tmpSts1 = MultiLang.Surface(this, "VeriUser", "正在验证用户...");
            SetMainStatus(tmpSts1);

            try
            {
                string postUserID = TxtUserID.Text;
                string postUserPass = TxtUserPass.Text;
                if (string.IsNullOrEmpty(postUserID) || string.IsNullOrEmpty(postUserPass))
                {
                    MyMsg.Information("T.201001/用户名和密码不能为空.");
                    TxtUserID.Focus();
                    return;
                }
                if ((SelAccountNO == "DEMOH") )
                {
                    MyMsg.Warning("T.201002/此帐套尚未开放,请选择其它帐套.");
                    return;
                }

                if (SelAccountNO == "DEMO")
                {
                    MyMsg.Warning("T.201003/您登录的是演示库,数据可能随时丢失,如需正式作业,请登录NORMAL正式库.");
                }
                string connStrCurAct = GetConnStrFromCfg(SelAccountNO, "ConnStr");
                string connStrExt1 = GetConnStrFromCfg(SelAccountNO, "ConnStrExt1");
                string connStrExt2 = GetConnStrFromCfg(SelAccountNO, "ConnStrExt2");
                string connStrExt3 = GetConnStrFromCfg(SelAccountNO, "ConnStrExt3");
                string connStrExt4 = GetConnStrFromCfg(SelAccountNO, "ConnStrExt4");
                string connStrExt5 = GetConnStrFromCfg(SelAccountNO, "ConnStrExt5");
                this.Cursor = Cursors.WaitCursor;
                #region 用户ERP登录验证部分
                DBContext dBContext = new DBContext(connStrCurAct);
                if (!dBContext.IsConnected)
                {
                    MyMsg.Error("T.1010/数据库连接已经创建,但无法开启!", dBContext.ConnectErrorMessage);
                    return;  
                }

                postUserPass = OCrypto.AES16Encrypt(postUserPass, DftAESKeyOfDlls);
                SqlParameter[] paras = new SqlParameter[]
                {
                                    new SqlParameter("@UserID", postUserID),
                                    new SqlParameter("@PasswordHash", postUserPass)
                };
                string sqlText= "SELECT     U.*, D.DeptCode, D.DeptNameLL, D.DeptNameLI FROM SYS_User AS U LEFT OUTER JOIN  Base_Department AS D ON U.DeptRKEY = D.DeptRKEY WHERE (U.UserID= @UserID) AND (U.PasswordHash= @PasswordHash)";
                DataTable tmpDTUInfo = dBContext.SqlToDT(sqlText, paras);
                if (tmpDTUInfo.Rows.Count > 0)
                {
                    DataRow drUser = tmpDTUInfo.Rows[0];
                    if (!OString.NZ2Bool(drUser["IActived"]))
                    {
                        IsLogin = false;
                        SetMainStatus(MultiLang.Surface(this, "LoginFail1", "登录失败(帐号未激活),请联系管理员!"));
                    }
                    else if (OString.NZ2Bool(drUser["ILocked"]))
                    {
                        IsLogin = false;
                        SetMainStatus(MultiLang.Surface(this, "LoginFail2", "登录失败(帐号被锁定),请联系管理员!"));
                    }
                    else
                    {
                        GlbInfo.User.UserID = OString.NZ2Str(drUser["UserID"]);
                        GlbInfo.User.UserNameLL = OString.NZ2Str(drUser["UserNameLL"]);
                        GlbInfo.User.IsAdmin = OString.NZ2Bool(drUser["Hero"]);
                        GlbInfo.User.RolesUserIDS = OString.NZ2Str(drUser["RolesUserIDS"]);
                        GlbInfo.User.RightsList.Clear();
                        IsLogin = true;
                    }
                }
                else
                {
                    IsLogin = false;
                    SetMainStatus(MultiLang.Surface(this, "LoginFail3", "登录失败(帐号不存在),请重新尝试!"));
                }

                if (IsLogin)
                {
                    GlbInfo.ActNO = SelAccountNO;
                    GlbInfo.ActName = SelAccountName;
                    GlbInfo.ConnStrCurAct = connStrCurAct;
                    GlbInfo.ConnStrExt1 = connStrExt1;
                    GlbInfo.ConnStrExt2 = connStrExt2;
                    GlbInfo.ConnStrExt3 = connStrExt3;
                    GlbInfo.ConnStrExt4 = connStrExt4;
                    GlbInfo.ConnStrExt5 = connStrExt5;
                    fm20LoginTitle.Close();
                    if (ChkRemember.Checked)
                    {
                        try
                        {
                            //记录下登录用户,以便下次不必输入用户名
                            OINI oPIniFile = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
                            oPIniFile.WriteString("Login", "LastUserID", postUserID);
                        }
                        catch (Exception)
                        {
                        }
                    }

                    DialogResult = DialogResult.OK;
                }
                else
                {
                    TxtUserPass.Text = string.Empty;
                    TxtUserPass.Focus();
                    MyMsg.Warning("T.201004/用户名或密码错误,请检查.");
                }
                #endregion                

            }
            catch (Exception ex)
            {
                SetMainStatus(tmpSts1 + "出现异常!");
                MyMsg.Exclamation(ex.Message);
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// 从配置文件中获取指定帐套数据库连接字符串(已加密)
        /// </summary>
        /// <param name="actNO">帐套代码</param>
        /// <param name="tKey">定义的连接串Key</param>
        /// <returns></returns>
        private string GetConnStrFromCfg(string actNO, string tKey)
        {
            try
            {
                StringCollection Idents = new StringCollection();
                OINI oINI = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
                return oINI.ReadString("Account:" + actNO, tKey, string.Empty);
            }
            catch (Exception ex)
            {
                MyMsg.Error(ex.Message);
                return string.Empty;
            }
        }

        private void LabChkRemember_DoubleClick(object sender, EventArgs e)
        {
            try
            {
                //记录下登录用户,以便下次不必输入用户名
                OINI oPIniFile = new OINI(AppInfo.GetFile(AppInfo.AppFile.ConfigCore));
                oPIniFile.WriteString("Login", "LastUserID", string.Empty);
                MyMsg.Information("已经清除登录历史,下次登录将不再显示当前帐号名称.");
            }
            catch (Exception)
            {
            }
        }
    }
Login.cs

登录界面是一个起点,涉及到文件操作以及数据库的连接/存取,下次再分解。

原文地址:https://www.cnblogs.com/imes/p/9795191.html