Files
pengchen-exam-java-applicat…/Applications/Program.cs
2025-08-07 15:38:23 +08:00

773 lines
29 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Applications;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Win32;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Security.Principal;
using System.ServiceProcess;
using System.Text;
using System.Text.Json;
using System.Xml.Linq;
class Program
{
static ConcurrentDictionary<string, int> fileProcessMap = new(StringComparer.OrdinalIgnoreCase);
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}");
}
}
if (action == "/openjudgement")
{
string relativePath = "";
if (openType == "1")
{
string exeName = exePath;
relativePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exeName);
}
else
{
var startInfo = new ProcessStartInfo
{
FileName = exePath,
WorkingDirectory = workingDirectory, // 必须!
UseShellExecute = true // 或 false根据需要
};
// 检测一下端口是否被占用48080
if (!IsPortInUse("judgement"))
{
Process.Start(startInfo);
}
}
}
else if (action == "/start")
{
if (path == "navicat")
{
// 启动指定服务MySQL 服务)//需要用管理员身份启动
string serviceName = "wampmysqld"; // ← 请确认服务名
try
{
using (var service = new ServiceController(serviceName))
{
if (service.Status != ServiceControllerStatus.Running && service.Status != ServiceControllerStatus.StartPending)
{
Console.WriteLine($"🟡 正在启动服务:{serviceName}...");
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
Console.WriteLine($"✅ 服务 {serviceName} 启动成功");
}
else
{
Console.WriteLine($"✅ 服务 {serviceName} 已在运行中");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 启动服务失败:{ex.Message}");
}
// 获取当前用户的 SID
string userSid = WindowsIdentity.GetCurrent().User?.Value;
if (userSid == null)
{
Console.WriteLine("❌ 无法获取当前用户 SID");
return;
}
string subKeyPath = $@"{userSid}\Software\PremiumSoft\Navicat\Servers\答题专用";
Console.WriteLine("当前用户 subKeyPath" + subKeyPath);
try
{
// 打开HKEY_USERS根键
using (RegistryKey baseKey = Registry.Users)
{
// 创建或打开子项写权限需要true
using (RegistryKey key = baseKey.CreateSubKey(subKeyPath, true))
{
if (key == null)
{
Console.WriteLine("❌ 无法创建或打开注册表项");
return;
}
// 写入键值示例
key.SetValue("Host", "localhost");
key.SetValue("Port", 6033, RegistryValueKind.DWord);
key.SetValue("User", "root");
key.SetValue("Password", ""); // 注意密码如果要存可能是加密的
// 其他必要键值写入
Console.WriteLine("✅ 注册表写入成功");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 写入注册表失败:{ex.Message}");
}
// 执行数据库创建与初始化
string connectionString = "server=localhost;port=6033;user=root;password=;charset=utf8mb4;";
try
{
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
var createDbCmd = connection.CreateCommand();
createDbCmd.CommandText = $"CREATE DATABASE IF NOT EXISTS `{dbName}` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;";
createDbCmd.ExecuteNonQuery();
Console.WriteLine($"✅ 数据库 {dbName} 创建成功");
}
string sqlScript = File.ReadAllText(filepath);
using (var dbConn = new MySqlConnection(connectionString + $"database={dbName};"))
{
dbConn.Open();
var sqlStatements = SplitSqlStatements(sqlScript);
foreach (var statement in sqlStatements)
{
if (string.IsNullOrWhiteSpace(statement)) continue;
using var cmd = dbConn.CreateCommand();
cmd.CommandText = statement.Trim();
cmd.ExecuteNonQuery();
Console.WriteLine($"执行成功:{Truncate(statement, 80)}");
}
Console.WriteLine("✅ 所有 SQL 语句执行完成,数据库初始化成功");
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 出错了:{ex.Message}");
}
}
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")
{
string apiUrl = "http://" + ip + ":48080/admin-api/exam/app/getAppCheckList/" + taskId; // ← 替换成你的接口地址
List<AppCheck> softwareList = await FetchSoftwareListFromApi(apiUrl);
List<string> result = new List<string>();
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 == "/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();
}
}
}
catch
{
// 一些系统进程可能抛异常,忽略
}
}
}
else if (action == "/allapps")
{
List<string> result = new List<string>();
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<List<AppCheck>> FetchSoftwareListFromApi(string url)
{
try
{
using HttpClient client = new();
var response = await client.GetStringAsync(url);
var apiResponse = JsonSerializer.Deserialize<ApiResponse>(response);
if (apiResponse != null && apiResponse.code == 0)
{
// 如果 code 为 0说明数据有效返回 softwareList
return apiResponse.data ?? new List<AppCheck>();
}
else
{
// Console.WriteLine($"API error: {apiResponse?.msg}");
return new List<AppCheck>();
}
}
catch (Exception ex)
{
// Console.WriteLine("获取接口数据失败: " + ex.Message);
return new List<AppCheck>();
}
}
static string CreateJsonResponse<T>(int code, T data, string msg)
{
var response = new StandardResponse<T>
{
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<string> SplitSqlStatements(string sqlScript)
{
var statements = new List<string>();
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<string> SplitByDelimiter(string sql, string delimiter)
{
var list = new List<string>();
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; // 端口可用
}
/// <summary>
/// 解压ZIP文件并保留中文文件名
/// </summary>
/// <param name="zipFilePath">ZIP文件路径</param>
/// <param name="outputFolder">解压目标文件夹</param>
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<string> 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<T>
{
public int code { get; set; } // 0 表示成功,其他表示失败
public T? data { get; set; } // 实际数据内容
public string msg { get; set; } = ""; // 消息提示
}
}