Files
AohDrllTools/MainWindow.xaml.cs
Mr.Xia b75439cff4 fix: 修复含特殊字符路径的钻带文件读取失败 — 移除 cmd /c type,改为 AnsiFileReader 直接文件IO
问题:路径含 ( ) & % ! ^ @ 等字符时报"文件路径包含不允许的命令行特殊字符",
(D2-f)、(1) 这类正常业务文件名直接命中。

根因:读取统一入口把路径拼进命令行(cmd.exe /d /c type "..."),为"防炸"在 03d9b8b
引入 ForbiddenPathChars 黑名单——并非业务需求,是实现的自我约束。
实测:真正会坏的只有 %VAR%(必坏) 与延迟展开开启时的 !VAR!,而 ( ) & ^ @ 空格在
双引号内均正常;且 type 是字节透传,这层 cmd 对当初的编码问题毫无帮助。

改动:
- 新增 AnsiFileReader.cs:FileStream + StreamReader(默认 936 解码 + BOM 探测,
  与旧 StreamReader 解码路径逐字符等价),不经命令行
- 删除 CommandTypeFileReader.cs 及其 ForbiddenPathChars 字符校验
- 路径只保留"非空"与"文件存在"校验;IO 异常包装为
  InvalidOperationException("读取文件失败:{路径}({原因})")并保留 InnerException
- 15 处调用点同步改名;更新 CLAUDE.md 与 Docs 说明

验证:14 个 demo_drl 样例新旧实现字符串/行数逐字符一致;含特殊字符路径(含 %TEMP% 字面
路径,旧实现 rc=1)可读可解析;UTF-8/UTF-16 BOM 结果与旧实现一致;
dotnet build -p:Platform=x86 0 错误且警告集与基线逐条相同。
2026-09-20 11:30:04 +08:00

