3

Searched a lot, but without luck - so here goes

My C# winforms application creates temp files which are opened using the default registered application (let's call them viewer apps). Once the user is done viewing those files, I want to delete them.

Currently, I register for an Application.ApplicationExit event, to delete the file. This approach covers most of the situations but not all. Sometimes the user still has the viewing application open while exiting my app, so the success of my File.Delete depends on whether the viewer has opened the file with FileShare.Delete or not - which is out of my control.

This is what I have found so far, but fall short of what I want

  1. FileOptions.DeleteOnClose does not help, since my app will already be closed in some cases and the temp file will still be needed. Also, when I create the file like this: new FileStream(fn, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, FileOptions.DeleteOnClose), the viewer apps like say adobe reader & notepad, still complain about file in use by my application The process cannot access the file because it is being used by another process
  2. MoveFileEx with MOVEFILE_DELAY_UNTIL_REBOOT dwFlags works, but it would wait till a reboot to delete it - I would rather have it deleted once the use is done, since reboots can be few and far between and forcing reboots IMO is not the most user friendly approach. On a side note, does windows automatically clear the %temp% folder on restart? Or is there any temp folder that windows automatically clears on restart?
  3. I can write another background process which constantly tries to delete the temp files till it succeeds, but I would like to avoid deploying one more piece of software to accomplish this. It can be done using a windows service or scheduled task or adding command line switches to my existing app and making it run in "delete mode" in background or through task scheduler. All of it decrease my ease of deployment and use along with increasing my footprint on client's computer

In a nutshell, I am wondering if there is any Win32 API or .NET Framework API that will delete a file as soon as there are no processes with open handle to that file?

EDIT:

The information in the temp files are reasonably private (think your downloaded bank account statements) and hence the need for immediate deletion after viewing as opposed to waiting for a reboot or app restart

Summary of all Answers and Comments

After doing some more experiments with inputs from Scott Chamberlain's answer, and other comments on this question, the best path seems to be to force the end users to close the viewer app before closing my application, if the viewer app disallows deletion (FileShare.Delete) of the temp file. The below factors played a role in the decision

  1. The best option is FileOptions.DeleteOnClose, but this only works if all files open before or after this call use FileShare.Delete option to open the file.
  2. Viewer apps can frequently open files without FileShare.Delete option.
  3. Some viewers close the handle immediately after reading/displaying the file contents (like notepad), whereas some other apps (like Adobe Reader) retain such handle till the file is closed in the viewer
  4. Keeping sensitive files on disk for any longer than required is definitely not a good way to proceed. So waiting till reboot should only be used as a fail-safe and not as the main strategy.
  5. The costs of maintaining another process to do the temp file cleanup, far exceeds the slight user inconvenience when they are forced to "close" the viewer before proceeding further.
