You can try to catch the moment when actual app is closed by finding it py parent process id. If you found it, you can wait it to close as long as it is acceptable. Thanks jeremy-murray for GetAllProcessParentPids method:
public void StartProcessAndWathTillTerminated(string tempFilePath)
{
// Show app selection dialog to user
Process rundll32 = Process.Start("rundll32.exe", string.Format("shell32.dll,OpenAs_RunDLL {0}", tempFilePath));
int rundll32id = rundll32.Id;
// Wait till dialog is closed
while (!rundll32.HasExited)
{
System.Threading.Thread.Sleep(50);
}
// Get all running processes with parent id
Dictionary<int, int> allprocparents = GetAllProcessParentPids();
int openedAppId = 0;
// Loop throu all processes
foreach (var allprocparent in allprocparents)
{
// Found child process, started by our rundll32.exe instance
if (allprocparent.Value == rundll32id)
{
openedAppId = allprocparent.Key;
break;
}
}
// Check if we actually found any process. It can not be found in two situations:
// 1) Process was closed too soon, while we was looking for it
// 2) User clicked Cancel and no application was opened
// Also it is possible that chesen application is already running. In this
// case new instance will be opened by rundll32.exe for a very short period of
//time needed to pass file path to running instance. Anyway, this case falls into case 1).
//If we ca not find process explicitly, we can try to find it by file lock, if one exists:
//I'm using here a code snippet from https://stackoverflow.com/a/1263609/880156,
//which assumes that there are possible more than one lock on this file.
//I just take first.
if (openedAppId==0)
{
Process handleExe = new Process();
handleExe.StartInfo.FileName = "handle.exe";
handleExe.StartInfo.Arguments = tempFilePath;
handleExe.StartInfo.UseShellExecute = false;
handleExe.StartInfo.RedirectStandardOutput = true;
handleExe.Start();
handleExe.WaitForExit();
string outputhandleExe = handleExe.StandardOutput.ReadToEnd();
string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputhandleExe, matchPattern))
{
openedAppId = int.Parse(match.Value);
break;
}
}
if (openedAppId != 0)
{
Process openedApp = Process.GetProcessById(openedAppId);
while (!openedApp.HasExited)
{
System.Threading.Thread.Sleep(50);
}
}
// When we reach this position, App is already closed or was never started.
}
public static Dictionary<int, int> GetAllProcessParentPids()
{
var childPidToParentPid = new Dictionary<int, int>();
var processCounters = new SortedDictionary<string, PerformanceCounter[]>();
var category = new PerformanceCounterCategory("Process");
// As the base system always has more than one process running,
// don't special case a single instance return.
var instanceNames = category.GetInstanceNames();
foreach(string t in instanceNames)
{
try
{
processCounters[t] = category.GetCounters(t);
}
catch (InvalidOperationException)
{
// Transient processes may no longer exist between
// GetInstanceNames and when the counters are queried.
}
}
foreach (var kvp in processCounters)
{
int childPid = -1;
int parentPid = -1;
foreach (var counter in kvp.Value)
{
if ("ID Process".CompareTo(counter.CounterName) == 0)
{
childPid = (int)(counter.NextValue());
}
else if ("Creating Process ID".CompareTo(counter.CounterName) == 0)
{
parentPid = (int)(counter.NextValue());
}
}
if (childPid != -1 && parentPid != -1)
{
childPidToParentPid[childPid] = parentPid;
}
}
return childPidToParentPid;
}
Update
It seems that there is no solution with 100% guarantee of success due to many reasons.
I think that finding a process started by rundll32.exe is most solid among all other. If it fails, you still able to complete it with some other methods to determine process id.
As far as i know, there are several other ways to find that file is still used. Winword.exe, for example, creates some temp files in same directory and removes them when it closes. So if you able to catch a moment of temp files deleting then you may assume that program been closed.
Other programs may hold your file open by setting a lock on it. If so, you can find that program by finding lock owner. I used a solution with external program handle.exe from this answer https://stackoverflow.com/a/1263609/880156, so take a look.
I have to mention, that there may be no permanent file lock at all. It depend on program architecture. For example, if you open html file with Firefox, it reads file as fast as it can and closes it and does not leave file locked no more. In this case, even if you somehow find process name (e.g. "firefox.exe"), you will not able to find a moment when user closes a tab with your file.
If i were you, i would implement this solution, that still not ideal, and i would updgrade it later if it is necessary.