除了使用 Process.Exited(){} 事件并测试文件是否被锁定之外,有没有办法确定文件何时不再使用?有没有我们不使用的模式或某些命令可以更好地处理这个问题?
目前,我们在用户的临时文件夹中创建文件,在其默认程序中启动文件(由文件扩展名确定),然后如果有更改则更新文件并在关闭后删除。
这适用于所有单个文件启动。
但是,当在某些程序(如 Gimp、Word 或 Paint.net)中打开第二个或更多文件时,我们会遇到问题。他们打开文件,然后将文件句柄传递给现有进程,关闭作为下面的归档进程创建的初始进程。由于原始进程(文件进程)关闭,Exited 事件过早触发。
如果您将以下代码放入控制台应用程序项目的 Program.cs 并将路径和文件名更改为现有文件。您可以运行它,文件应在其默认查看器/编辑器中打开。可能需要选择默认程序。
我们用 Word 和 GIMP 对此进行了测试,以确保它有效。确保所有 WinWord 进程(或为您决定测试的文件设置为默认程序的任何进程)都已关闭,并在退出事件中使用断点运行程序。您会注意到它仅在程序关闭并且文件未锁定时才会触发。
但是,如果您有 Word 文档或上述任何其他程序,打开然后运行该程序,您会注意到 Exited 事件在打开文档后立即触发。对于 Word 文档,文件被锁定,但对于 GIMP,文件未被锁定。因此,仅在进程退出时确定文件是否被锁定是不够的。
因此,如果它是唯一运行的进程,或者它是保留我们创建的进程的程序,则没有问题。像记事本这样的程序会保留打开每个文件的过程,并且 Exited 事件会按预期触发。
有没有更好的方法来处理这个?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ExampleConsole
{
class Program
{
List<FileSystemWatcher> FileWatchers { get; set; } = new List<FileSystemWatcher>();
static public bool IsFileChanged { get; set; }
//replace the following with whatever file you prefer, but GIMP, Word, Paint.Net, and FoxIt PDF Editor
//are what we have noticed using a single manager processes rather than the process that ran when opening the file.
static string fileName = "source.gif";
static string myTempFile = Path.Combine(Path.GetTempPath() + fileName);
[STAThread]
static void Main()
{
Process fileProcess = Process.Start(new ProcessStartInfo(myTempFile)
{
UseShellExecute = true,
CreateNoWindow = true
});
fileProcess.EnableRaisingEvents = true;
//This is where the issue occurs launching more than 1 Gimp/Word/FoxIt PDF Editor,
//the "Exited" Event runs when the process that opened the file closes after handing off to a manager process
fileProcess.Exited += (o, args) =>
{
if (CheckFileLock(myTempFile))
{
MessageBox.Show("File is locked.");
}
else
{
MessageBox.Show("File is not locked.");
}
//If the process exits, we read the changes into memory and save to our database.
if (IsFileChanged)
{
MessageBox.Show("File would be saved.");
}
fileProcess?.Dispose();
};
#if DEBUG
try
{
//...
}
finally
{
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
#endif
}
public static bool CheckFileLock(string filePath)
{
try
{
using (File.Open(filePath, FileMode.Open)) { }
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
}
return false;
}
}
}