Vikhram
  • 4,294
  • 1
  • 20
  • 32
  • 1
    Could you create the files in a specific dedicated area for your application (e.g. Folder in %temp%) & try to clear that folder on startup of your application. – PaulF Jan 10 '18 at 15:11
  • @PaulF yes, but the next start of the application is reasonably unpredictable - it could be in the next few minutes or next few days/months – Vikhram Jan 10 '18 at 15:14
  • See this answer : https://stackoverflow.com/questions/29744474/deleting-temp-files-automatically-c/29744710#29744710 It is not something the API allows. Is the size of the files left of such a size that they cannot be left for a few days/months? – PaulF Jan 10 '18 at 15:17
  • @PaulF I am aware of this option. I just updated my post to reflect that the reason for not keeping the files is not size but the sensitivity of the information – Vikhram Jan 10 '18 at 15:19
  • 1
    Using task scheduler API you could schedule an [idle task](https://msdn.microsoft.com/en-us/library/windows/desktop/aa383561(v=vs.85).aspx) that clears the temp files regularly. – zett42 Jan 10 '18 at 15:20
  • @zett42 I am aware of this option (#3 in my post), but it increases my software's footprint on the client's computer. Currently my software is a single exe which can be "double-clicked" to start - no installs or dependencies or command lines or configs. The only way to achieve this is to add one more service or scheduled task or make my existing software dual purpose using command line switches. – Vikhram Jan 10 '18 at 15:27
  • This does appear to be a big security risk - saving private information in readable format without guarantee of secure deletion. Can you keep track of the "viewer apps" & not allow shutdown of your application until all of those have been shutdown? Of course, if your application is forced to shutdown this private data will be accessible. – PaulF Jan 10 '18 at 15:30
  • No API could tell you that the registered viewing application is just a launcher for the real application and is managing to close itself before the real viewer has launched and managed to open the file it's being asked to view. Just as your application is free to do all manner of wonderfully odd things, so are other applications. So if you don't control them, you cannot predict their behaviour. – Damien_The_Unbeliever Jan 10 '18 at 15:30
  • @Damien_The_Unbeliever sure, but what an API can possibly do is some simple reference counted delete in the OS layer, so it is independent of the application, right? – Vikhram Jan 10 '18 at 15:32
  • Can you encrypt/decrypt on open/save to remove the security need to delete immediately after use..? – J M Jan 10 '18 at 15:32
  • Right, so an application might open the file, reason about its contents, close the existing handle and then re-open it in another manner - again, you're proposed API might make the file go "poof" before the application has really started working with it. – Damien_The_Unbeliever Jan 10 '18 at 15:33
  • @PaulF Not saying it is a very safe approach, but isn't that what happens when you do online banking and download statements? – Vikhram Jan 10 '18 at 15:33
  • @JM The information is usually encrypted and is decrypted on user's request (which results in this temp file) which needs to be deleted – Vikhram Jan 10 '18 at 15:34
  • I'm not sure why decryption has to result in the creation of a file? Is there a reason you have to create an un-encrypted temp file at all? – J M Jan 10 '18 at 15:35
  • Take, as an example, Visual Studio solution files. The registered handler for `.sln` files is a small stub program that opens the file, reasons about the contents and then decides on the *appropriate* version of VS to launch to handle that particular solution (if you have multiple versions of VS installed). As you're probably aware, VS can take a long time between being started and when it may actually first access the solution file. – Damien_The_Unbeliever Jan 10 '18 at 15:36
  • @JM I would like to avoid that too, but don't see any practical way to do so. For example, if there is a pdf data, I can't display it in user's default PDF viewer unless it is on a file. In my case, the data can be any format and it can be any app. Most other apps that I have come across either download and wait for reboot to let it get cleaned up or just use their own internal viewer which is far from ideal from a user experience perspective – Vikhram Jan 10 '18 at 15:46
  • I would say then that you have a fundamental design conflict that can't really be resolved until the decision is made regarding security vs user experience. Even if you did find a way to quickly delete the files, you still have a security issue in my opinion so I still think attempting to solve it this way is perhaps not the way to go. – J M Jan 10 '18 at 15:55
  • 1
    Can you explain more why option 1 would not fit your needs? It seems like the perfect solution, I don't understand your explanation why you can't use it. DeleteOnClose does exactly what you want it *"will delete a file as soon as there are no processes with open handle to that file"* – Scott Chamberlain Jan 10 '18 at 15:56
  • @ScottChamberlain When I create the file like this: `new FileStream(fn, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, FileOptions.DeleteOnClose)`, the viewer apps like say adobe reader & notepad, still complain about file in use by my application `The process cannot access the file because it is being used by another process.` – Vikhram Jan 10 '18 at 16:11
  • @Damien_The_Unbeliever The issue you point out exists with every "delete on reference count is zero" algorithm, but still is very widely used. For example, VB memory free mechanism, C++ `autoptr`, hard links on *nix file systems, etc. Not arguing the issue does not exist, but merely stating that proper usage of the API is always expected . – Vikhram Jan 10 '18 at 16:17
  • @JM I am not trying to make my app more secure then most prevailing apps, just making sure its not worse than what's out there :). Every time you access some sensitive information, say in proprietary format, via your browser, your browser downloads it onto your filesystem and expects the user to delete it. I am just trying to attempt a bit better than that – Vikhram Jan 10 '18 at 16:22
  • I do get what you mean. I just think you are flogging a dead horse. If security is "an issue", then I think you have to do it full on. If it isn't an issue then you are possibly making pain for yourself? All of the suggestions so far involve either polling or additional services which would be effort, are non-ideal and don't really do a lot to secure anything. – J M Jan 10 '18 at 16:31
  • Yes, but reference counting algorithms rely on *everyone following the rules*. There is *no* rule that states that the default application for any particular extension has to hold at least one handle to the file open for the duration that that application deems to be interesting. – Damien_The_Unbeliever Jan 10 '18 at 17:19

1 Answers1

1

This answer is based on my comments in the question.

Try write the file without the delete, close the file, let the editor open the file, then open a new filestream as a read with DeleteOnClose with an empty body in the using section.

If that 2nd opening does not fail it will behave exactly like you wanted, it will delete the file as soon as there are no processes with open handle to that file. If the 2nd opening for the delete does fail you can use MoveFileEx as a fallback failsafe.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • This has the same issue as continually trying to delete the file though or delete on exit (although it is better than delete on exit)...? It only works if the main app is running? I thought the op wanted to solve the issue even if the main app wasn't running? Am I missing something..? – J M Jan 10 '18 at 16:26
  • Did some quick experiments and it seems to suggest that that `FileOptions.DeleteOnClose` will not work unless it is "exclusive". Whoever goes second (either the viewer or my app), will get the message "The process cannot access the file ". Just to validate my experiments, I tried without the `FileOptions.DeleteOnClose` option and it opens the handle fine – Vikhram Jan 10 '18 at 16:34
  • DeleteOnClose is not the problem, the othet reader app opened the file without passing the FileShare.Delete flag is the problem. Not sure if there are any ways around it (but I will look in to it because I am curious) – Scott Chamberlain Jan 10 '18 at 16:46
  • I tried this method by creating a text file, opening it in Notepad++ and then opening another stream as suggested. I had to add a thread sleep delay between the opening in Notepad++ and the second stream. Otherwise, Notepad++ complained that the file didn't exist. After I added the delay, the file opened OK in Notepad++. But, when the next stream access ran (with DeleteOnClose and empty using body) Notepad++ complained that I had deleted the file.... – J M Jan 10 '18 at 16:47
  • 1
    Notepad++ closes the file after it reads it so there is no file handle to keep the file around. Try with regular notepad.exe, last I checked (Windows Vista) it kept the file open while the program was ipen. – Scott Chamberlain Jan 10 '18 at 17:13
  • I’ll give that a go in the morning. Interesting all round. Did you also need to add a delay in your tests? My test was just a dummy test. I didn’t get to actually trying this with a PDF or Word doc. – J M Jan 10 '18 at 22:15
  • Yes you usually will need to add a little delay before doing the 2nd opening. For things you are opening that have a GUI you can do [`Process.WaitForInputIdle()`](https://msdn.microsoft.com/en-us/library/8d7363e2(v=vs.110).aspx) to have it wait till the process is started and the Message pump is idle. You may still need to put a sleep in there after the call but you might be able to get away with a smaller one. – Scott Chamberlain Jan 10 '18 at 22:25
  • I tried your solution with my own test viewer to control file handling parameters and your claim seems to be completely accurate. But seems like I am at the mercy of the viewer itself. Viewers like notepad close the handle immediately after opening it, so I can work with it even though it does not use `FileShare.Delete`, but viewers like AdobeReader (possibly) don't open the file with `FileShare.Delete` and nor do the release the handle once the file is opened – Vikhram Jan 10 '18 at 23:59
  • 1
    Some useful info, found a old answer of mine that brings up the point of using `FILE_ATTRIBUTE_TEMPORARY` to open the file if you do get a viewer that is happy with using a open file, this will make it so the writes to the file are not flushed out to disk and you are more likely to have a 0 byte file on the drive that contains no info if the program or computer is stopped unexpectedly. https://stackoverflow.com/questions/34776031/how-to-create-a-temp-file-that-is-automatically-deleted-after-the-program-termin/34776237#34776237 – Scott Chamberlain Jan 11 '18 at 00:20