1. 程式人生 > >C# Winform下一個熱插拔的MIS/MRP/ERP框架11(啟航)

C# Winform下一個熱插拔的MIS/MRP/ERP框架11(啟航)

aer tab chan byname 可能 清理 contex cati break

  初學時,有了想法卻完全不知道該從何下指,此序列將拋磚引玉,與大家共同學習進步。

  一個程序的初始,必然是啟動。

  我的要求:

  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) + @"\App\LoginLd.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

登錄界面是一個起點,涉及到文件操作以及數據庫的連接/存取,下次再分解。

C# Winform下一個熱插拔的MIS/MRP/ERP框架11(啟航)