using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Ports; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using POSV.Entity; using POSV.HttpApi; using POSV.ShoppingCart; using POSV.Utils; using System.Data.SQLite; using POSV.Cef.Client.ClientComm; using POS.Language.Language; using POSV.OtherWaiMai; using POSV.QiMai; using POSV.Common.Util; using NLog.RestTarget; using POSV.Common.Wlan; using System.Net.NetworkInformation; using POSV.Payment.Abmcs; using System.Security.Cryptography; using POSV.Dianping; using POSV.Card; namespace POSV { static class Program { private const int SW_HIDE = 0; private const int SW_NORMAL = 1; private const int SW_MAXIMIZE = 3; private const int SW_SHOWNOACTIVATE = 4; private const int SW_SHOW = 5; private const int SW_MINIMIZE = 6; private const int SW_RESTORE = 9; private const int SW_SHOWDEFAULT = 10; private static ExceptionForm exception = null; /// 该函数设置由不同线程产生的窗口的显示状态 /// /// 窗口句柄 /// 指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分 /// 如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零 [DllImport("User32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); /// /// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。 /// 系统给创建前台窗口的线程分配的权限稍高于其他线程。 /// /// 将被激活并被调入前台的窗口句柄 /// 如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零 [DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); public static Mutex AppMutex; /// /// 应用程序的主入口点。 /// [STAThread] static void Main() { //全局异常捕捉 Application.ThreadException += ApplicationThreadException; //UI线程异常 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; //多线程异常 //NetworkInterface[] allInterfaces = NetworkInterface.GetAllNetworkInterfaces(); //NetworkInterface firstInterface = allInterfaces[0]; //foreach (var i in allInterfaces) //{ // bool interfaceSupportsIPv6 = InterfaceHasIpv6Enabled(i); //} bool createdNew = true; AppMutex = new System.Threading.Mutex(true, Application.ProductName, out createdNew); if (createdNew) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); SplashScreen.ShowSplashScreen(); //初始化NLog NLogUtils.InitLogger(); SplashScreen.SetStatus("加载运行日志..."); Global.Instance.Restart = false; AutoUpdateNew(); var _type = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_LANGUAGETYPE, "中文"); LangProxy.SetLangTypeVal(_type); //检测是否WiFi网路连接 Global.Instance.IsWiFi = WiFiApi.Connected(); SplashScreen.SetStatus(LangProxy.ToLang("检测本机网络连接...")); var openApiTask = Task.Factory.StartNew(() => { return HttpClientUtils.IsAvailable(); }); Task.WaitAll(openApiTask); //网络可用:a)网卡工作;b)可以联网;c)开放平台正常 bool isConnected = openApiTask.Result; //网络连接压入全局参数 Global.Instance.Online = isConnected; SplashScreen.SetStatus(string.Format(LangProxy.ToLang("本机网络连接{0}..."), isConnected ? LangProxy.ToLang("正常") : LangProxy.ToLang("不正常"))); SplashScreen.SetStatus(LangProxy.ToLang("检测服务中心连接...")); try { Global.Instance.MessageCenterOnline = MessageCenterUtils.Instance.IsAvailable(); } catch { } SplashScreen.SetStatus(LangProxy.ToLang("设备自检中...")); var finger = DeviceUtils.Instance; bool isGo = true; SplashScreen.SetStatus(LangProxy.ToLang("检测数据库版本...")); bool isException = AutoUpdateDatabase(); if (isException) { isGo = false; SplashScreen.SetStatus(LangProxy.ToLang("数据更新失败,即将退出...")); } else { SplashScreen.SetStatus(LangProxy.ToLang("数据更新成功...")); } if (isGo) { SplashScreen.SetStatus(LangProxy.ToLang("启动定时任务...")); JobUtils.Startup(); SplashScreen.CloseForm(); //初始化来电猫全局参数,应用关闭后主动关闭端口 Global.Instance.Cats = new List(); //登陆窗口,并最大化 FastLoginForm loginForm = new FastLoginForm(); ShowWindowAsync(loginForm.Handle, SW_MAXIMIZE);//显示 SetForegroundWindow(loginForm.Handle);//当到最前端 if (DialogResult.OK == loginForm.ShowDialog()) { //zhangy 2020-03-05 Add 登陆成功后,更新数据库默认参数 //AutoUpdateParamVersion(); //联机状态 if (Global.Instance.Online) { var download = new FastDownloading(); if (DialogResult.OK == download.ShowDialog()) { var wsse = WSSE.Instance; StartTheme(); } } else { var wsse = WSSE.Instance; StartTheme(); } } //关闭来电猫端口 Global.Instance.Cats.ForEach(x => { if (x != null && x.IsOpen) { x.Close(); NLog.LogManager.GetCurrentClassLogger().Info("关闭来电参数<{0},{1}>", x.PortName, x.BaudRate); } }); //关闭云端推送连接 Proxy.Mqtt.MqttClientUtils.Instance.Stop(); NLog.LogManager.GetCurrentClassLogger().Info("关闭云端推送连接"); //关闭消息中心连接 MqttUtils.Instance.Stop(); NLog.LogManager.GetCurrentClassLogger().Info("关闭消息中心连接"); //关闭计划任务 JobUtils.Stop(); NLog.LogManager.GetCurrentClassLogger().Info("关闭计划任务"); if (Global.Instance.Restart) { AppMutex.ReleaseMutex(); AppMutex.Dispose(); AppMutex = null; Application.Exit(); System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location); System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); currentProcess.Kill(); } else { System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); currentProcess.Kill(); } } else { Thread.Sleep(3000); SplashScreen.CloseForm(); Application.ExitThread(); } } else { Process instance = Instance(); ShowWindowAsync(instance.MainWindowHandle, SW_MAXIMIZE);//显示 SetForegroundWindow(instance.MainWindowHandle);//当到最前端 } } /// /// 启动对应主题布局界面 /// private static void StartTheme() { Global.Instance.ReloadConfig(); try { //版本选择 ThemeEnum enableTheme = ThemeEnum.快餐; var layoutTheme = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_THEME, ThemeEnum.快餐.ToString()); Enum.TryParse(layoutTheme, out enableTheme); switch (enableTheme) { case ThemeEnum.茶饮: { Application.Run(new TeaTemplate()); } break; case ThemeEnum.新食堂: { DownloadCefDependentFile DownloadCef = new DownloadCefDependentFile(); if (!DownloadCef.Needtodownload()) { DownloadCef.ShowDialog(); if (!DownloadCef.IsAccomplish) { //zhangy 2020-03-01 Add 新食堂版本依赖CEF环境,如果环境异常,重置到快餐模式,以便于下次正常启动 using (var db = Global.Instance.OpenDataBase) { var config = db.Single(" where [group] = @0 and keys = @1", ConfigConstant.LAYOUT_GROUP, ConfigConstant.LAYOUT_THEME); if (config != null) { config.Values = ThemeEnum.快餐.ToString(); using (var trans = db.GetTransaction()) { db.Save(config); trans.Complete(); } } } MessageBox.Show("运行环境出现问题,已经恢复到快餐模式,请与管理员联系!"); Application.ExitThread(); } } Application.Run(new POSV.Cef.FastTemplate.FastTemplate()); } break; case ThemeEnum.食堂: { Application.Run(new FastTemplate());//zhangy 2020-002-18 Add 已经在参数配置中屏蔽 } break; case ThemeEnum.称重: { Application.Run(new WeightTemplate()); } break; case ThemeEnum.快餐: default: { Application.Run(new DefaultTemplate()); } break; } } catch (Exception ex) { if (exception == null || exception.IsDisposed) { exception = new ExceptionForm(); } exception.SetMessage = ex.Message; exception.SetErrorMessage = ex.StackTrace; exception.ShowDialog(); } } /// /// 只运行一个实例 /// /// private static Process Instance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(current.ProcessName); foreach (Process process in processes) { //如果实例已经存在则忽略当前进程 if (process.Id != current.Id) { //保证要打开的进程同已经存在的进程来自同一文件路径 if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName) { //返回已经存在的进程 return process; } } } return null; } /// /// 主程序更新 AutoUpdater.exe /// static void AutoUpdateNew() { string auto_update = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"AutoUpdater.exe"); string auto_update_new = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constant.AUTO_UPDATER_NEW); string auto_update_backup = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\backup\AutoUpdater.exe"); if (File.Exists(auto_update_new)) { if (File.Exists(auto_update)) { if (File.Exists(auto_update_backup)) { File.Delete(auto_update_backup); } File.Move(auto_update, auto_update_backup); } FileInfo fi = new FileInfo(auto_update_new); fi.MoveTo(auto_update); } } //static void AutoUpdateParamVersion() //{ // try // { // ApplicationVersion entity = null; // //判断数据库中记录的版本 // using (var db = Global.Instance.OpenDataBase) // { // entity = db.Query().OrderByDescending(x => x.ModifyDate).FirstOrDefault(); // } // if(entity != null) // { // //数据库版本大于参数版本 // if (string.Compare(entity.DbVersion,entity.ParamVersion) > 0) // { // var _name = string.Format("data.db_{0}.txt", entity.DbVersion); // //获得正在运行类所在的名称空间 // Type type = MethodBase.GetCurrentMethod().DeclaringType; // string _namespace = type.Namespace; // //获得当前运行的Assembly // Assembly _assembly = Assembly.GetExecutingAssembly(); // //根据名称空间和文件名生成资源名称 // string resourceName = _namespace + "." + _name; // var list = new ConcurrentQueue(); // using (var stream = _assembly.GetManifestResourceStream(resourceName)) // { // if (stream != null) // { // NLog.LogManager.GetCurrentClassLogger().Info("默认参数更新<{0}>", _name); // string json = string.Empty; // using (StreamReader reader = new StreamReader(stream)) // { // json = reader.ReadToEnd(); // } // if (!string.IsNullOrEmpty(json)) // { // var migrate = DynamicJson.Parse(json); // if (migrate != null) // { // //处理租户更新,影响具体租户全部门店 // if (migrate.IsDefined("tenant") && Global.Instance.Authc != null) // { // var tenant = migrate["tenant"]; // //zhangy 2020-03-05 JSON解析有问题,不能以数字开头 // var tenantNo = "tenant_" + Global.Instance.Authc.TenantId; // if (tenant.IsDefined(tenantNo)) // { // var arrayJson = tenant[tenantNo]; // foreach (var item in arrayJson) // { // list.Enqueue(item); // } // } // } // //处理租户门店更新,影响具体租户具体门店 // if (migrate.IsDefined("store") && Global.Instance.Authc != null) // { // var storeNo = "store" + Global.Instance.Authc.TenantId + "_" + Global.Instance.Authc.StoreNo; // var tenant = migrate["store"]; // if (tenant.IsDefined(storeNo)) // { // var arrayJson = tenant[storeNo]; // foreach (var item in arrayJson) // { // list.Enqueue(item); // } // } // } // } // } // } // } // if(list.Count > 0) // { // SqliteUtils.ExecuteTransaction(list); // //刷新参数 // Global.Instance.ReloadConfig(); // NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新成功"); // } // entity.ParamVersion = entity.DbVersion; // using (var db = Global.Instance.OpenDataBase) // { // using (var trans = db.GetTransaction()) // { // db.Save(entity); // trans.Complete(); // } // } // } // } // else // { // NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新失败:原因:表没有数据"); // } // } // catch (Exception ex) // { // NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常"); // } //} static bool AutoUpdateDatabase() { bool isException = false; //当前程序嵌入的数据库版本 int _version = 1; //数据库的版本 int _dbVersion = 1; ApplicationVersion entity = null; try { var _array = Application.ProductVersion.Split('.'); int major = 0; int.TryParse(_array[0], out major); int minor = 0; int.TryParse(_array[1], out minor); int build = 2; int.TryParse(_array[2], out build); //当前程序嵌入的数据库版本 _version = major * 100 + minor * 10 + build; //应用的版本 var _appVersion = Application.ProductVersion; //判断数据库中记录的版本 using (var db = Global.Instance.OpenDataBase) { entity = db.Query().OrderByDescending(x => x.ModifyDate).FirstOrDefault(); } if (entity == null) { entity = new ApplicationVersion(); entity.Id = IdWorkerUtils.Instance.NextId(); entity.AppVersion = _appVersion; entity.DbVersion = _dbVersion.ToString(); } int.TryParse(entity.DbVersion, out _dbVersion); //升级前备份数据文件 string sourceFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db"); string destFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(@"data\backup\posv2.s3db", "")); //数据库需要升级 if (_version > _dbVersion) { SplashScreen.SetStatus(string.Format("备份历史数据<{0}>...", _dbVersion)); System.IO.File.Copy(sourceFileName, destFileName, true); for (int i = _dbVersion + 1; i <= _version; i++) { var _name = string.Format("data.db_{0}.txt", i); //获得正在运行类所在的名称空间 Type type = MethodBase.GetCurrentMethod().DeclaringType; string _namespace = type.Namespace; //获得当前运行的Assembly Assembly _assembly = Assembly.GetExecutingAssembly(); //根据名称空间和文件名生成资源名称 string resourceName = _namespace + "." + _name; Tuple> migrate = null; using (Stream stream = _assembly.GetManifestResourceStream(resourceName)) { if (stream == null) { continue; } NLog.LogManager.GetCurrentClassLogger().Info("发布数据库更新<{0}>", _name); string json = string.Empty; using (StreamReader reader = new StreamReader(stream)) { json = reader.ReadToEnd(); } if (!string.IsNullOrEmpty(json)) { migrate = ParseDbMigrate(json); } } if (migrate != null && migrate.Item1) { var sqls = migrate.Item2; SplashScreen.SetStatus(string.Format("更新数据,请稍候...")); foreach (var sql in migrate.Item2) { try { SqliteUtils.Execute(sql); } catch (Exception ex) { if (ex.Message.Contains("duplicate column name")) { NLog.LogManager.GetCurrentClassLogger().Error(ex, "列名升级失败,已存在:"+ sql); } else { NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常"); } } } } } } } catch (Exception ex) { isException = true; NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常"); } finally { if (!isException) { entity.AppVersion = Application.ProductVersion; entity.DbVersion = _version.ToString(); using (var db = Global.Instance.OpenDataBase) { using (var trans = db.GetTransaction()) { db.Save(entity); trans.Complete(); } } } //刷新Http请求的AppKey,避免调用错误 OpenApiUtils.Instance.Refresh(); } return isException; } static Tuple> ParseDbMigrate(string json) { Tuple> result = null; try { NLog.LogManager.GetCurrentClassLogger().Info("解析数据库更新脚本......"); var list = new ConcurrentQueue(); var migrate = DynamicJson.Parse(json); if (migrate != null) { //处理全局更新,影响全部客户 if (migrate.IsDefined("global")) { var global = migrate["global"]; if (global.IsDefined("all")) { var arrayJson = global["all"]; foreach (var item in arrayJson) { list.Enqueue(item); } NLog.LogManager.GetCurrentClassLogger().Info("处理数据库全局更新......"); } } ////处理租户更新,影响具体租户全部门店 //if (migrate.IsDefined("tenant") && Global.Instance.Authc != null) //{ // var tenant = migrate["tenant"]; // var tenantNo = Global.Instance.Authc.TenantId; // if (tenant.IsDefined(tenantNo)) // { // var arrayJson = tenant[tenantNo]; // foreach (var item in arrayJson) // { // list.Enqueue(item); // } // NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0); // } //} ////处理租户门店更新,影响具体租户具体门店 //if (migrate.IsDefined("store") && Global.Instance.Authc != null) //{ // var storeNo = Global.Instance.Authc.TenantId+"_"+ Global.Instance.Authc.StoreNo; // var tenant = migrate["store"]; // if (tenant.IsDefined(storeNo)) // { // var arrayJson = tenant[storeNo]; // foreach (var item in arrayJson) // { // list.Enqueue(item); // } // NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0); // } //} } result = new Tuple>(true, list); } catch (Exception ex) { NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级文件格式错误"); result = new Tuple>(false, null); } return result; } /// /// 修改数据库密码 /// public static void ChangePwd(string pwd) { SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder(); builder.DataSource = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db"); builder.Password = "passwd"; builder.Version = 3; builder.CacheSize = 10000; builder.DefaultTimeout = 5000; builder.PageSize = 4096; builder.FailIfMissing = true; builder.Pooling = true; builder.DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted; builder.SyncMode = SynchronizationModes.Off; builder.JournalMode = SQLiteJournalModeEnum.Truncate; builder.UseUTF16Encoding = false; using (SQLiteConnection connection = new SQLiteConnection(builder.ToString())) { try { connection.Open(); connection.ChangePassword(pwd); } catch (SQLiteException ex) { throw new Exception(ex.Message); } finally { connection.Close(); } } } //UI线程异常 static void ApplicationThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { NLog.LogManager.GetCurrentClassLogger().Error(e.Exception, "ApplicationThreadException"); Global.Instance.BugReport(e.Exception); } //多线程异常 static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) { NLog.LogManager.GetCurrentClassLogger().Error(e.ExceptionObject as Exception, "ApplicationThreadException"); Global.Instance.BugReport(e.ExceptionObject as Exception); } public static bool InterfaceHasIpv6Enabled(NetworkInterface @interface) { try { var properties = @interface.GetIPProperties().GetIPv6Properties(); return properties.Index > -999; } catch (NetworkInformationException) { return false; } catch (Exception ex) { throw ex; } } } }