using JwKdsV.Core; using JwKdsV.Core.Utils; using JwKdsV.Entity.Common; using JwKdsV.Entity.Tenant; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.SQLite; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace JwKdsV { 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; /// 该函数设置由不同线程产生的窗口的显示状态 /// /// 窗口句柄 /// 指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分 /// 如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零 [DllImport("User32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); /// /// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。 /// 系统给创建前台窗口的线程分配的权限稍高于其他线程。 /// /// 将被激活并被调入前台的窗口句柄 /// 如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零 [DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); /// /// 应用程序的主入口点。 /// [STAThread] static void Main() { //全局异常捕捉 Application.ThreadException += ApplicationThreadException; //UI线程异常 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; //多线程异常 bool createdNew = true; using (Mutex mutex = new Mutex(true, Application.ProductName, out createdNew)) { if (createdNew) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); SplashScreen.ShowSplashScreen(); //初始化NLog NLogUtils.InitLogger(); //数据库去密码 try { ChangePwd(""); NLog.LogManager.GetCurrentClassLogger().Info("sqliteNoP success"); } catch (Exception) { NLog.LogManager.GetCurrentClassLogger().Info("sqliteNoP error, already success!"); } AutoUpdateNew(); SplashScreen.SetStatus("检测本机网络连接..."); var openApiTask = Task.Factory.StartNew(() => { return HttpClientUtils.IsAvailable(ApiType.Business); }); Task.WaitAll(openApiTask); //网络可用:a)网卡工作;b)可以联网;c)开放平台正常 bool isConnected = openApiTask.Result; SplashScreen.SetStatus(string.Format("本机网络连接{0}...", isConnected ? "正常" : "不正常")); //网络连接压入全局参数 Global.Instance.Online = isConnected; bool isGo = true; SplashScreen.SetStatus("检测数据库版本..."); bool isException = AutoUpdateDatabase(); if (isException) { isGo = false; SplashScreen.SetStatus("数据更新失败,即将退出..."); } else { SplashScreen.SetStatus("数据更新成功..."); //重新加载设置,避免升级脚本更新设置,导致第一次不生效 Global.Instance.ReloadConfig(); } SplashScreen.CloseForm(); if (isGo) { if (!CheckSameDevice()) { //设备不一致,需要重新进行注册 var initForm = new TenantInitForm(); initForm.BringToFront(); if (initForm.ShowDialog() != DialogResult.OK) { //自杀 System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); currentProcess.Kill(); } } //登录 var loginForm = new LoginForm(); loginForm.BringToFront(); if (loginForm.ShowDialog() == DialogResult.OK) { //下载处理信息 var download = new DownloadingForm(); download.BringToFront(); if(download.ShowDialog() == DialogResult.OK) { //启动websocket WebSocketEnter.Instance.Init(); //启动定时任务 JobUtils.Startup(); //展示主界面 var mainForm = new MainForm(); mainForm.BringToFront(); Application.Run(mainForm); } } //关闭计划任务 JobUtils.Stop(); NLog.LogManager.GetCurrentClassLogger().Info("关闭计划任务"); } } else { Process instance = Instance(); ShowWindowAsync(instance.MainWindowHandle, SW_MAXIMIZE);//显示 SetForegroundWindow(instance.MainWindowHandle);//当到最前端 } } } /// /// 验证pos设备信息是否变更 /// /// private static bool CheckSameDevice() { Authc authc = null; using(var db = Global.Instance.OpenDataBase) { authc = db.FirstOrDefault("where 1 = 1"); } if(authc == null) { return false; } else { var device = new DeviceInfo(); device.Cpu = authc.CpuSerialNumber; device.Disk = authc.DiskSerialNumber; device.Mac = authc.MacAddress; device.ComputerName = authc.CompterName; return device.Equals(Global.Instance.DeviceInfo); } } /// /// 只运行一个实例 /// /// 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; } //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); } /// /// 主程序更新 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 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\kdsDatav1.s3db"); string destFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(@"Data\backup\kdsDatav1.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); using (StreamReader reader = new StreamReader(stream)) { string json = reader.ReadToEnd(); 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) { NLog.LogManager.GetCurrentClassLogger().Info(sql); if (ex.Message.Contains("duplicate column name")) { NLog.LogManager.GetCurrentClassLogger().Error(ex, "列名升级失败,已存在"); } 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 { 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); } } } } 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\kdsDatav1.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(); } } } } }