gpt4 book ai didi

c# - WPF App.OnStartup() 在写入文件和 FileWatcher 时崩溃

转载 作者:可可西里 更新时间:2023-11-01 13:27:33 26 4
gpt4 key购买 nike

在使用 WPF 应用程序时,我遇到了一个非常疯狂的问题,该应用程序使用 Singleton 实例模式来确保只有一个实例在运行。单个实例检测和命令行转发机制工作正常,但是,作为在辅助实例上退出的启动代码的一部分,将文件写入磁盘,主应用程序通过 FileWatcher 获取该文件。辅助实例经常严重崩溃并出现内核级错误。

检查辅助实例并随机崩溃的启动代码是这样做的:

    protected override void OnStartup(StartupEventArgs e)
{
bool isOnlyInstance = false;
Mutex = new Mutex(true, @"MarkdownMonster", out isOnlyInstance);
if (!isOnlyInstance)
{
filesToOpen = " ";
var args = Environment.GetCommandLineArgs();
if (args != null && args.Length > 1)
{
StringBuilder sb = new StringBuilder();
for (int i = 1; i < args.Length; i++)
{
sb.AppendLine(args[i]);
}
filesToOpen = sb.ToString();
}

File.WriteAllText(mmApp.Configuration.FileWatcherOpenFilePath, filesToOpen);

Mutex.Dispose();

// This blows up when writing files and file watcher watching
// No idea why - Environment.Exit() works with no issue
ShutdownMode = ShutdownMode.OnMainWindowClose;
App.Current.Shutdown();

return;
}

// ...
}

检查写入文件的代码加载到主窗体的构造函数中:

           openFileWatcher = new FileSystemWatcher(
Path.GetDirectoryName(mmApp.Configuration.FileWatcherOpenFilePath),
Path.GetFileName(mmApp.Configuration.FileWatcherOpenFilePath))
{
NotifyFilter = NotifyFilters.LastWrite,
EnableRaisingEvents = true
};
openFileWatcher.Changed += openFileWatcher_Changed;
openFileWatcher.Created += openFileWatcher_Changed;

然后处理程序会像这样检查文件:

    private void openFileWatcher_Changed(object sender, FileSystemEventArgs e)
{
string filesToOpen = null;

// due to write timing we may have to try a few times
for (int i = 0; i < 100; i++)
{
try
{
if (File.Exists(mmApp.Configuration.FileWatcherOpenFilePath))
{
filesToOpen = File.ReadAllText(mmApp.Configuration.FileWatcherOpenFilePath);
File.Delete(mmApp.Configuration.FileWatcherOpenFilePath);
filesToOpen = filesToOpen.TrimEnd();
}
break;
}
catch
{
Thread.Sleep(10);
}
}

Dispatcher.Invoke(() =>
{

if (!string.IsNullOrEmpty(filesToOpen))
{
foreach (var file in StringUtils.GetLines(filesToOpen))
{
MessageBox.Show(file);
this.OpenTab(file.Trim());
}
}

if (WindowState == WindowState.Minimized)
WindowState = WindowState.Normal;

this.Activate();
});
}

所有这一切的逻辑工作正常。应用程序正确检测到始终写出文件的辅助实例,第一个实例拾取文件并激活/加载命令行中指定的文件。

但是,辅助实例崩溃并出现无法捕获的错误(AppDomain.UnhandledException 事件已 Hook 但未触发),会弹出一个 Windows 错误对话框到桌面。

次级加载在启动时大约有 80% 的时间崩溃 - 这不一致,但频率更高。

如果我删除 File.WriteAllText() 代码,则不会发生崩溃。如果我删除 FileWatcher 代码,则不会发生崩溃。如果两者都处于事件状态:Boom。 IOW,文件写入和 FileWatcher 都需要发生才能崩溃——如果一个不活动,则不会发生崩溃。我试过用 try/catch 包装 File.WriteAllText() 调用,但它没有被触发。代码退出我的用户函数后发生故障,我似乎无法控制该错误。

其他异常:

  • 在Debug下不会出现故障
  • 我无法从 Windows 崩溃中附加调试器
  • OnStartup() 中的 MessageBox.Show() 只是闪烁 MB(非模态)

我还尝试用 Environment.Exit() 替换 App.Current.Shutdown() 代码,这更好 - 崩溃频率要低得多,但它们 仍有 10% 的时间发生。

当所有辅助实例都在将文件写入磁盘时,是什么导致了应用程序的硬崩溃?

更新
所以事实证明,崩溃问题根本与文件写入/FileWatcher 操作无关。我创建了相同代码的 NamedPipe 版本,但仍然失败。

事实证明,真正的罪魁祸首是 WPF 在启动时用于将图像启动到屏幕的 SplashScreen。虽然不是 WPF 术语中的完整“窗口”,但此窗口在新线程上启动并在完全初始化之前提前关闭会在 SplashScreen 线程完成之前终止应用程序,从而导致内核崩溃。解决方法是 a) 删除启动画面,b) 手动管理启动画面并且不显示它以便提前退出​​或 c) 在退出之前明确关闭启动画面:

SplashScreen.Close(TimeSpan.MinValue);
Environment.Exit(0);

我写了一篇博文,部分更详细地介绍了这个问题:

http://weblog.west-wind.com/posts/2016/May/13/Creating-Single-Instance-WPF-Applications-that-open-multiple-Files

最佳答案

尝试改用 FileStream,然后您可以确保使用 FileStream.Flush 关闭文件句柄

using (FileStream fs = File.Create(mmApp.Configuration.FileWatcherOpenFilePath))
{
byte[] info = new UTF8Encoding(true).GetBytes(filesToOpen);
fs.Write(info, 0, info.Length);
fs.Flush();
}

关于c# - WPF App.OnStartup() 在写入文件和 FileWatcher 时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37179821/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com