|
|
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;
|
|
|
|
|
|
/// 该函数设置由不同线程产生的窗口的显示状态
|
|
|
/// </summary>
|
|
|
/// <param name="hWnd">窗口句柄</param>
|
|
|
/// <param name="cmdShow">指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分</param>
|
|
|
/// <returns>如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零</returns>
|
|
|
[DllImport("User32.dll")]
|
|
|
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
|
|
|
/// <summary>
|
|
|
/// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。
|
|
|
/// 系统给创建前台窗口的线程分配的权限稍高于其他线程。
|
|
|
/// </summary>
|
|
|
/// <param name="hWnd">将被激活并被调入前台的窗口句柄</param>
|
|
|
/// <returns>如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零</returns>
|
|
|
[DllImport("User32.dll")]
|
|
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 应用程序的主入口点。
|
|
|
/// </summary>
|
|
|
[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<bool>(() => { 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);//当到最前端
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 验证pos设备信息是否变更
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
private static bool CheckSameDevice()
|
|
|
{
|
|
|
Authc authc = null;
|
|
|
using(var db = Global.Instance.OpenDataBase)
|
|
|
{
|
|
|
authc = db.FirstOrDefault<Authc>("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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 只运行一个实例
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 主程序更新 AutoUpdater.exe
|
|
|
/// </summary>
|
|
|
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<ApplicationVersion>().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<bool, ConcurrentQueue<string>> 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<bool, ConcurrentQueue<string>> ParseDbMigrate(string json)
|
|
|
{
|
|
|
|
|
|
Tuple<bool, ConcurrentQueue<string>> result = null;
|
|
|
try
|
|
|
{
|
|
|
var list = new ConcurrentQueue<string>();
|
|
|
|
|
|
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<bool, ConcurrentQueue<string>>(true, list);
|
|
|
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级文件格式错误");
|
|
|
|
|
|
result = new Tuple<bool, ConcurrentQueue<string>>(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();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|