first commit

This commit is contained in:
Lannamokia
2025-08-08 14:58:38 +08:00
commit 2d42b0f83a
11 changed files with 855 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
name: Build VHD Mounter
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
release:
types: [ published ]
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
continue-on-error: true
- name: Publish Windows x64
run: dotnet publish --configuration Release --runtime win-x64 --self-contained true --output ./publish/win-x64
- name: Publish Windows x86
run: dotnet publish --configuration Release --runtime win-x86 --self-contained true --output ./publish/win-x86
- name: Upload Windows x64 artifacts
uses: actions/upload-artifact@v4
with:
name: VHDMounter-win-x64
path: ./publish/win-x64/
- name: Upload Windows x86 artifacts
uses: actions/upload-artifact@v4
with:
name: VHDMounter-win-x86
path: ./publish/win-x86/
- name: Create Release Assets
if: github.event_name == 'release'
run: |
Compress-Archive -Path ./publish/win-x64/* -DestinationPath VHDMounter-win-x64.zip
Compress-Archive -Path ./publish/win-x86/* -DestinationPath VHDMounter-win-x86.zip
- name: Upload Release Assets
if: github.event_name == 'release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./VHDMounter-win-x64.zip
asset_name: VHDMounter-win-x64.zip
asset_content_type: application/zip
- name: Upload Release Assets x86
if: github.event_name == 'release'
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./VHDMounter-win-x86.zip
asset_name: VHDMounter-win-x86.zip
asset_content_type: application/zip
+78
View File
@@ -0,0 +1,78 @@
<Window x:Class="VHDMounter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="VHD Mounter"
WindowState="Maximized"
WindowStyle="None"
Topmost="True"
Background="White"
KeyDown="Window_KeyDown">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,100">
<!-- 状态显示 -->
<TextBlock x:Name="StatusText"
Text="正在初始化..."
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,0,0,30"
Foreground="Black"/>
<!-- VHD选择器 -->
<StackPanel x:Name="VHDSelector" Visibility="Collapsed">
<TextBlock Text="发现多个VHD文件,请选择一个:"
FontSize="18"
HorizontalAlignment="Center"
Margin="0,0,0,20"
Foreground="Black"/>
<ListBox x:Name="VHDListBox"
Width="600"
Height="300"
FontSize="14"
SelectionMode="Single"
Background="#F0F0F0"
BorderBrush="#CCCCCC"
BorderThickness="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Padding="10,5" Foreground="Black"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="使用上下键选择,回车确认"
FontSize="14"
HorizontalAlignment="Center"
Margin="0,10,0,0"
Foreground="Gray"/>
</StackPanel>
<!-- 进度指示器 -->
<ProgressBar x:Name="ProgressBar"
Width="400"
Height="20"
Margin="0,30,0,0"
IsIndeterminate="True"
Visibility="Visible"/>
</StackPanel>
<!-- 关闭按钮 -->
<Button x:Name="CloseButton"
Content="×"
Width="40"
Height="40"
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="0,10,10,0"
Background="Transparent"
BorderBrush="Gray"
BorderThickness="1"
Foreground="Gray"
Click="CloseButton_Click"
Cursor="Hand"/>
</Grid>
</Window>
+210
View File
@@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace VHDMounter
{
public partial class MainWindow : Window
{
private VHDManager vhdManager;
private List<string> availableVHDs;
private bool isProcessing = false;
public MainWindow()
{
InitializeComponent();
vhdManager = new VHDManager();
vhdManager.StatusChanged += OnStatusChanged;
vhdManager.VHDFilesFound += OnVHDFilesFound;
// 注册开机启动
if (!StartupManager.IsRegisteredForStartup())
{
StartupManager.RegisterForStartup();
}
// 开始主流程
_ = StartMainProcess();
}
private void OnStatusChanged(string status)
{
Dispatcher.Invoke(() =>
{
StatusText.Text = status;
});
}
private void OnVHDFilesFound(List<string> vhdFiles)
{
Dispatcher.Invoke(() =>
{
availableVHDs = vhdFiles;
ShowVHDSelector(vhdFiles);
});
}
private async Task StartMainProcess()
{
try
{
isProcessing = true;
// 扫描VHD文件
var vhdFiles = await vhdManager.ScanForVHDFiles();
if (vhdFiles.Count == 0)
{
OnStatusChanged("未找到符合条件的VHD文件");
await Task.Delay(3000);
Application.Current.Shutdown();
return;
}
string selectedVHD;
if (vhdFiles.Count == 1)
{
selectedVHD = vhdFiles[0];
await ProcessSelectedVHD(selectedVHD);
}
else
{
// 显示选择器
availableVHDs = vhdFiles;
ShowVHDSelector(vhdFiles);
}
}
catch (Exception ex)
{
OnStatusChanged($"发生错误: {ex.Message}");
await Task.Delay(5000);
Application.Current.Shutdown();
}
}
private void ShowVHDSelector(List<string> vhdFiles)
{
Dispatcher.Invoke(() =>
{
VHDListBox.ItemsSource = vhdFiles.Select(f => System.IO.Path.GetFileName(f)).ToList();
VHDListBox.SelectedIndex = 0;
VHDSelector.Visibility = Visibility.Visible;
ProgressBar.Visibility = Visibility.Collapsed;
OnStatusChanged("请选择要挂载的VHD文件");
});
}
private async Task ProcessSelectedVHD(string vhdPath)
{
try
{
Dispatcher.Invoke(() =>
{
VHDSelector.Visibility = Visibility.Collapsed;
ProgressBar.Visibility = Visibility.Visible;
});
// 挂载VHD
bool mounted = await vhdManager.MountVHD(vhdPath);
if (!mounted)
{
OnStatusChanged("VHD挂载失败");
await Task.Delay(3000);
Application.Current.Shutdown();
return;
}
// 查找package文件夹
string packagePath = await vhdManager.FindPackageFolder();
if (string.IsNullOrEmpty(packagePath))
{
OnStatusChanged("未找到package文件夹");
await Task.Delay(3000);
Application.Current.Shutdown();
return;
}
// 启动start.bat
bool started = await vhdManager.StartBatchFile(packagePath);
if (!started)
{
OnStatusChanged("启动start.bat失败");
await Task.Delay(3000);
Application.Current.Shutdown();
return;
}
OnStatusChanged("程序启动成功,开始监控...");
// 隐藏窗口并开始监控
Dispatcher.Invoke(() =>
{
this.WindowState = WindowState.Minimized;
this.ShowInTaskbar = false;
});
// 开始监控和重启循环
await vhdManager.MonitorAndRestart(packagePath);
}
catch (Exception ex)
{
OnStatusChanged($"处理过程中发生错误: {ex.Message}");
await Task.Delay(5000);
Application.Current.Shutdown();
}
}
private async void Window_KeyDown(object sender, KeyEventArgs e)
{
if (isProcessing || VHDSelector.Visibility != Visibility.Visible)
return;
switch (e.Key)
{
case Key.Up:
if (VHDListBox.SelectedIndex > 0)
VHDListBox.SelectedIndex--;
break;
case Key.Down:
if (VHDListBox.SelectedIndex < VHDListBox.Items.Count - 1)
VHDListBox.SelectedIndex++;
break;
case Key.Enter:
if (VHDListBox.SelectedIndex >= 0 && availableVHDs != null)
{
isProcessing = true;
string selectedVHD = availableVHDs[VHDListBox.SelectedIndex];
await ProcessSelectedVHD(selectedVHD);
}
break;
case Key.Escape:
Application.Current.Shutdown();
break;
}
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
protected override void OnClosed(EventArgs e)
{
// 程序关闭时卸载VHD
try
{
_ = vhdManager.UnmountDrive();
}
catch { }
base.OnClosed(e);
}
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Windows;
namespace VHDMounter
{
class Program
{
[STAThread]
static void Main(string[] args)
{
// 检查是否已有实例运行
using (var mutex = new System.Threading.Mutex(true, "VHDMounterApp", out bool createdNew))
{
if (!createdNew)
{
return; // 已有实例运行,退出
}
var app = new Application();
var mainWindow = new MainWindow();
app.Run(mainWindow);
}
}
}
}
+81
View File
@@ -0,0 +1,81 @@
# VHD Mounter - VHD自动挂载工具
## 功能描述
这是一个Windows桌面应用程序,用于自动挂载包含特定关键词的VHD文件并管理相关进程。
### 主要功能
1. **开机自启动** - 程序会自动注册到Windows开机启动项
2. **VHD文件扫描** - 自动扫描所有磁盘驱动器,查找文件名包含SDEZ/SDHD/SDDT的VHD文件
3. **智能挂载** - 将选中的VHD文件挂载为M盘
4. **用户选择界面** - 当发现多个符合条件的VHD文件时,提供全屏选择界面
5. **自动启动** - 在挂载的VHD中查找package文件夹并启动其中的start.bat
6. **进程监控** - 监控sinmai/chusanapp/mu3相关进程,如果进程停止则自动重启start.bat
7. **全屏状态显示** - 在操作过程中显示全屏白色界面,实时显示当前任务状态
## 系统要求
- Windows 10/11
- .NET 6.0 Runtime
- 管理员权限(用于VHD挂载操作)
## 编译和运行
### 编译
```bash
dotnet build --configuration Release
```
### 发布
```bash
dotnet publish --configuration Release --self-contained true --runtime win-x64
```
### 运行
程序需要以管理员身份运行才能执行VHD挂载操作。
## 使用说明
1. **首次运行**:程序会自动注册到开机启动项
2. **VHD扫描**:程序启动后会自动扫描所有驱动器查找符合条件的VHD文件
3. **文件选择**
- 如果只找到一个VHD文件,会自动挂载
- 如果找到多个VHD文件,会显示选择界面
- 使用上下键选择,回车确认
4. **自动挂载**:选中的VHD文件会被挂载为M盘
5. **启动程序**:自动查找并启动M盘中package文件夹内的start.bat
6. **后台监控**:程序会在后台持续监控目标进程,必要时重启
## 界面操作
- **上下键**:在VHD选择界面中切换选项
- **回车键**:确认选择
- **ESC键**:退出程序
- **右上角×按钮**:关闭程序
## 注意事项
1. **管理员权限**:程序必须以管理员身份运行
2. **VHD文件要求**:文件名必须包含SDEZ、SDHD或SDDT关键词
3. **目标驱动器**:VHD文件会被挂载为M盘,如果M盘已被占用会先卸载
4. **进程监控**:程序会监控包含sinmai、chusanapp或mu3关键词的进程
5. **自动重启**:如果目标进程停止运行,程序会自动重启start.bat
## 故障排除
1. **挂载失败**:确保以管理员身份运行程序
2. **找不到VHD文件**:检查文件名是否包含必要的关键词
3. **start.bat启动失败**:确保package文件夹中存在start.bat文件
4. **进程监控异常**:检查目标程序是否正常运行
## 技术实现
- **框架**.NET 6.0 + WPF
- **VHD挂载**:使用Windows diskpart命令
- **进程监控**System.Diagnostics.Process
- **开机启动**Windows注册表
- **权限管理**:应用程序清单文件
+71
View File
@@ -0,0 +1,71 @@
using Microsoft.Win32;
using System;
using System.IO;
using System.Reflection;
namespace VHDMounter
{
public class StartupManager
{
private const string APP_NAME = "VHDMounter";
private const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
public static bool IsRegisteredForStartup()
{
try
{
using (var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false))
{
return key?.GetValue(APP_NAME) != null;
}
}
catch
{
return false;
}
}
public static bool RegisterForStartup()
{
try
{
var exePath = Assembly.GetExecutingAssembly().Location;
if (exePath.EndsWith(".dll"))
{
// 如果是.dll,需要找到对应的.exe
exePath = exePath.Replace(".dll", ".exe");
}
using (var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, true))
{
key?.SetValue(APP_NAME, $"\"{exePath}\"");
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"注册开机启动失败: {ex.Message}");
return false;
}
}
public static bool UnregisterFromStartup()
{
try
{
using (var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, true))
{
key?.DeleteValue(APP_NAME, false);
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"取消开机启动失败: {ex.Message}");
return false;
}
}
}
}
+225
View File
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Threading.Tasks;
namespace VHDMounter
{
public class VHDManager
{
private const string TARGET_DRIVE = "M:";
private readonly string[] TARGET_KEYWORDS = { "SDEZ", "SDHD", "SDDT" };
private readonly string[] PROCESS_KEYWORDS = { "sinmai", "chusanapp", "mu3" };
public event Action<string> StatusChanged;
public event Action<List<string>> VHDFilesFound;
public async Task<List<string>> ScanForVHDFiles()
{
StatusChanged?.Invoke("正在扫描VHD文件...");
var vhdFiles = new List<string>();
await Task.Run(() =>
{
var drives = DriveInfo.GetDrives().Where(d => d.IsReady && d.DriveType == DriveType.Fixed);
foreach (var drive in drives)
{
try
{
var files = Directory.GetFiles(drive.RootDirectory.FullName, "*.vhd", SearchOption.AllDirectories)
.Where(f => TARGET_KEYWORDS.Any(keyword => Path.GetFileName(f).ToUpper().Contains(keyword)))
.ToList();
vhdFiles.AddRange(files);
}
catch (Exception ex)
{
// 忽略无法访问的目录
Debug.WriteLine($"扫描驱动器 {drive.Name} 时出错: {ex.Message}");
}
}
});
return vhdFiles;
}
public async Task<bool> MountVHD(string vhdPath)
{
StatusChanged?.Invoke($"正在挂载VHD文件: {Path.GetFileName(vhdPath)}");
try
{
// 先卸载M盘(如果已挂载)
await UnmountDrive();
// 使用diskpart挂载VHD
var diskpartScript = $@"select vdisk file=""{vhdPath}""
attach vdisk
assign letter=M
exit";
var tempScript = Path.GetTempFileName();
await File.WriteAllTextAsync(tempScript, diskpartScript);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "diskpart",
Arguments = $"/s \"{tempScript}\"",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
await process.WaitForExitAsync();
File.Delete(tempScript);
// 等待挂载完成
await Task.Delay(2000);
return Directory.Exists(TARGET_DRIVE);
}
catch (Exception ex)
{
StatusChanged?.Invoke($"挂载失败: {ex.Message}");
return false;
}
}
public async Task<bool> UnmountDrive()
{
try
{
if (!Directory.Exists(TARGET_DRIVE))
return true;
var diskpartScript = "select volume M\nremove\nexit";
var tempScript = Path.GetTempFileName();
await File.WriteAllTextAsync(tempScript, diskpartScript);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "diskpart",
Arguments = $"/s \"{tempScript}\"",
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
await process.WaitForExitAsync();
File.Delete(tempScript);
return true;
}
catch
{
return false;
}
}
public async Task<string> FindPackageFolder()
{
StatusChanged?.Invoke("正在搜索package文件夹...");
if (!Directory.Exists(TARGET_DRIVE))
return null;
return await Task.Run(() =>
{
try
{
var directories = Directory.GetDirectories(TARGET_DRIVE, "*", SearchOption.AllDirectories)
.Where(d => Path.GetFileName(d).ToLower() == "package")
.FirstOrDefault();
return directories;
}
catch
{
return null;
}
});
}
public async Task<bool> StartBatchFile(string packagePath)
{
var startBatPath = Path.Combine(packagePath, "start.bat");
if (!File.Exists(startBatPath))
{
StatusChanged?.Invoke("未找到start.bat文件");
return false;
}
StatusChanged?.Invoke("正在启动start.bat...");
try
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = startBatPath,
WorkingDirectory = packagePath,
UseShellExecute = true,
CreateNoWindow = false
}
};
process.Start();
return true;
}
catch (Exception ex)
{
StatusChanged?.Invoke($"启动失败: {ex.Message}");
return false;
}
}
public bool IsTargetProcessRunning()
{
try
{
var processes = Process.GetProcesses();
return processes.Any(p => PROCESS_KEYWORDS.Any(keyword =>
p.ProcessName.ToLower().Contains(keyword.ToLower())));
}
catch
{
return false;
}
}
public async Task MonitorAndRestart(string packagePath)
{
StatusChanged?.Invoke("等待15秒后开始监控进程...");
await Task.Delay(15000); // 等待15秒
StatusChanged?.Invoke("开始监控目标进程...");
while (true)
{
if (!IsTargetProcessRunning())
{
StatusChanged?.Invoke("目标进程未运行,重新启动start.bat...");
await StartBatchFile(packagePath);
await Task.Delay(15000); // 重启后等待15秒
}
await Task.Delay(1000); // 每秒检查一次
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Management" Version="7.0.2" />
</ItemGroup>
</Project>
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="VHDMounter.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
+28
View File
@@ -0,0 +1,28 @@
@echo off
echo VHD Mounter 编译脚本
echo ==================
echo 正在编译项目...
dotnet build --configuration Release
if %ERRORLEVEL% EQU 0 (
echo.
echo 编译成功!
echo.
echo 正在发布自包含版本...
dotnet publish --configuration Release --self-contained true --runtime win-x64 --output ./publish
if %ERRORLEVEL% EQU 0 (
echo.
echo 发布成功!
echo 可执行文件位置: ./publish/VHDMounter.exe
echo.
echo 注意:程序需要以管理员身份运行!
) else (
echo 发布失败!
)
) else (
echo 编译失败!
)
pause
+34
View File
@@ -0,0 +1,34 @@
@echo off
echo VHD Mounter 管理员运行脚本
echo ============================
REM 检查是否以管理员身份运行
net session >nul 2>&1
if %errorLevel% == 0 (
echo 已以管理员身份运行
echo.
) else (
echo 请求管理员权限...
powershell -Command "Start-Process '%~f0' -Verb RunAs"
exit /b
)
REM 检查可执行文件是否存在
if exist "./publish/VHDMounter.exe" (
echo 启动 VHD Mounter...
cd publish
VHDMounter.exe
) else if exist "./bin/Release/net6.0-windows/VHDMounter.exe" (
echo 启动 VHD Mounter (Debug版本)...
cd bin/Release/net6.0-windows
VHDMounter.exe
) else (
echo 错误:找不到可执行文件!
echo 请先运行 build.bat 编译项目
pause
exit /b 1
)
echo.
echo 程序已退出
pause