Reputation: 101
Is there a way to determine when a File is no longer in use other than using a Process.Exited(){} event and testing whether the file is locked? Is there a pattern or certain commands we are not using that will handle this better?
Currently, we create the file in the user's temp folder, launch the file in its default program (determined by file extension), then have the file updated if there were changes and deleted after closing.
This works fine for ALL single file launches.
However, we run into an issue when a second, or more, file(s) are opened in certain programs, like Gimp, Word, or Paint.net. They open the file and then pass the files Handle off to the existing process, closing the initial process that was created as the filing process below. Since the original process (file process) closes, the Exited event fires prematurely.
If you place the code below into the Program.cs of a console app project and change the path and file name to an existing file. You can run it and the file should open in its default viewer/editor. A default program may need to be chosen.
We tested this with Word and GIMP to make sure this works. Make sure all WinWord processes(or whatever process for the program that is set as default for the file you decide to test) are closed and run the program with a breakpoint in the exited event. You will notice that it only hits once the program closes and the file is not locked.
However, If you have a word document, or any of the other programs mentioned above, open and then run the program you will notice the Exited event fires immediately after opening the document. In the case of Word Documents, the file is locked, but for GIMP the file is not locked. So, simply determining if the file is locked or not when the process exits are not enough.
So, if it is the only process running, or if it is a program that keeps the process we created, there is no issue. Programs like NotePad keep the process that opens each file and the Exited event fires as expected.
Is there a better way to handle this?
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;
}
}
}
Upvotes: 0
Views: 119