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(); }
}
}
Related
I have reviewed the stackoverflow questions that are pertinent to my situation.
They don't answer a base question.
My application allows the user to type in a fully qualified path. This path must be to a file. The file cannot exist yet (they are saving a backup). If you use the getattributes approach, it will trip the catch of the try/catch as the file is not found. (as it shouldn't be). I need to catch if the user just put in a path to a directory, and if the path is to a file that the file doesn't exists. I need to give directed feedback to the user if either case happens. I am using C# with .NET framework of 4.5.2.
Thanks for any pointers.
It is not mandatory for a file to have a file extension for it to be a valid file. Therefore, you can't rely on the path having a file extension to call it a file.
using System;
namespace FileFolder_46434099
{
class Program
{
static void Main(string[] args)
{
string incomingpath = #"C:\temp\3075";
if (System.IO.Directory.Exists(incomingpath))
{
Console.WriteLine("path is a directory");
}
else if (System.IO.File.Exists(incomingpath))
{
Console.WriteLine("path is of a file");
}
Console.ReadLine();
}
}
}
In this instance... C:\temp\3075 is actually a file, and the program returns it as such.
Can you try this and see if this is what you are looking for. I am assuming that user will enter extension of the file, as you mentioned user will enter fully qualified path.
static void Main(string[] args)
{
Console.WriteLine("Enter fully qualified path of the file to be accessed.");
var eneteredPath = Console.ReadLine();
var isItFile = Path.HasExtension(eneteredPath);
if (isItFile)
{
Console.WriteLine($"Specified File exists = {File.Exists(eneteredPath)}");
}
else if(Directory.Exists(eneteredPath))
{
Console.WriteLine($"Specified path is to a directory.");
}
}
So I have a simple form with a txtInput, and a button:
using System.IO;
private void cmdCheck_Click(object sender, EventArgs e)
{
if (Directory.Exists(txtInput.Text))
{
// This is a directory, not a file.
}
else
{
try
{
if (File.Exists(txtInput.Text))
{
var fileInfo = new FileInfo(txtInput.Text);
// File exists and now we have the information. Alert the user.
}
else
{
// File doesn't exist. Do things.
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message, "ERROR");
}
}
}
Does this suit your requirements?
This will helps you https://www.dotnetperls.com/path
Path. This path leads somewhere. It goes between trees and buildings. A cloud moves. Sunlight reaches the ground and our direction is clear.
With Path, a class in the .NET Framework, we have built-in methods. This class helps when handling file paths. It is part of System.IO.
I found this answer https://stackoverflow.com/a/14336292/1537195 which gave a good way to detect password protection for DOC and XLS files.
//Flagged with password
if (bytes.Skip(0x20c).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2003
if (bytes.Skip(0x214).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2005
if (bytes.Skip(0x20B).Take(1).ToArray()[0] == 0x13) return true; //DOC 2005
However it does not seem to cover all XLS files and I am also looking for a way to detect PPT files in the same manner. Does anyway know which bytes to look at for these file types?
I saved a PowerPoint presentation as .ppt and .pptx with and without a password required for opening them, opened them in 7-Zip and came to the tentative conclusion that
.pptx files without a password always use a standard .zip file format
.ppt files are CompoundDocuments
.pptx files with a password also CompoundDocuments
All passworded CompoundDocuments contain an entry named *Encrypt*
To get this code running, you need to installed the NuGet package OpenMcdf. This is the first C# library that I could find for reading CompoundDocuments.
using OpenMcdf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace _22916194
{
//http://stackoverflow.com/questions/22916194/detecing-password-protected-ppt-and-xls-documents
class Program
{
static void Main(string[] args)
{
foreach (var file in args.Where(File.Exists))
{
switch (Path.GetExtension(file))
{
case ".ppt":
case ".pptx":
Console.WriteLine($"* {file} " + (HasPassword(file) ? "is " : "isn't ") + "passworded");
Console.WriteLine();
break;
default:
Console.WriteLine($" * Unknown file type: {file}");
break;
}
}
Console.ReadLine();
}
private static bool HasPassword(string file)
{
try
{
using (var compoundFile = new CompoundFile(file))
{
var entryNames = new List<string>();
compoundFile.RootStorage.VisitEntries(e => entryNames.Add(e.Name), false);
//As far as I can see, only passworded files contain an entry with a name containing Encrypt
foreach (var entryName in entryNames)
{
if (entryName.Contains("Encrypt"))
return true;
}
compoundFile.Close();
}
}
catch (CFFileFormatException) {
//This is probably a .zip file (=unprotected .pptx)
return false;
}
return false;
}
}
}
You should be able to extend this code to handle other Office formats. The conclusions at the top should hold true, except that you need to look for some other data in the CompoundDocument than a filename containing *Encrypt* (I had a quick look at .doc files and it didn't seem to work exactly the same).
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());
}
}
}
In my C# program that is made with Visual Studio 2010 and uses WinForms, I would like the program to save state of some checkboxes and textboxes so the next time program will be loaded they are checked or unchecked as theire last run's state. Also same with strings inside textboxes and etc...
What will be the proper way to achieve this? Is there a built in stuff in .NET? Any tips and code snippets would be appriciated!
Thanks
You'd probably want to look at reading the relevant values from your UI during the FormClosing event, and then saving them into User Settings.
Have a look at: http://codehill.com/2009/01/saving-user-and-application-settings-in-winforms/
I would bind the value to user settings, and saving the configuration OnClose event.
One way to do this is using an XML configuration file and serializing it:
ConfigManager.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyApplication
{ [ Serializable() ]
public class ConfigManager
{
private int removeDays = 7;
public ConfigManager() { }
public int RemoveDays
{
get
{
return removeDays;
}
set
{
removeDays = value;
}
}
}
somewhere in your application
private ConfigManager cm;
private XmlSerializer ser;
...
Then you have to load the configuration:
private void LoadConfig()
{
try
{
cm = new ConfigManager();
ser = new XmlSerializer(typeof(ConfigManager));
filepath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + cm.filepath;
if (File.Exists(filepath))
{
FileStream fs = new FileStream(filepath, FileMode.Open);
cm = (ConfigManager)ser.Deserialize(fs);
// do something
}
} catch (Exception ex) { }
}
To save it:
XmlSerializer ser;
ConfigManager cm;
...
private void saveConfig()
{
try {
cm.RemoveDays = 6;
TextWriter tw = new StreamWriter(filepath, false);
ser.Serialize(tw, cm);
tw.Close();
} catch (Exception ex)
}
You asked very broad question. there are two ways to look at it.
1) If you have a need to persist application level configuration, your best bet is to use Application Settings. One can serialize program settings the user has done using your app, and restore them after the program has restarted. This works with WinForms and WPF:
2) If you need user level persistence, you need user settings.
Also, you can create custom class that implements that stores all of the configuration properties that you need.
Implement ISerializable and mark it [Serializable]. You could just mark it [Serializable], but if you add new properties in the future, you'll run into deserialization problems.
Add a Version property.
Add two static methods: Load and Save. These methods use IsolatedStorage to deserialize/serialize your configuration class to disk. You can use any kind of serialization you want - I use binary. Why not XML? Because binary is faster and users never need to get into these files. I used to do this for .net 2.0.
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");
}