What's a good way to ensure that a temp file is deleted if my application closes or crashes? Ideally, I would like to obtain a temp file, use it, and then forget about it.
Right now, I keep a list of my temp files and delete them with an EventHandler that's triggered on Application.ApplicationExit.
Is there a better way?
Nothing is guaranteed if the process is killed prematurely, however, I use "using" to do this..
using System;
using System.IO;
sealed class TempFile : IDisposable
{
string path;
public TempFile() : this(System.IO.Path.GetTempFileName()) { }
public TempFile(string path)
{
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
this.path = path;
}
public string Path
{
get
{
if (path == null) throw new ObjectDisposedException(GetType().Name);
return path;
}
}
~TempFile() { Dispose(false); }
public void Dispose() { Dispose(true); }
private void Dispose(bool disposing)
{
if (disposing)
{
GC.SuppressFinalize(this);
}
if (path != null)
{
try { File.Delete(path); }
catch { } // best effort
path = null;
}
}
}
static class Program
{
static void Main()
{
string path;
using (var tmp = new TempFile())
{
path = tmp.Path;
Console.WriteLine(File.Exists(path));
}
Console.WriteLine(File.Exists(path));
}
}
Now when the TempFile is disposed or garbage-collected the file is deleted (if possible). You could obviously use this as tightly-scoped as you like, or in a collection somewhere.
Consider using the FileOptions.DeleteOnClose flag:
using (FileStream fs = new FileStream(Path.GetTempFileName(),
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None,
4096, FileOptions.RandomAccess | FileOptions.DeleteOnClose))
{
// temp file exists
}
// temp file is gone
You could P/Invoke CreateFile and pass the FILE_FLAG_DELETE_ON_CLOSE flag. This tells Windows to delete the file once all handles are closed. See also: Win32 CreateFile docs.
I would use the .NET TempFileCollection class, as it's built-in, available in old versions of .NET, and implements the IDisposable interface and thus cleans up after itself if used e.g. in conjunction with the "using" keyword.
Here's an example that extracts text from an embedded resource (added via the projects property pages -> Resources tab as described here: How to embed a text file in a .NET assembly?, then set to "EmbeddedResource" in the embedded file's property settings).
// Extracts the contents of the embedded file, writes them to a temp file, executes it, and cleans up automatically on exit.
private void ExtractAndRunMyScript()
{
string vbsFilePath;
// By default, TempFileCollection cleans up after itself.
using (var tempFiles = new System.CodeDom.Compiler.TempFileCollection())
{
vbsFilePath= tempFiles.AddExtension("vbs");
// Using IntelliSense will display the name, but it's the file name
// minus its extension.
System.IO.File.WriteAllText(vbsFilePath, global::Instrumentation.Properties.Resources.MyEmbeddedFileNameWithoutExtension);
RunMyScript(vbsFilePath);
}
System.Diagnostics.Debug.Assert(!File.Exists(vbsFilePath), #"Temp file """ + vbsFilePath+ #""" has not been deleted.");
}
I use a more reliable solution:
using System.IO;
using System.Reflection;
namespace Helpers
{
public static partial class TemporaryFiles
{
private const string UserFilesListFilenamePrefix = ".used-temporary-files.txt";
static private readonly object UsedFilesListLock = new object();
private static string GetUsedFilesListFilename()
{
return Assembly.GetEntryAssembly().Location + UserFilesListFilenamePrefix;
}
private static void AddToUsedFilesList(string filename)
{
lock (UsedFilesListLock)
{
using (var writer = File.AppendText(GetUsedFilesListFilename()))
writer.WriteLine(filename);
}
}
public static string UseNew()
{
var filename = Path.GetTempFileName();
AddToUsedFilesList(filename);
return filename;
}
public static void DeleteAllPreviouslyUsed()
{
lock (UsedFilesListLock)
{
var usedFilesListFilename = GetUsedFilesListFilename();
if (!File.Exists(usedFilesListFilename))
return;
using (var listFile = File.Open(usedFilesListFilename, FileMode.Open))
{
using (var reader = new StreamReader(listFile))
{
string tempFileToDelete;
while ((tempFileToDelete = reader.ReadLine()) != null)
{
if (File.Exists(tempFileToDelete))
File.Delete(tempFileToDelete);
}
}
}
// Clean up
using (File.Open(usedFilesListFilename, FileMode.Truncate)) { }
}
}
}
}
Every time you need temporary file use:
var tempFile = TemporaryFiles.UseNew();
To be sure all temporary files are deleted after application closes or crashes put
TemporaryFiles.DeleteAllPreviouslyUsed();
at start of the application.
It's nice to see that you want to be responsible, but if the files aren't huge (>50MB), you would be in line with everyone (MS included) in leaving them in the temp directory. Disk space is abundant.
As csl posted, GetTempPath is the way to go. Users who are short on space will be able to run disk cleanup and your files (along with everyone else's) will be cleaned up.
I'm not primarily a C# programmer, but in C++ I'd use RAII for this. There are some hints on using RAII-like behaviour in C# online, but most seem to use the finalizer — which is not deterministic.
I think there are some Windows SDK functions to create temporary files, but don't know if they are automatically deleted on program termination. There is the GetTempPath function, but files there are only deleted when you log out or restart, IIRC.
P.S. The C# destructor documentation says you can and should release resources there, which I find a bit odd. If so, you could simply delete the temp file in the destructor, but again, this might not be completely deterministic.
You could launch a thread on startup that will delete files that exist when they "shouldn't" to recover from your crash.
If you're building a Windows Forms Application, you can use this code:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
File.Delete("temp.data");
}
Related
I have a pair of apps. One is a game for young kids. The other is a tool for use by the parents. I separate the apps in this way to keep the UI as simple as possible for the kids.
One part of the parent app is to control specific settings of the kids' app. I've just been using Settings.settings for most of my settings up to now, but can't see a simple way for the parent app to access and change the settings in the kids' app (apart from a rather kludgy back-door using XML).
Is there a way, or alternatively, is there another place I should consider keeping my shared settings?
I like the ease of two-way binding for managing settings via a dialog, but could survive without that if necessary.
FWIW: Both apps do use a common DLL where a lot of common code resides. Maybe there's a way of leveraging that?
You can save the settings to a file into a common folder like the below (eg C:\ProgramData\yourfolder)
String DataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\yourfolder";
and access the settings file from both applications. No need of a database and easy of use.
The settings file can be a simple json or whatever you feel comfortable.
In the end, I went with having a class that contains the shared settings. Here's the main part of it:
public class AppConfig
{
public int SaveSize = 1000;
//... other shared settings here
static private bool ready = false;
static private AppConfig instance;
static public AppConfig Instance
{
get
{
if (!ready)
{
ReadFromFile();
}
return instance;
}
}
static private readonly string filename = Common.AppDataFileName("config.xml");
static private void ReadFromFile()
{
ready = true;
if (!File.Exists(filename))
{
instance = new AppConfig();
return;
}
XmlSerializer serializer = new XmlSerializer(typeof(AppConfig));
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
instance = (AppConfig)serializer.Deserialize(fs);
}
}
static public void Save()
{
XmlSerializer serializer = new XmlSerializer(typeof(AppConfig));
using (TextWriter writer = new StreamWriter(filename))
{
serializer.Serialize(writer, Instance);
}
}
}
Because you have to have an instance for serialization, I instantiate that on-demand and then refer to it as needed. E.g.:
int size = AppConfig.Instance.SaveSize;
Don't know if this is the best way, but it works for my needs for now. If it is a bad way to do this, don't be shy to comment below. I'm here to learn!
I've got a Windows 10 UWP application written in C#. I'm using SQLite to store my data locally. The issue I'm experiencing is that the file is never saved and/or retrieved using this code. It should work, but I can't find out what's wrong.
dbExists always evaluates to false, so what am I missing here?
private SQLiteConnection localConn;
private string dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "myDatabase.db");
public async void DBInit()
{
bool dbExists = false;
try
{
var store = await ApplicationData.Current.LocalFolder.GetFileAsync(dbPath);
dbExists = true;
}
catch { dbExists = false; }
if (!dbExists)
{
using (localConn = new SQLiteConnection(new SQLitePlatformWinRT(), dbPath))
{
// Create table
localConn.CreateTable<MyTable>();
}
}
else // CURRENTLY NOT FIRING!!
{}
}
Please consider using below code to create and access database file:
StorageFile notesFile = await storageFolder.CreateFileAsync(dbPath, CreationCollisionOption.OpenIfExists);
This will create new file if it does not exists and retrieve it when it is already created.
Please check my blog article to see more about UWP Data Storage:
https://mobileprogrammerblog.wordpress.com/2016/05/23/universal-windows-10-apps-data-storage/
I think you're missing this important piece of code:
SQLiteConnection.CreateFile("mydatabase.sqlite");
Do that first, then create a connection instance referencing the (now) created file.
Also, I'd suggest that you name the db with the .sqlite extension, so that the rest of the team and incoming devs, when then look at the db file artifact, can immediately tell that this is an sqlite database.
EDIT:
The method is a static method. So you would use it like this...
using System.Data.SQLite;
namespace sqlite_sample
{
class Program
{
static void Main(string[] args)
{
SQLiteConnection.CreateFile("sample.db");
}
}
}
The following will not work:
var conn = SQLiteConnection(...);
conn.CreateFile(dbPath); //<-- static methods can't be invoked at the instance level...
I have not found yet a file-rename-function in .NET for C#, so I'm a bit confused how I would rename a file. I use the command prompt with Process.Start, but this isn't really professional and a black DOS window is popping up each time. Yes, I know there is something in the Visual Basic namespace, but this is not my intention to add the "visual-basic.dll" to my project.
I found some examples which "move" the file to rename it. It is a quite painful method and a shoddy workaround for things. Such footwork I can program myself.
Every language has renaming commands, so I am stunned that C# hasn't or I haven't found out yet. What is the right command?
For large files and to rename on CD, this code works, but your project will be partly converted into Visual Basic (as I understand it, maybe it is not so):
//Add the Microsoft.VisualBasic.MyServices reference and namespace in a project;
//For directories:
private static bool RenameDirectory(string DirPath, string NewName)
{
try
{
FileSystemProxy FileSystem = new Microsoft.VisualBasic.Devices.Computer().FileSystem;
FileSystem.RenameDirectory(DirPath, NewName);
FileSystem = null;
return true;
}
catch {
return false;
} //Just shut up the error generator of Visual Studio
}
//For files:
private static bool RenameFile(string FilePath, string NewName)
{
try
{
FileSystemProxy FileSystem = new Microsoft.VisualBasic.Devices.Computer().FileSystem;
FileSystem.RenameFile(FilePath, NewName);
FileSystem = null;
return true;
}
catch {
return false;
} //Just shut up the error generator of Visual Studio
}
A rename is just a move and vice versa, see the MSDN : File.Move
In the OS the operations are the same for all intents an purposes. That's why in explorer a move on the same partition is near instantaneous - just adjusts the file name and logical location. To Rename a file in the same directory you Move it to a new File Name in the same directory.
using System;
using System.IO;
class Test
{
public static void Main()
{
string path = #"c:\temp\MyTest.txt";
string path2 = #"c:\temp2\MyTest.txt";
try
{
if (!File.Exists(path))
{
// This statement ensures that the file is created,
// but the handle is not kept.
using (FileStream fs = File.Create(path)) {}
}
// Ensure that the target does not exist.
if (File.Exists(path2))
File.Delete(path2);
// Move the file.
File.Move(path, path2);
Console.WriteLine("{0} was moved/renamed to {1}.", path, path2);
// See if the original exists now.
if (File.Exists(path))
{
Console.WriteLine("The original file still exists, which is unexpected.");
}
else
{
Console.WriteLine("The original file no longer exists, which is expected.");
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
}
}
I am trying to execute cs scripts in a directory a loop. Every time the script changed (or if it's new) it gets loaded and executed. But I receive an error on trying to load the script a second time:
Access to the path 'C:\Users\Admin\AppData\Local\Temp\CSSCRIPT\Cache\647885655\hello.cs.compiled' is denied.
What I tried to do was:
static Dictionary<string, string> mFilePathFileHashes = new Dictionary<string, string>();
public static void LoadFromDir(string dir)
{
foreach (string filepath in Directory.GetFiles(dir))
{
string hash = GetMD5HashFromFile(filepath); //Generate file hash
if (mFilePathFileHashes.Contains(new KeyValuePair<string, string>(filepath, hash))) continue; //Skip if it hasn't changed
if (mFilePathFileHashes.ContainsKey(filepath))
{ //Hash changed
mFilePathFileHashes[filepath] = hash;
}
else //This is the first time this file entered the loop
mFilePathFileHashes.Add(filepath, hash);
//Load the script
IScript script = CSScript.Load(filepath)
.CreateInstance("Script")
.AlignToInterface<IScript>();
//Do stuff
script.AddUserControl();
}
protected static string GetMD5HashFromFile(string fileName)
{
FileStream file = new FileStream(fileName, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
At the "Load the script" part it would throw the error. So I read up on it a bit and tried this:
//Load the script
string asmFile = CSScript.Compile(filepath, null, false);
using (AsmHelper helper = new AsmHelper(asmFile, "temp_dom_" + Path.GetFileName(filepath), true))
{
IScript script = helper.CreateAndAlignToInterface<IScript>("Script");
script.AddUserControl();
//helper.Invoke("Script.AddUserControl");
}
Because that page said Script is loaded in the temporary AppDomain and unloaded after the execution. To set up the AsmHelper to work in this mode instantiate it with the constructor that takes the assembly file name as a parameter
But that won't Align to the interface : Type 'Script' in Assembly 'hello.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable. What does that even mean, and why would it need to be serializable?
If I switch to the helper.Invoke line instead I get a NullReferenceException.
The script:
using System;
using System.Windows.Forms;
using CSScriptTest;
class Script : CSScriptTest.IScript
{
public void AddUserControl()
{
Form1.frm.AddUserControl1(this, "test_uc_1");
}
}
So that last error may be because I never actually Aligned to an interface, or because I am calling a static method from outside of the main AppDomain (I really wouldn't know).
Is there any way to get this working?
Well, it works by passing the object I want to operate on to the interface' method like this:
using (var helper = new AsmHelper(CSScript.Compile(filepath), null, false))
{
IScript script = helper.CreateAndAlignToInterface<IScript>("Script");
script.AddUserControl(Form1.frm);
}
With the script inheriting from MarshalByRefObject like so:
using System;
using System.Windows.Forms;
using CSScriptTest;
class Script : MarshalByRefObject, CSScriptTest.IScript
{
public void AddUserControl(CSScriptTest.Form1 host)
{
host.AddUserControl1(this, "lol2");
}
}
MSDN sais MarshalByRefObject Enables access to objects across application domain boundaries in applications that support remoting. So I guess that makes sense.. but is there any way for me to expose my main application's methods to the scripts?
It doesn't seem to be possible by inheriting from MarshalByRefObject in the main program, like so:
public class CTestIt : MarshalByRefObject
{
public static CTestIt Singleton;
internal static void SetSingleton()
{ //This method is successfully executed before we start loading scripts
Singleton = new CTestIt();
Console.WriteLine("CTestIt Singleton set");
}
public static void test()
{
//Null reference when a script calls CSScriptTest.CTestIt.test();
Singleton.test_member();
}
public void test_member()
{
Console.WriteLine("test");
}
}
I am working on a file locker/unlocker application using C# on VS2010.
what i want is to lock a file with a password using my application and then unlock it any time.
In fact, I used the following code to lock the file, but the file is being locked only while the application is still running; when I close the application, the file is unlocked.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Configuration;
using System.Windows.Forms;
namespace LockFile
{
public enum LockStatus
{
Unlocked,
Locked
}
public class LockFilePresenter
{
private ILockFileView view;
private string file2Lock = string.Empty;
private FileStream fileLockStream = null;
public LockFilePresenter(ILockFileView view)
{
this.view = view;
}
internal void LockFile()
{
if (string.IsNullOrEmpty(file2Lock) || !File.Exists(file2Lock))
{
view.ShowMessage("Please select a path to lock.");
return;
}
if (fileLockStream != null)
{
view.ShowMessage("The path is already locked.");
return;
}
try
{
fileLockStream = File.Open(file2Lock, FileMode.Open);
fileLockStream.Lock(0, fileLockStream.Length);
view.SetStatus(LockStatus.Locked);
}
catch (Exception ex)
{
fileLockStream = null;
view.SetStatus(LockStatus.Unlocked);
view.ShowMessage(string.Format("An error occurred locking the path.\r\n\r\n{0}", ex.Message));
}
}
internal void UnlockFile()
{
if (fileLockStream == null)
{
view.ShowMessage("No path is currently locked.");
return;
}
try
{
using (fileLockStream)
fileLockStream.Unlock(0, fileLockStream.Length);
}
catch (Exception ex)
{
view.ShowMessage(string.Format("An error occurred unlocking the path.\r\n\r\n{0}", ex.Message));
}
finally
{
fileLockStream = null;
}
view.SetStatus(LockStatus.Unlocked);
}
internal void SetFile(string path)
{
if (ValidateFile(path))
{
if (fileLockStream != null)
UnlockFile();
view.SetStatus(LockStatus.Unlocked);
file2Lock = path;
view.SetFile(path);
}
}
internal bool ValidateFile(string path)
{
bool exists = File.Exists(path);
if (!exists)
view.ShowMessage("File does not exist.");
return exists;
}
}
}
and
using System;
using System.Collections.Generic;
using System.Text;
namespace LockFile
{
public interface ILockFileView
{
void ShowMessage(string p);
void SetStatus(LockStatus lockStatus);
void SetFile(string path);
}
}
As I said previously, the application works fine during the running time, but when I close it, the locked file will be unlocked.
If anybody has any idea about how to do it, I would be grateful.
A Lock on a FileStream just means that your process has exclusive access to the file while it's active; it has nothing to do with password protecting a file.
It sounds like what you want is to encrypt a file with a password. The file class provides Encrypt/Decrypt based on the current user, or, if you want it based on your own custom password there's a sample of using some of the classes in the System.Security.Cryptography namespace to encrypt a file with a password here (instead of hard coding you would take it as input presumably) http://www.codeproject.com/Articles/26085/File-Encryption-and-Decryption-in-C
Keep in mind, doing security right is hard.
You're using the FileStream.Lock() method to lock a specific file so that only the process running the FileStream can use it.
http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx
This is a mechanism designed to prevent other processes writing to a file that you are reading/writing to, and you can see this method in use with applications like Microsoft Excel.
When you close your application, the process is no longer running, and the lock on the file is disengaged.
If your goal is to prevent other applications from reading the file, you have some limited options:
Encrypt the file. This will mean that an application cannot read usable information from the file without the decryption key, but there is the potential for an application to open and change the encrypted file.
Save the file to a read-only media like a CD/DVD, or to removable storage that you then unplug and carry with you.
If you want to prevent other applications from modifying the file, you might look at the ReadOnly flags that Windows offers: http://msdn.microsoft.com/en-us/library/system.io.fileinfo.isreadonly.aspx
Note that these will still be insecure, as readonly flags can be ignored.
Something you need to think about is your reasoning for why you want to be restricting access to a file - that will help determine the best strategy for restricting access.
If all you need to do is make sure nothing else can read or modify the file while you've got your application locking it, the below should do the job.
If you need anything more, look into proper file encryption techniques.
Note that if you close the application the lock will no longer be in effect.
System.IO.FileStream fileStream;
private void LockFile(string FilePath)
{
fileStream = System.IO.File.Open(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None);
//using System.IO.FileShare.None in the above line should be sufficient, but just to go the extra mile...
fileStream.Lock(0, fileStream.Length);
}
private void UnlockFile()
{
if (fileStream != null)
{
try { fileStream.Unlock(0, fileStream.Length); }
finally { fileStream.Dispose(); }
}
}