using Applications; using ICSharpCode.SharpZipLib.Zip; using Microsoft.Win32; using MySql.Data.MySqlClient; using System.Collections.Concurrent; using System.Diagnostics; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Security.Principal; using System.ServiceProcess; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; class Program { static ConcurrentDictionary fileProcessMap = new(StringComparer.OrdinalIgnoreCase); //[STAThread] static void Main(string[] args) { HttpListener listener = new HttpListener(); listener.Prefixes.Add("http://localhost:48081/"); listener.Start(); // Console.WriteLine("Listening on http://localhost:48081/"); while (true) { var context = listener.GetContext(); ThreadPool.QueueUserWorkItem(_ => HandleRequestAsync(context)); } } static async Task HandleRequestAsync(HttpListenerContext context) { // 添加 CORS 响应头 context.Response.AddHeader("Access-Control-Allow-Origin", "*"); context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); context.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, tenant-id"); // 处理预检请求 if (context.Request.HttpMethod == "OPTIONS") { context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.Close(); return; } string path = context.Request.QueryString["path"]; string dbName = context.Request.QueryString["dbName"]; string filepath = context.Request.QueryString["filepath"]; string fileToOpen = context.Request.QueryString["file"]; string openType = context.Request.QueryString["type"]; string exePath = context.Request.QueryString["exepath"]; string workingDirectory = context.Request.QueryString["workingdirectory"]; // 生成文件夹 string folderPath = context.Request.QueryString["folderPath"]; // 文件URL string filesurl = context.Request.QueryString["filesurl"]; // 文件名称 string desiredFileName = context.Request.QueryString["filename"]; // 需要被打包的 源目录路径 string sourceDir = context.Request.QueryString["sourceDir"]; string ip = context.Request.QueryString["ip"]; // 方案ID string taskId = context.Request.QueryString["taskId"]; string action = context.Request.Url.AbsolutePath.ToLower(); string responseMessage = ""; // 压缩文件方法 if (action == "/zipfiles") { try { if (!Directory.Exists(sourceDir)) { throw new DirectoryNotFoundException("源目录不存在!"); } // 构造ZIP文件路径(同级目录,同名.zip) string parentDir = Directory.GetParent(sourceDir).FullName; string folderName = new DirectoryInfo(sourceDir).Name; string outputZipPath = Path.Combine(parentDir, $"{folderName}.zip"); // 如果ZIP已存在,先删除(可选) if (File.Exists(outputZipPath)) { File.Delete(outputZipPath); } // 使用SharpZipLib打包 using (var zipStream = new ZipOutputStream(File.Create(outputZipPath))) { zipStream.SetLevel(5); // 压缩级别 (0-9) foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)) { string relativePath = file.Substring(sourceDir.Length + 1); var entry = new ZipEntry(relativePath) { DateTime = File.GetLastWriteTime(file), IsUnicodeText = true }; // 保留文件属性(隐藏、只读等) var fileAttributes = File.GetAttributes(file); entry.ExternalFileAttributes = (int)fileAttributes << 16; zipStream.PutNextEntry(entry); using (var fileStream = File.OpenRead(file)) { fileStream.CopyTo(zipStream); } zipStream.CloseEntry(); } } // 返回ZIP文件的绝对路径 responseMessage = Path.GetFullPath(outputZipPath); } catch (Exception ex) { throw new Exception($"压缩失败: {ex.Message}"); } } // 下载文件方法 if (action == "/downloadfiles") { try { string downloadedFilePath = await DownloadFileAsync(filesurl, folderPath); Console.WriteLine($"文件已下载到: {downloadedFilePath}"); if (IsZipFile(downloadedFilePath)) { Console.WriteLine("检测到ZIP文件,开始解压..."); string extractPath = Path.Combine(folderPath, Path.GetFileNameWithoutExtension(downloadedFilePath)); //System.IO.Compression.ZipFile.ExtractToDirectory(downloadedFilePath, folderPath); UnzipWithChineseNames(downloadedFilePath, folderPath); Console.WriteLine($"解压完成,文件保存在: {extractPath}"); // 删除原ZIP文件 File.Delete(downloadedFilePath); Console.WriteLine("已删除原ZIP文件"); } else { Console.WriteLine("文件不是ZIP格式,保留原文件"); // 获取原文件扩展名 string originalExtension = Path.GetExtension(downloadedFilePath); // 构建新文件名(保留原扩展名) string newFileName = $"{desiredFileName}{originalExtension}"; string newFilePath = Path.Combine(folderPath, newFileName); // 如果目标文件已存在,先删除 if (File.Exists(newFilePath)) { File.Delete(newFilePath); } File.Move(downloadedFilePath, newFilePath); Console.WriteLine($"文件已重命名为: {newFilePath}"); } } catch (Exception ex) { Console.WriteLine($"操作失败: {ex.Message}"); } } // 获取学生端本地的IPV4地址 if (action == "/getipv4address") { Console.WriteLine("获取IPV4"); responseMessage = Dns.GetHostEntry(Dns.GetHostName()) .AddressList .FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork) ?.ToString() ?? "未找到IPv4地址"; } // 打开本地判分的JAVA环境 if (action == "/openjudgement") { string relativePath = ""; if (openType == "1") { string exeName = exePath; relativePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName); } else { //ip = "www.hblk.top:48080"; // 如果带有端口号(即含冒号)就去掉冒号及后面内容 if (ip.Contains(":")) { ip = ip.Split(':')[0]; } Console.WriteLine(ip); string dbUser = "root"; // ✅ 你的数据库账号 string dbPass = "root"; // ✅ 你的数据库密码 var startInfo = new ProcessStartInfo { FileName = exePath, WorkingDirectory = workingDirectory, // 必须! Arguments = $"-jar \"{exePath}\" " + $"--spring.datasource.url=\"jdbc:mysql://{ip}:3306/pc-exam?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true\" " + $"--spring.datasource.username={dbUser} " + $"--spring.datasource.password={dbPass}", UseShellExecute = true, // 或 false,根据需要 Verb = "runas" // 指定管理员运行 }; // 检测一下端口是否被占用48082 if (!IsPortInUse("judgement")) { Process.Start(startInfo); } } } else if (action == "/start") { if (path == "navicat") { int port = 48086; // 1. 自动搜索 MySQL bin 目录 string[] possiblePaths = new string[] { @"C:\Program Files\MySQL\MySQL Server 5.5\bin", @"C:\Program Files\MySQL\MySQL Server 5.6\bin", @"C:\Program Files\MySQL\MySQL Server 5.7\bin", @"C:\Program Files\MySQL\MySQL Server 8.0\bin", @"C:\Program Files\MySQL\MySQL Server 8.1\bin", @"C:\PROGRA~1\MySQL\MYSQLS~1.0\bin" }; string mysqlBinDir = null; foreach (var patha in possiblePaths) { if (File.Exists(Path.Combine(patha, "mysqld.exe"))) { mysqlBinDir = patha; break; } } if (mysqlBinDir == null) { mysqlBinDir = FindMySqlBin(); if (mysqlBinDir == null) { return; } } // 2. 设置临时数据目录 string tempData = @"D:\mysql_temp_data"; string tempIni = @"D:\mysql_temp.ini"; int existingPid = GetProcessIdByPort(port); if (existingPid > 0) { try { using var conn = new MySqlConnection($"server=127.0.0.1;port={port};user=root;password=;Charset=utf8mb4;"); conn.Open(); } catch { } } else { try { if (Directory.Exists(tempData)) Directory.Delete(tempData, true); Directory.CreateDirectory(tempData); // 如果已有 ini 文件,先删掉 if (File.Exists(tempIni)) { File.Delete(tempIni); } // 3. 生成临时 my.ini using (StreamWriter writer = new StreamWriter(tempIni)) { writer.WriteLine("[mysqld]"); writer.WriteLine($"datadir={tempData}"); writer.WriteLine($"port={port}"); writer.WriteLine("skip-networking=0"); writer.WriteLine("skip-grant-tables=0"); writer.WriteLine($"log-error={tempData}\\mysql.err"); } } catch (Exception ex) { } // 4. 初始化临时数据库 if (!RunProcess(Path.Combine(mysqlBinDir, "mysqld.exe"), $"--defaults-file=\"{tempIni}\" --initialize-insecure")) { return; } // 5. 启动临时 MySQL(WinExe 不重定向输出) Process mysqlProcess = new Process(); mysqlProcess.StartInfo.FileName = Path.Combine(mysqlBinDir, "mysqld.exe"); mysqlProcess.StartInfo.Arguments = $"--defaults-file=\"{tempIni}\" --standalone --port={port}"; // 必须 false 才能重定向输出和隐藏窗口 mysqlProcess.StartInfo.UseShellExecute = false; mysqlProcess.StartInfo.CreateNoWindow = true; // 重定向输出 mysqlProcess.StartInfo.RedirectStandardOutput = true; mysqlProcess.StartInfo.RedirectStandardError = true; // 捕获输出 mysqlProcess.OutputDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine("[MySQL] " + e.Data); }; mysqlProcess.ErrorDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine("[MySQL-ERR] " + e.Data); }; mysqlProcess.Start(); // 开始异步读取输出 mysqlProcess.BeginOutputReadLine(); mysqlProcess.BeginErrorReadLine(); } // 6. 等待 MySQL 可连接(最长等待 30 秒) string connStr = $"server=127.0.0.1;port={port};user=root;password=;Charset=utf8mb4;"; var sw = System.Diagnostics.Stopwatch.StartNew(); bool started = false; while (sw.ElapsedMilliseconds < 30000) { try { using var conn = new MySql.Data.MySqlClient.MySqlConnection(connStr); conn.Open(); started = true; break; } catch { System.Threading.Thread.Sleep(500); } } if (!started) { return; } // 7 写入 Navicat 注册表 try { string userSid = WindowsIdentity.GetCurrent().User?.Value; if (userSid != null) { string subKeyPath = $@"{userSid}\Software\PremiumSoft\Navicat\Servers\答题专用"; using (var baseKey = Registry.Users) using (var key = baseKey.CreateSubKey(subKeyPath, true)) { key.SetValue("Host", "127.0.0.1"); key.SetValue("Port", port, RegistryValueKind.DWord); key.SetValue("User", "root"); key.SetValue("Password", ""); } } } catch (Exception ex) { } // 8 创建数据库 + 执行 SQL try { using (var conn = new MySqlConnection(connStr)) { conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = $"CREATE DATABASE IF NOT EXISTS `{dbName}` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"; cmd.ExecuteNonQuery(); } string sqlScript = File.ReadAllText(filepath); using (var dbConn = new MySqlConnection(connStr + $"database={dbName};")) { dbConn.Open(); var sqlStatements = SplitSqlStatements(sqlScript); foreach (var stmt in sqlStatements) { if (string.IsNullOrWhiteSpace(stmt)) continue; using var cmd = dbConn.CreateCommand(); cmd.CommandText = stmt.Trim(); cmd.ExecuteNonQuery(); } } } catch (Exception ex) { } } try { string? resolvedPath = ResolveAppPath(path); if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath)) { ProcessStartInfo psi = new ProcessStartInfo { FileName = resolvedPath, Arguments = string.IsNullOrEmpty(fileToOpen) ? "" : $"\"{fileToOpen}\"", UseShellExecute = true }; var process = Process.Start(psi); if (process != null) { if (!string.IsNullOrEmpty(fileToOpen)) { fileProcessMap[fileToOpen] = process.Id; responseMessage = $"Started {resolvedPath} with {fileToOpen}"; } else { responseMessage = $"Started {resolvedPath} (no file)"; } } else { responseMessage = "Failed to start process."; } } else if (!string.IsNullOrEmpty(fileToOpen) && File.Exists(fileToOpen)) { // 使用默认程序打开文件 ProcessStartInfo psi = new ProcessStartInfo { FileName = fileToOpen, UseShellExecute = true }; var process = Process.Start(psi); if (process != null) { fileProcessMap[fileToOpen] = process.Id; responseMessage = $"Opened {fileToOpen} with default program (PID: {process.Id})"; } else { responseMessage = $"Failed to open {fileToOpen}."; } } else { responseMessage = "Missing or invalid 'path' or 'file'."; } } catch (Exception ex) { responseMessage = "Error: " + ex.Message; } } // 停止软件 else if (action == "/stop") { if (!string.IsNullOrEmpty(fileToOpen)) { if (fileProcessMap.TryRemove(fileToOpen, out int pid)) { try { var proc = Process.GetProcessById(pid); proc.Kill(); responseMessage = $"Stopped process for file: {fileToOpen} (PID: {pid})"; } catch (Exception ex) { responseMessage = $"Failed to stop process: {ex.Message}"; } } else { responseMessage = $"No tracked process for file: {fileToOpen}"; } } else { responseMessage = "Missing 'file' parameter."; } } // 检测学生端环境 else if (action == "/check") { Console.WriteLine("check"); string apiUrl; if (ip.Contains(":")) { // 已包含端口号或域名带端口 apiUrl = $"http://{ip}/admin-api/exam/app/getAppCheckList/{taskId}"; } else { // 未包含端口号 apiUrl = $"http://{ip}:48080/admin-api/exam/app/getAppCheckList/{taskId}"; } List softwareList = await FetchSoftwareListFromApi(apiUrl); List result = new List(); foreach (AppCheck softwareLine in softwareList) { var keywords = softwareLine.appName; string? resolvedPath = ResolveAppPath(keywords); if (!string.IsNullOrEmpty(resolvedPath) && File.Exists(resolvedPath)) { result.Add(keywords + ":True"); } else { result.Add(keywords + ":False"); } } // 将 result 转换为 JSON 并返回 responseMessage = JsonSerializer.Serialize(result); } // 检测学生端环境 else if (action == "/white") { Console.WriteLine("check111"); string apiUrl; if (ip.Contains(":")) { // 已包含端口号或域名带端口 apiUrl = $"http://{ip}/admin-api/exam/param/getAppWhiteList/{taskId}"; } else { // 未包含端口号 apiUrl = $"http://{ip}:48080/admin-api/exam/param/getAppWhiteList/{taskId}"; } Console.WriteLine(apiUrl); List softwareList = await FetchSoftwareWhiteListFromApi(apiUrl); List result = new List(); // 将 result 转换为 JSON 并返回 responseMessage = JsonSerializer.Serialize(softwareList); } else if (action == "/closeapps") { foreach (var proc in Process.GetProcesses()) { try { // 主窗口句柄不为 0,表示是一个窗口程序(用户级应用) if (proc.MainWindowHandle != IntPtr.Zero && proc.SessionId != 0 && proc.Responding) { Console.WriteLine($"App: {proc.ProcessName} (PID: {proc.Id}) - Title: {proc.MainWindowTitle}"); if (!proc.MainWindowTitle.Contains("ExamStudent") && !proc.MainWindowTitle.Contains("Applications")) { var procs = Process.GetProcessById(proc.Id); procs.Kill(); } } // 关闭临时 MySQL(只关掉端口 48086) // if (proc.ProcessName.Equals("mysqld", StringComparison.OrdinalIgnoreCase)) // { // string cmdLine = GetCommandLine(proc); // if (!string.IsNullOrEmpty(cmdLine) && cmdLine.Contains("--port=48086")) // { // Console.WriteLine($"Closing temporary MySQL (PID: {proc.Id}) on port 48086"); // proc.Kill(); // } // } } catch { // 一些系统进程可能抛异常,忽略 } } } else if (action == "/allapps") { List result = new List(); foreach (var proc in Process.GetProcesses()) { try { // 主窗口句柄不为 0,表示是一个窗口程序(用户级应用) if (proc.MainWindowHandle != IntPtr.Zero && proc.SessionId != 0 && proc.Responding) { Console.WriteLine($"App: {proc.ProcessName} (PID: {proc.Id}) - Title: {proc.MainWindowTitle}"); result.Add(proc.ProcessName); } } catch { // 一些系统进程可能抛异常,忽略 } } responseMessage = JsonSerializer.Serialize(result); } try { context.Response.ContentType = "text/plain"; context.Response.ContentEncoding = System.Text.Encoding.UTF8; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(CreateJsonResponse(200, responseMessage, "") ?? "No response"); context.Response.ContentLength64 = buffer.Length; await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length); } catch (Exception ex) { Console.WriteLine("Response write error: " + ex.Message); } finally { context.Response.OutputStream.Close(); } } static string? ResolveAppPath(string appName) { var fromReg = FindInRegistry(appName); if (!string.IsNullOrEmpty(fromReg)) return fromReg; return null; } static string? FindInRegistry(string exeName) { using var regKey = Registry.ClassesRoot.OpenSubKey("Applications"); if (regKey != null) { // 获取所有子项 foreach (var subKeyName in regKey.GetSubKeyNames()) { // 打开每个应用程序子键 using var appKey = regKey.OpenSubKey(subKeyName); if (appKey != null) { // 查看是否有 "shell\open\command" 子键,这里存储着启动路径 using var commandKey = appKey.OpenSubKey(@"shell\open\command"); if (commandKey != null) { var commandValue = commandKey.GetValue("") as string; if (commandValue != null) { if (subKeyName.IndexOf(exeName, StringComparison.OrdinalIgnoreCase) >= 0) { return commandValue.Split('"')[1]; } } } } } } return null; } static async Task> FetchSoftwareListFromApi(string url) { try { using HttpClient client = new(); var response = await client.GetStringAsync(url); var apiResponse = JsonSerializer.Deserialize(response); if (apiResponse != null && apiResponse.code == 0) { // 如果 code 为 0,说明数据有效,返回 softwareList return apiResponse.data ?? new List(); } else { // Console.WriteLine($"API error: {apiResponse?.msg}"); return new List(); } } catch (Exception ex) { // Console.WriteLine("获取接口数据失败: " + ex.Message); return new List(); } } static async Task> FetchSoftwareWhiteListFromApi(string url) { try { using HttpClient client = new(); var response = await client.GetStringAsync(url); var apiResponse = JsonSerializer.Deserialize(response); if (apiResponse != null && apiResponse.code == 0) { // 如果 code 为 0,说明数据有效,返回 softwareList return apiResponse.data ?? new List(); } else { // Console.WriteLine($"API error: {apiResponse?.msg}"); return new List(); } } catch (Exception ex) { // Console.WriteLine("获取接口数据失败: " + ex.Message); return new List(); } } static string CreateJsonResponse(int code, T data, string msg) { var response = new StandardResponse { code = code, data = data, msg = msg }; return JsonSerializer.Serialize(response); } static string Truncate(string value, int maxLength) { if (string.IsNullOrEmpty(value)) return value; return value.Length <= maxLength ? value : value.Substring(0, maxLength) + "..."; } static List SplitSqlStatements(string sqlScript) { var statements = new List(); string delimiter = ";"; int pos = 0, lastPos = 0, length = sqlScript.Length; while (pos < length) { if (MatchKeyword(sqlScript, pos, "DELIMITER")) { string prevSegment = sqlScript.Substring(lastPos, pos - lastPos); var prevStatements = SplitByDelimiter(prevSegment, delimiter); statements.AddRange(prevStatements); int delimStart = pos + "DELIMITER".Length; while (delimStart < length && char.IsWhiteSpace(sqlScript[delimStart])) delimStart++; int delimEnd = delimStart; while (delimEnd < length && !char.IsWhiteSpace(sqlScript[delimEnd]) && sqlScript[delimEnd] != '\r' && sqlScript[delimEnd] != '\n') delimEnd++; delimiter = sqlScript.Substring(delimStart, delimEnd - delimStart); pos = delimEnd; while (pos < length && sqlScript[pos] != '\n') pos++; pos++; lastPos = pos; } else { pos++; } } if (lastPos < length) { string lastSegment = sqlScript.Substring(lastPos); var lastStatements = SplitByDelimiter(lastSegment, delimiter); statements.AddRange(lastStatements); } return statements; } static bool MatchKeyword(string text, int pos, string keyword) { if (pos + keyword.Length > text.Length) return false; var segment = text.Substring(pos, keyword.Length); return string.Equals(segment, keyword, StringComparison.OrdinalIgnoreCase); } static List SplitByDelimiter(string sql, string delimiter) { var list = new List(); int start = 0, idx; while ((idx = sql.IndexOf(delimiter, start, StringComparison.Ordinal)) >= 0) { list.Add(sql.Substring(start, idx - start)); start = idx + delimiter.Length; } if (start < sql.Length) list.Add(sql.Substring(start)); return list; } public static bool IsPortInUse(string name) { foreach (var proc in Process.GetProcesses()) { try { // 主窗口句柄不为 0,表示是一个窗口程序(用户级应用) if (proc.SessionId != 0 && proc.Responding) { if (proc.ProcessName.Contains(name)) { var procs = Process.GetProcessById(proc.Id); return true; } } } catch { // 一些系统进程可能抛异常,忽略 } } return false; // 端口可用 } /// /// 解压ZIP文件并保留中文文件名 /// /// ZIP文件路径 /// 解压目标文件夹 public static void UnzipWithChineseNames(string zipFilePath, string outputFolder) { // ✅ 注册编码提供程序(仅.NET Core / .NET 5 + 需要) Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // ✅ 设置 GBK 编码(或 UTF-8) ZipStrings.CodePage = Encoding.GetEncoding("GBK").CodePage; // 或 936 / UTF8 if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); using (var fs = File.OpenRead(zipFilePath)) using (var zipInputStream = new ZipInputStream(fs)) { ZipEntry entry; while ((entry = zipInputStream.GetNextEntry()) != null) { // 处理路径(统一替换路径分隔符为当前系统的分隔符) string entryName = entry.Name.Replace('/', Path.DirectorySeparatorChar); string fullPath = Path.Combine(outputFolder, entryName); if (entry.IsDirectory) { // 如果是目录,确保创建该目录 if (!Directory.Exists(fullPath)) { Directory.CreateDirectory(fullPath); // 设置目录的修改时间(可选) Directory.SetLastWriteTime(fullPath, entry.DateTime); } } else { // 如果是文件,确保父目录存在 string directoryName = Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } // 写入文件内容 using (var streamWriter = File.Create(fullPath)) { byte[] buffer = new byte[4096]; int size; while ((size = zipInputStream.Read(buffer, 0, buffer.Length)) > 0) { streamWriter.Write(buffer, 0, size); } } // 设置文件的修改时间 File.SetLastWriteTime(fullPath, entry.DateTime); } } } } static async Task DownloadFileAsync(string url, string saveDir) { using (var httpClient = new HttpClient()) { // 确保目录存在 Directory.CreateDirectory(saveDir); // 从URL获取文件名 Uri uri = new Uri(url); string fileName = Path.GetFileName(uri.LocalPath); if (string.IsNullOrEmpty(fileName)) { fileName = $"downloaded_{DateTime.Now:yyyyMMddHHmmss}"; } string fullPath = Path.Combine(saveDir, fileName); // 下载文件 var response = await httpClient.GetAsync(url); response.EnsureSuccessStatusCode(); using (var fileStream = File.Create(fullPath)) { await response.Content.CopyToAsync(fileStream); } return fullPath; } } // 检查文件是否是ZIP格式 static bool IsZipFile(string filePath) { try { if (filePath.Contains("zip")) { return true; } else { return false; } } catch { return false; } } // 获取当前登录用户 SID static string GetCurrentUserSid() { var sid = System.Security.Principal.WindowsIdentity.GetCurrent().User?.ToString(); if (sid == null) throw new Exception("无法获取当前用户 SID"); return $"HKEY_USERS\\{sid}"; } class StandardResponse { public int code { get; set; } // 0 表示成功,其他表示失败 public T? data { get; set; } // 实际数据内容 public string msg { get; set; } = ""; // 消息提示 } // 等待 MySQL 可连接 public static bool WaitForMySql(string connStr, int timeoutSeconds) { var start = DateTime.Now; while ((DateTime.Now - start).TotalSeconds < timeoutSeconds) { try { using var conn = new MySqlConnection(connStr); conn.Open(); return true; } catch { Thread.Sleep(1000); } } return false; } static bool RunProcess(string fileName, string arguments, string logFile = null) { try { Process proc = new Process(); proc.StartInfo.FileName = fileName; proc.StartInfo.Arguments = arguments; proc.StartInfo.UseShellExecute = false; proc.StartInfo.RedirectStandardOutput = true; proc.StartInfo.RedirectStandardError = true; using (StreamWriter sw = logFile != null ? new StreamWriter(logFile, true) : null) { proc.OutputDataReceived += (s, e) => { if (!string.IsNullOrEmpty(e.Data) && sw != null) sw.WriteLine("[OUT] " + e.Data); }; proc.ErrorDataReceived += (s, e) => { if (!string.IsNullOrEmpty(e.Data) && sw != null) sw.WriteLine("[ERR] " + e.Data); }; proc.Start(); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); return proc.ExitCode == 0; } } catch (Exception ex) { if (logFile != null) File.AppendAllText(logFile, "RunProcess Exception: " + ex + Environment.NewLine); return false; } } // 以管理员身份结束进程 static void KillProcessByPid(int pid) { try { Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.Arguments = $"/c taskkill /PID {pid} /F"; p.StartInfo.Verb = "runas"; // 请求管理员权限 p.StartInfo.UseShellExecute = true; p.Start(); p.WaitForExit(); } catch (Exception ex) { } } static int GetProcessIdByPort(int port) { try { // 调用 netstat 命令获取端口占用 Process netstat = new Process(); netstat.StartInfo.FileName = "netstat.exe"; netstat.StartInfo.Arguments = "-aon"; netstat.StartInfo.UseShellExecute = false; netstat.StartInfo.RedirectStandardOutput = true; netstat.StartInfo.CreateNoWindow = true; netstat.Start(); string output = netstat.StandardOutput.ReadToEnd(); netstat.WaitForExit(); // 正则匹配端口对应 PID string pattern = $@"\s+TCP\s+\S+:{port}\s+\S+\s+LISTENING\s+(\d+)"; var match = Regex.Match(output, pattern); if (match.Success && int.TryParse(match.Groups[1].Value, out int pid)) { return pid; } } catch { // 出错返回 0 } return 0; // 未找到占用 } static string FindMySqlBin() { string[] roots = new string[] { @"C:\Program Files\MySQL", @"C:\Program Files (x86)\MySQL", @"D:\Program Files\MySQL", @"D:\Program Files (x86)\MySQL", @"D:\MySQL" }; foreach (var root in roots) { if (!Directory.Exists(root)) continue; try { var dirs = Directory.GetDirectories(root, "*", SearchOption.AllDirectories); foreach (var dir in dirs) { string mysqldPath = Path.Combine(dir, "mysqld.exe"); if (File.Exists(mysqldPath)) { return dir; // 找到 mysqld.exe 返回目录 } } } catch { // 访问被拒绝等异常直接忽略 continue; } } return null; // 没找到 } // 获取进程命令行,需要添加 System.Management 引用 //public static string GetCommandLine(Process process) //{ // try // { // using (var searcher = new System.Management.ManagementObjectSearcher( // "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) // { // foreach (var obj in searcher.Get()) // { // return obj["CommandLine"]?.ToString(); // } // } // } // catch // { // return null; // } // return null; //} }