510k-install/Main.cs

1010 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Windows.Forms;
using System.Net;
using System.Security.Cryptography;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using Microsoft.VisualBasic.Logging;
using System.IO.Compression;
using SharpCompress.Archives.Rar;
using SharpCompress.Common;
using SharpCompress.Archives;
using System.Diagnostics.Eventing.Reader;
using System.ServiceProcess;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Security.Policy;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolBar;
using System.Data.SqlTypes;
using Serilog;
using System.Xml.Linq;
using System.IO;
using Microsoft.Win32;
using System.Web;
namespace Start
{
public partial class Main : Form
{
private bool isCanDBConfig = true;
private bool isPortTestOk = false;
private bool isServiceTestOk = false;
private bool isDBNameTestOK = false;
private bool isRuntimeTestOk = false;
public Main()
{
InitializeComponent();
this.MaximizeBox = false;
var physicalAddressList = NetworkInterface.GetAllNetworkInterfaces().Select(t => t.GetPhysicalAddress().ToString());
this.machineTextBox.Text = physicalAddressList.FirstOrDefault()?.ToString();
//this.KeySecreteTextBox.Text = Md5($"{this.machineTextBox.Text}_XINGCANG");
}
int apiPort = 7100;
int vuePort = 9527;
private async void connectButton_Click(object sender, EventArgs e)
{
string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};";
using (SqlConnection connection = new SqlConnection(connectionString))
{
try
{
await Task.Run(() =>
{
connection.Open();
});
WinformLog("Database connection test successful", Color.Green);
isCanDBConfig = false;
serverTextBox.Enabled = isCanDBConfig;
usernameTextBox.Enabled = isCanDBConfig;
passwordTextBox.Enabled = isCanDBConfig;
connectButton.Enabled = false;
selectPathBtn.Enabled = true;
//portBtn.Enabled = true;
//testDBBtn.Enabled = true;
//testServicebtn.Enabled = true;
}
catch (Exception ex)
{
WinformLog($"Database connection test failed{ex.Message}", Color.Red);
}
}
}
private void portBtn_Click(object sender, EventArgs e)
{
if (int.TryParse(nginxPortTBox.Text, out vuePort) == false || int.TryParse(apiPortTBox.Text, out apiPort) == false)
{
WinformLog($"Please enter a valid port", Color.Red);
return;
}
if (IsPortInUse(vuePort))
{
WinformLog($"The front-end port set by the service is occupied, please choose another port", Color.Red);
return;
}
if (IsPortInUse(apiPort))
{
WinformLog($"The back-end port set by the service is occupied, please choose another port", Color.Red);
return;
}
WinformLog("Port test successful", Color.Green);
isPortTestOk = true;
}
private void testServicebtn_Click(object sender, EventArgs e)
{
if (ServiceController.GetServices().Any(t => t.ServiceName == serviceNameTBox.Text))
{
WinformLog($"The set backend service name already exists, please replace it", Color.Red);
return;
}
if (ServiceController.GetServices().Any(t => t.ServiceName == nginxServiceNameTbox.Text))
{
WinformLog($"The nginx service name already exists. Please change it", Color.Red);
return;
}
WinformLog($"Service name test successful", Color.Green);
isServiceTestOk = true;
}
private void testNetcoreRuntime()
{
string runtimeListCommand = "dotnet --list-runtimes";
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/c " + runtimeListCommand;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
bool isAspNetCoreRuntimeInstalled = false;
bool isNETCoreRuntimeInstalled = false;
var lineList = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList();
foreach (string line in lineList)
{
if (line.Contains("Microsoft.AspNetCore.App") && !line.Contains("Preview"))
{
string versionString = line.Split(' ')[1];
Version version = new Version(versionString);
if (version.Major > 6 || (version.Major == 6 && version.Minor >= 0))
{
// 大于等于 6.0.0 的 .NET 运行时已安装
isAspNetCoreRuntimeInstalled = true;
continue;
}
}
if (line.Contains("Microsoft.NETCore.App") && !line.Contains("Preview"))
{
string versionString = line.Split(' ')[1];
Version version = new Version(versionString);
if (version.Major > 6 || (version.Major == 6 && version.Minor >= 0))
{
// 大于等于 6.0.0 的 .NET 运行时已安装
isNETCoreRuntimeInstalled = true;
continue;
}
}
}
if (isAspNetCoreRuntimeInstalled && isNETCoreRuntimeInstalled)
{
// 执行逻辑,表示大于等于 6.0.0 的 .NET 运行时已安装
WinformLog("Current system >= 6.0.0 NetCore runtime is installed", Color.Green);
isRuntimeTestOk = true;
}
else
{
WinformLog("The current system >= 6.0.0 NetCore runtime is not installed, please install it before running the program", Color.Red);
connectButton.Enabled = false;
//await Task.Run(() =>
// {
// WinformLog("Now start installing the Net core runtime for you...", Color.Orange);
// string runtimePath = Path.Combine(AppContext.BaseDirectory, "Resource/NetCoreRuntime/dotnet-hosting-6.0.0-win.exe");
// var processInfo = new ProcessStartInfo
// {
// FileName = runtimePath, // 安装程序的文件名
// Arguments = $"/S /v /qn REINSTALLMODE=vomus",
// UseShellExecute = false
// };
// var otherProcess = new Process { StartInfo = processInfo };
// otherProcess.Start();
// otherProcess.WaitForExit();
// WinformLog("Net core 6.0.0 runtime installation completed", Color.Green);
// });
}
}
private void testDBBtn_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(dbNameTBox.Text))
{
WinformLog($"Please enter the deployed database name", Color.Red);
return;
}
string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};";
string sql = $"SELECT COUNT(*) FROM sys.databases WHERE name = '{dbNameTBox.Text.Trim()}';";
// 创建连接对象和命令对象
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(sql, connection))
{
// 打开连接
connection.Open();
// 执行查询,返回结果行数
int rowCount = (int)command.ExecuteScalar();
// 判断结果是否大于0即是否存在该数据库
if (rowCount > 0)
{
WinformLog($"{dbNameTBox.Text.Trim()}The database already exists.", Color.Red);
return;
}
}
WinformLog($"Database name test successful", Color.Green);
isDBNameTestOK = true;
}
private void confimDeployBtn_Click(object sender, EventArgs e)
{
portBtn_Click(null, null);
testServicebtn_Click(null, null);
testDBBtn_Click(null, null);
if (string.IsNullOrWhiteSpace(this.folderPathTbox.Text))
{
WinformLog($"Confirmation can only be made after selecting the deployment path, test port, database name, and service name.", Color.Red);
return;
}
if (isDBNameTestOK == false || isPortTestOk == false || isServiceTestOk == false)
{
WinformLog($"Confirmation can only be made after the test port, database name, and service name are all test successful.", Color.Red);
return;
}
WinformLog($"Deployment configuration confirmed successfully", Color.Green);
activeBtn.Enabled = true;
confimDeployBtn.Enabled = false;
selectPathBtn.Enabled = false;
//portBtn.Enabled = false;
//testDBBtn.Enabled = false;
//testServicebtn.Enabled = false;
nginxPortTBox.Enabled = false;
apiPortTBox.Enabled = false;
dbNameTBox.Enabled = false;
serviceNameTBox.Enabled = false;
serviceDisplayNameTBox.Enabled = false;
nginxServiceNameTbox.Enabled = false;
nginxServiceDisplayNameTbox.Enabled = false;
}
private static bool IsPortInUse(int port)
{
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] tcpEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in tcpEndPoints)
{
if (endPoint.Port == port)
{
return true;
}
}
return false;
}
public static string Md5(string target)
{
using (MD5 md5 = MD5.Create())
{ // MD5非线程安全
byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(target));
StringBuilder sb = new StringBuilder(32);
for (int i = 0; i < bytes.Length; ++i)
sb.Append(bytes[i].ToString("x2"));
return sb.ToString();
}
}
private void WinformLog(string message, Color color)
{
if (InvokeRequired)
{
Invoke(new Action(() => WinformLog(message, color)));
return;
}
logTBox.SelectionColor = color;
logTBox.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}: {message}" + Environment.NewLine);
Serilog.Log.Information(message);
}
private async Task ProcessStandardInputAsync(Process process, string cmd, string workDirectory = "")
{
if (!string.IsNullOrWhiteSpace(workDirectory))
{
process.StartInfo.WorkingDirectory = workDirectory;
}
await process.StandardInput.WriteLineAsync(cmd);
await process.StandardInput.FlushAsync();
//while (!process.StandardOutput.EndOfStream)
//{
// var output = await process.StandardOutput.ReadLineAsync();
// if (string.IsNullOrWhiteSpace(output))
// {
// break;
// }
//}
}
private async void activeBtn_Click(object sender, EventArgs e)
{
if (Md5($"{machineTextBox.Text}_XINGCANG") != KeySecreteTextBox.Text.Trim())
{
WinformLog($"The secret key is incorrect, please contact the supplier", Color.Red);
return;
}
this.activeBtn.Enabled = false;
var serviceName = serviceNameTBox.Text;
var serviceDisplayName = serviceDisplayNameTBox.Text;
var nginxServiceName = nginxServiceNameTbox.Text;
var nginxServiceDesName = nginxServiceDisplayNameTbox.Text;
var deployFoder = this.folderPathTbox.Text;
string nginxExtractName = "EIMedViewerWeb";
string apiExtractName = "EIMedViewerService";
string dataExtraName = "EIMedViewerData";
string nginxStartPath = Path.Combine(deployFoder, @$"{nginxExtractName}\");
var nginxConfigPath = Path.Combine(nginxStartPath, @$"conf\nginx.conf");
var nginxEXEPath = Path.Combine(nginxStartPath, "nginx.exe");
string nginxServiceEXEPath = Path.Combine(nginxStartPath, "nginxService.exe");
string nginxServiceXMLPath = Path.Combine(nginxStartPath, "nginxService.xml");
var apiBinPath = Path.Combine(deployFoder, "EIMedViewerService/EI_Med_Viewer.exe");
var apiJsonConfigPath = Path.Combine(deployFoder, "EIMedViewerService/appsettings.Test_510K.json");
string nginxRarFilePath = Path.Combine(AppContext.BaseDirectory, $@"Resource\EIMedViewerWeb.rar");
string apiRarFilePath = Path.Combine(AppContext.BaseDirectory, "Resource/EIMedViewerService.rar");
string dataRarFilePath = Path.Combine(AppContext.BaseDirectory, "Resource/EIMedViewerData.rar");
#region 创建进程执行命令
var startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe"; // 指定要启动的应用程序
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
// 创建新进程并启动
var process = new Process();
process.StartInfo = startInfo;
process.Start();
#endregion
await Task.Run(async () =>
{
#region 解压nginx 和前端部署的文件
WinformLog($"Start decompressing nginx and front-end deployment compressed files...", Color.Green);
string extractPath = Path.Combine(deployFoder, nginxExtractName);
if (!Directory.Exists(extractPath))
{
Directory.CreateDirectory(extractPath);
}
using (var archive = RarArchive.Open(nginxRarFilePath))
{
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
}
else
{
string directoryPath = Path.Combine(extractPath, entry.Key);
Directory.CreateDirectory(directoryPath);
}
}
}
WinformLog($"The nginx and front-end deployment compressed files have been successfully decompressed to: {extractPath}", Color.Green);
#endregion
#region 修改 nginx 配置文件 启动nginx
if (!File.Exists(nginxConfigPath))
{
WinformLog("No nginx exist under the default path", Color.Red);
}
WinformLog("Prepare to write nginx configuration...", Color.Green);
var nginxConfig = File.ReadAllText(nginxConfigPath);
nginxConfig = nginxConfig.Replace("9520", vuePort.ToString());
nginxConfig = nginxConfig.Replace("7100", apiPort.ToString());
File.WriteAllText(nginxConfigPath, nginxConfig);
WinformLog("nginx deployment configuration writing completed", Color.Green);
#region nginxservice
WinformLog("Prepare nginx self-starting service configuration file...", Color.Green);
var nginxServiceConfig = File.ReadAllText(nginxServiceXMLPath);
nginxServiceConfig = nginxServiceConfig.Replace("{nginxName}", nginxServiceName);
nginxServiceConfig = nginxServiceConfig.Replace("{nginxDesName}", nginxServiceDesName);
nginxServiceConfig = nginxServiceConfig.Replace("{nginxPath}", nginxEXEPath);
nginxServiceConfig = nginxServiceConfig.Replace("{nginxStartPath}", nginxStartPath);
File.WriteAllText(nginxServiceXMLPath, nginxServiceConfig);
WinformLog("nginx self-starting service configuration writing completed", Color.Green);
#endregion
WinformLog($"Prepare to create a self-starting nginx service...", Color.Green);
var nginxCreateStr = $@"{nginxServiceEXEPath} install ";
// 执行 sc create 命令来创建服务
await ProcessStandardInputAsync(process, nginxCreateStr);
WinformLog($"The execution of creating service {nginxServiceName} ends", Color.Green);
await Task.Delay(2000);
#region nginx 服务
WinformLog($"Start the deployed {nginxServiceName} service...", Color.Green);
await ProcessStandardInputAsync(process, $"sc start {nginxServiceName}");
ServiceController scNginx = new ServiceController(nginxServiceName);
if (scNginx.Status != ServiceControllerStatus.Running)
{
scNginx.Refresh();
await Task.Delay(3000);
scNginx.Refresh();
await Task.Delay(2000);
}
if (scNginx.Status == ServiceControllerStatus.Running)
{
WinformLog($"nginx service started successfully", Color.Green);
}
else
{
WinformLog($"nginx service failed to start", Color.Red);
}
#endregion
#region 命令行方式启动 废弃
//// 创建ProcessStartInfo对象指定要启动的可执行文件及其参数
//ProcessStartInfo psi = new ProcessStartInfo(nginxEXEPath);
//// 指定工作目录即进入nginx.exe所在的目录
//psi.WorkingDirectory = nginxStartPath;
//WinformLog(" Start nginx service...", Color.Green);
//// 启动可执行文件
//Process.Start(psi);
//if (Process.GetProcesses().Any(t => t.ProcessName.Contains("nginx") && t.MainModule.FileName.Contains(nginxStartPath)))
//{
// WinformLog(" nginx service started successfully", Color.Green);
//}
//else
//{
// WinformLog(" nginx service failed to start", Color.Red);
// return;
//}
#endregion
#endregion
});
await Task.Run(() =>
{
#region 解压后端部署的文件
WinformLog($"Start decompressing the backend deployment compressed file...", Color.Green);
string apiExtractPath = Path.Combine(deployFoder, apiExtractName);
if (!Directory.Exists(apiExtractPath))
{
Directory.CreateDirectory(apiExtractPath);
}
using (var archive = RarArchive.Open(apiRarFilePath))
{
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
entry.WriteToDirectory(apiExtractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
}
else
{
string directoryPath = Path.Combine(apiExtractPath, entry.Key);
Directory.CreateDirectory(directoryPath);
}
}
}
WinformLog($"The backend deployment compressed file was successfully extracted to: {apiExtractPath}", Color.Green);
#endregion
});
await Task.Run(() =>
{
#region 解压模板文件
WinformLog($"Start decompressing the deployment dependency template compressed file...", Color.Green);
string extractPath = Path.Combine(deployFoder, dataExtraName);
if (!Directory.Exists(extractPath))
{
Directory.CreateDirectory(extractPath);
}
using (var archive = RarArchive.Open(dataRarFilePath))
{
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
}
}
}
WinformLog($"The dependency template compressed file was successfully extracted to: {extractPath}", Color.Green);
#endregion
#region 文档等资源文件
var otherFileFolder = Path.Combine(AppContext.BaseDirectory, $@"Resource");
if (Directory.Exists(otherFileFolder))
{
// 获取源目录下所有文件
string[] files = Directory.GetFiles(otherFileFolder);
foreach (string file in files)
{
// 筛选掉文件夹
if (!File.GetAttributes(file).HasFlag(FileAttributes.Directory))
{
string extension = Path.GetExtension(file);
if (extension != ".zip" && extension != ".rar" && extension != ".7z" && extension != ".tar" && extension != ".gz")
{
// 获取文件名
string fileName = Path.GetFileName(file);
// 拷贝文件到目标目录
File.Copy(file, Path.Combine(deployFoder, fileName), true);
}
}
}
}
WinformLog($"Document resources released successfully", Color.Green);
#endregion
});
await Task.Run(() =>
{
#region 激活
try
{
string directoryPath = @"C:\ProgramData\.xingcang";
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
var configObj = new
{
key = this.machineTextBox.Text,
value = this.KeySecreteTextBox.Text.Trim(),
user = usernameTextBox.Text,
server = serverTextBox.Text,
password = passwordTextBox.Text,
dbName = dbNameTBox.Text,
deployFolder = deployFoder,
nginxStartPath = nginxStartPath,
serviceName = serviceName,
nginxServiceName = nginxServiceName,
nginxServiceEXEPath = nginxServiceEXEPath
};
File.WriteAllText(Path.Combine(directoryPath, "config.json"), JsonConvert.SerializeObject(configObj));
WinformLog("Writing the activation key is completed", Color.Green);
}
catch (Exception ex)
{
WinformLog("Failed to write the activation key. Please start the program in administrator mode to ensure that the program has sufficient permissions.", Color.Red);
}
#endregion
});
await Task.Run(async () =>
{
#region 创建服务
WinformLog($"Start creating a service {serviceName}...", Color.Green);
var createTestStr = $"sc create {serviceName} binPath= \"{apiBinPath} --urls=\"http://*:{apiPort}\" --env Test_510K\" DisplayName= \"{serviceDisplayName}\" start= auto";
var createStr = $@"sc create {serviceName} binPath= ""{apiBinPath} --urls=""http://*:{apiPort}"" --env Test_510K"" DisplayName= ""{serviceDisplayName}"" start= auto";
// 执行 sc create 命令来创建服务
await ProcessStandardInputAsync(process, createStr);
WinformLog($"The execution of creating service{serviceName}ends", Color.Green);
#endregion
#region 初始化数据库脚本
WinformLog($"Execute initialization database script...", Color.Green);
//执行数据库脚本
await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i ""{AppContext.BaseDirectory}Resource\Data\dbo.sql"" ");
await Task.Delay(3000);
await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i ""{AppContext.BaseDirectory}Resource\Data\data.sql"" ");
WinformLog($"The execution of the initialization database script ends", Color.Green);
#endregion
#region 部署网站配置文件写入
WinformLog("Start writing the deployment website service configuration file...", Color.Green);
var appsettingsJson = File.ReadAllText(apiJsonConfigPath);
// 解析 JSON 字符串
var jObject = JObject.Parse(appsettingsJson);
// 获取 UpdateConfig 属性所在的节点
var updateConfigNode = jObject["ConnectionStrings"];
var connectionString = $"Server={serverTextBox.Text};Database={dbNameTBox.Text};User ID={usernameTextBox.Text};Password={passwordTextBox.Text};TrustServerCertificate=true";
updateConfigNode["RemoteNew"] = connectionString;
try
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
File.WriteAllText(apiJsonConfigPath, jObject.ToString());
}
}
catch (SqlException ex)
{
WinformLog($"Connection string error", Color.Red);
return;
}
WinformLog("Deploy the website service configuration file, write and test OK", Color.Green);
#endregion
});
await Task.Run(async () =>
{
#region 启动后端服务
WinformLog($"Start the deployed backend service...", Color.Green);
await ProcessStandardInputAsync(process, $"sc start {serviceName}");
// 关闭进程流并等待进程退出
process.StandardInput.Close();
process.WaitForExit();
ServiceController sc = new ServiceController(serviceName);
if (sc.Status != ServiceControllerStatus.Running)
{
sc.Refresh();
await Task.Delay(3000);
sc.Refresh();
await Task.Delay(2000);
}
if (sc.Status == ServiceControllerStatus.Running)
{
WinformLog($"Backend service started successfully", Color.Green);
WinformLog($"Deployment completed", Color.Green);
try
{
WinformLog($"Now ready to open the browser...", Color.Green);
await Task.Delay(2000);
// Use ProcessStartInfo class
var start = new ProcessStartInfo($"http://127.0.0.1:{vuePort}")
{
UseShellExecute = true,
Verb = "open"
};
var urlProcess = Process.Start(start);
urlProcess.WaitForExit(); // 等待进程结束
}
catch (Exception ex)
{
// Handle exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
WinformLog($"The backend service failed to start. You can try to start {serviceDisplayName}manually. If you still cannot start after trying manually, please contact technical personnel to confirm the deployment environment.", Color.Red);
}
#endregion
await Task.Delay(10000);
Application.Exit();
});
}
private void selectPathBtn_Click(object sender, EventArgs e)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
folderPathTbox.Text = dialog.SelectedPath;
confimDeployBtn.Enabled = true;
WinformLog($"Deployment path selected successfully", Color.Green);
}
}
private void btnCopy_Click(object sender, EventArgs e)
{
Clipboard.SetText(machineTextBox.Text);
WinformLog("The machine code was copied to the clipboard successfully!", Color.Green);
}
private void Main_Load(object sender, EventArgs e)
{
Serilog.Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
// 检查是否安装了 SQL Server
bool isSqlServerInstalled = false;
RegistryKey key = null;
try
{
key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server");
if (key != null)
isSqlServerInstalled = true;
}
finally
{
key?.Close();
}
if (isSqlServerInstalled)
{
// 获取 SQL Server 实例列表
key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL");
if (key != null)
{
foreach (var instanceName in key.GetValueNames())
{
WinformLog($"The current system SQL Server installed instance: {instanceName}", Color.Green);
}
}
}
else
{
WinformLog("SQL Server is not installed, please install it before running the deployment program.", Color.Red);
}
key?.Close();
testNetcoreRuntime();
this.TopMost = true;
this.Activate();
}
}
}