353 lines
14 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 System;
using System.Windows;
using System.Windows.Controls;
namespace DrillTools
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;
public MainWindow(string? initialFilePath = null)
{
//System.Diagnostics.Debug.WriteLine("=== MainWindow构造函数开始执行 ===");
//System.Diagnostics.Debug.WriteLine($"参数 initialFilePath: {initialFilePath ?? "null"}");
try
{
//System.Diagnostics.Debug.WriteLine("调用 InitializeComponent()...");
InitializeComponent();
//System.Diagnostics.Debug.WriteLine("InitializeComponent() 执行完成");
//System.Diagnostics.Debug.WriteLine("创建 MainWindowViewModel...");
var viewModel = new MainWindowViewModel();
//System.Diagnostics.Debug.WriteLine("MainWindowViewModel 创建完成");
viewModel.IsStartupDrillTapeFile = !string.IsNullOrEmpty(initialFilePath);
//System.Diagnostics.Debug.WriteLine("设置 DataContext...");
DataContext = viewModel;
//System.Diagnostics.Debug.WriteLine("DataContext 设置完成");
//System.Diagnostics.Debug.WriteLine("初始化拖放功能...");
InitializeDragDrop();
//System.Diagnostics.Debug.WriteLine("拖放功能初始化完成");
// 如果有初始文件路径,在窗口加载完成后自动加载
if (!string.IsNullOrEmpty(initialFilePath))
{
//System.Diagnostics.Debug.WriteLine("注册文件加载事件...");
this.Loaded += (sender, e) => LoadInitialFile(initialFilePath, viewModel);
//System.Diagnostics.Debug.WriteLine("文件加载事件注册完成");
}
//System.Diagnostics.Debug.WriteLine("=== MainWindow构造函数执行完成 ===");
}
catch (Exception ex)
{
//System.Diagnostics.Debug.WriteLine($"MainWindow构造函数中发生异常: {ex.GetType().Name} - {ex.Message}");
//System.Diagnostics.Debug.WriteLine($"异常堆栈: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 加载初始文件
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="viewModel">视图模型</param>
private async void LoadInitialFile(string filePath, MainWindowViewModel viewModel)
{
try
{
// 异步加载文件以避免阻塞UI
await System.Threading.Tasks.Task.Run(() =>
{
string drillTapeContent = AnsiFileReader.ReadAllText(filePath);
// 在UI线程中更新界面
this.Dispatcher.Invoke(() =>
{
try
{
// 保存原始文件路径
viewModel.IsStartupDrillTapeFile = true;
viewModel.OriginalFilePath = filePath;
// 加载钻带内容
viewModel.LoadToolsFromDrillTape(drillTapeContent);
// 显示成功提示
//System.Windows.MessageBox.Show(
// $"已成功加载钻带文件:\n{System.IO.Path.GetFileName(filePath)}\n\n刀具数量:{viewModel.Tools.Count}把",
// "文件加载完成",
// System.Windows.MessageBoxButton.OK,
// System.Windows.MessageBoxImage.Information);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(
$"加载钻带文件失败:\n{ex.Message}",
"错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
});
});
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(
$"读取文件时发生错误:\n{ex.Message}",
"错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
/// <summary>
/// 初始化拖放功能
/// </summary>
private void InitializeDragDrop()
{
DragDropHelper.EnableDragDrop<ToolItem>(ToolsListView, InsertionIndicator);
}
/// <summary>
/// 加载示例数据按钮点击事件
/// </summary>
private void LoadSampleDataButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.LoadSampleData();
}
/// <summary>
/// 加载钻带文件按钮点击事件
/// </summary>
private void LoadDrillTapeButton_Click(object sender, RoutedEventArgs e)
{
var openFileDialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "钻带文件 (*.txt;*.drl;*.dr2;*.dpin)|*.txt;*.drl;*.dr2;*.dpin|所有文件 (*.*)|*.*",
Title = "选择钻带文件"
};
if (openFileDialog.ShowDialog() == true)
{
try
{
// 保存原始文件路径
ViewModel.IsStartupDrillTapeFile = false;
ViewModel.OriginalFilePath = openFileDialog.FileName;
string drillTapeContent = AnsiFileReader.ReadAllText(openFileDialog.FileName);
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"加载钻带文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 重排刀序按钮点击事件
/// </summary>
private void ReorderToolsButton_Click(object sender, RoutedEventArgs e)
{
try
{
string reorderedDrillTape = ViewModel.ReorderAndRenumberTools();
ViewModel.DrillTapeContent = reorderedDrillTape;
System.Windows.MessageBox.Show("刀序重排完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (OperationCanceledException)
{
// 用户取消操作,不显示错误消息
//System.Diagnostics.Debug.WriteLine("用户取消了刀序重排操作");
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"重排刀序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 应用顺序到钻带按钮点击事件
/// </summary>
private void ApplyOrderButton_Click(object sender, RoutedEventArgs e)
{
try
{
string reorderedDrillTape = ViewModel.ReorderAndRenumberTools(true);
ViewModel.DrillTapeContent = reorderedDrillTape;
System.Windows.MessageBox.Show("刀具顺序已应用到钻带", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (OperationCanceledException)
{
// 用户取消操作,不显示错误消息
//System.Diagnostics.Debug.WriteLine("用户取消了保存操作");
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"应用刀具顺序失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 导出孔数报表按钮点击事件
/// </summary>
private void ExportDrillUsageReportButton_Click(object sender, RoutedEventArgs e)
{
try
{
ViewModel.ExportDrillUsageReport();
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"导出孔数报表失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 生成 PP 钻带按钮点击事件
/// </summary>
private void GeneratePpDrillTapeButton_Click(object sender, RoutedEventArgs e)
{
try
{
string outputFilePath = ViewModel.GeneratePpDrillTape();
System.Windows.MessageBox.Show($"PP钻带已生成:\n{outputFilePath}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"生成PP钻带失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 生成排序种子按钮点击事件
/// </summary>
private void GenerateSortSeedButton_Click(object sender, RoutedEventArgs e)
{
try
{
ViewModel.GenerateGeneralSortSeedFile(ViewModel.Tools);
System.Windows.MessageBox.Show("通用排序种子文件已生成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (OperationCanceledException)
{
// 用户取消操作,不显示错误消息
//System.Diagnostics.Debug.WriteLine("用户取消了生成排序种子操作");
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"生成排序种子失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 使用指定钻带的刀序按钮点击事件
/// </summary>
private void UseReferenceDrillTapeButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.ReorderToolsByReferenceDrillTape();
}
/// <summary>
/// 使用指定种子的刀序按钮点击事件
/// </summary>
private void UseSortSeedButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.ReorderToolsBySortSeedFile();
}
/// <summary>
/// 测试参考钻带功能按钮点击事件
/// </summary>
private void TestReferenceDrillTapeButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.TestReorderByReferenceDrillTape();
}
/// <summary>
/// 上移按钮点击事件
/// </summary>
private void MoveUpButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.MoveSelectedToolUp();
}
/// <summary>
/// 下移按钮点击事件
/// </summary>
private void MoveDownButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.MoveSelectedToolDown();
}
/// <summary>
/// 刀具列表双击事件
/// </summary>
private void ToolsListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is System.Windows.Controls.ListView listView && listView.SelectedItem is ToolItem selectedTool)
{
ViewModel.ShowToolDetail(selectedTool);
}
}
/// <summary>
/// 拖放文件进入窗口事件
/// </summary>
protected override void OnDragEnter(System.Windows.DragEventArgs e)
{
if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop))
{
e.Effects = System.Windows.DragDropEffects.Copy;
}
else
{
e.Effects = System.Windows.DragDropEffects.None;
}
base.OnDragEnter(e);
}
/// <summary>
/// 拖放文件到窗口事件
/// </summary>
protected override void OnDrop(System.Windows.DragEventArgs e)
{
if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(System.Windows.DataFormats.FileDrop);
if (files.Length > 0)
{
try
{
// 保存原始文件路径
ViewModel.IsStartupDrillTapeFile = false;
ViewModel.OriginalFilePath = files[0];
string drillTapeContent = AnsiFileReader.ReadAllText(files[0]);
ViewModel.LoadToolsFromDrillTape(drillTapeContent);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"加载钻带文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
base.OnDrop(e);
}
}
}