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).
Related
in the constructor :
SaveLoadFiles.LoadFile(textBoxRadarPath, "radarpath.txt");
SaveLoadFiles.LoadFile(textBoxSatellitePath, "satellitepath.txt");
if (textBoxRadarPath.Text != "" || textBoxSatellitePath.Text != "")
{
if(!Directory.Exists(textBoxRadarPath.Text))
{
Directory.CreateDirectory(textBoxRadarPath.Text);
}
if (!Directory.Exists(textBoxSatellitePath.Text))
{
Directory.CreateDirectory(textBoxSatellitePath.Text);
}
btnStart.Enabled = true;
}
else
{
btnStart.Enabled = false;
}
the SaveLoadFiles class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Weather
{
public class SaveLoadFiles
{
public static void SaveFile(string contentToSave, string fileName)
{
string applicationPath = Path.GetFullPath(System.AppDomain.CurrentDomain.BaseDirectory); // the directory that your program is installed in
string saveFilePath = Path.Combine(applicationPath, fileName);
File.WriteAllText(saveFilePath, contentToSave);
}
public static void LoadFile(TextBox loadTo, string fileName)
{
string applicationPath = Path.GetFullPath(System.AppDomain.CurrentDomain.BaseDirectory); // the directory that your program is installed in
string saveFilePath = Path.Combine(applicationPath, fileName); // add a file name to this path. This is your full file path.
if (File.Exists(saveFilePath))
{
loadTo.Text = File.ReadAllText(saveFilePath);
}
}
}
}
when i used the application before and backed it up on my usb flash drive the second hard drive letter was D i had two hard disks : C and D and the project and the folders were on drive D.
now i backed up the project including the saved files but now my hard disks letters are C and E there is no D
but in the constructor when it's reading the text files the folders in the text files are D:....etc
but it should be E:
I'm checking if the folder exist or not and then if not creating it but it's trying to create the folder on drive D and D is not existing.
You are reading contents of a data file that has a file path that no longer exists.
The solution is to edit those data files: "radarpath.txt" and "satellitepath.txt" to have the proper path.
An application would normally provide a UI for selecting the folder to use, rather than saving a hardcoded path in a datafile. What you could do is use FileDialog to prompt the user for the directories to use if they don't exist.
I have a Windows Forms application that uses Selenium. I have multiple production clients that need to run this application and I’ve noticed that in every new client (and also when I need to update the webdriver) I need to copy and paste the .exe to the [PATH] location (%USERPROFILE%\AppData\Local\Microsoft\WindowsApps) and I want to automate that with the setup file that gets generated by Visual Studio every time I publish the application.
I’ve found that you can install an extension called “Microsoft Visual Studio Installer Project”, include the .exe file on it and either make a new Form that’ll check if the webdriver is in place and if it’s not to copy it, or I can change the [PATH] of my IWebDriver object in order to reflect the new path of this file. As a bonus you can also add the the desktop icon.
But first I want to know if there’s a way to publish this webdriver.exe file to it’s proper address through the “Publish wizard” parameters before I start looking for workarounds.
This worked for my use case, for context, I'm using a windows forms project targeting .NET (framework) 4.7.1. these are snippets from my events "load" and "show" formated as a different function. I only included the logic behind the file check, download and unzip with overwite. Since the System.IO.Compression.ZipFile class for this version of .NET doesn't natively support overwrite files, I used Ionic's DotNetZip package downloaded from NuGet.
using Ionic.Zip;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
private void DriverCheck(){
string edge, edgeVersion, edgeDriverPath, edgeDriver, edgeDriverVersion;
edge = #"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
edgeVersion = FileVersionInfo.GetVersionInfo(edge).FileVersion;
edgeDriverPath = Environment.GetEnvironmentVariable("LocalAppData") + "\\Microsoft\\WindowsApps\\";
edgeDriver = edgeDriverPath + "msedgedriver.exe";
try
{
edgeDriverVersion = FileVersionInfo.GetVersionInfo(edgeDriver).FileVersion;
}
catch
{
edgeDriverVersion = null;
}
if (!File.Exists(edgeDriver) || edgeVersion != edgeDriverVersion)
{
try
{
using (var client = new WebClient())
{
string winver;
if (Environment.Is64BitProcess)
{
winver = "64";
}
else
{
winver = "32";
}
string zipPath = edgeDriverPath + "edgedriver_win64.zip";
client.DownloadFile("https://msedgedriver.azureedge.net/" + edgeVersion + "/edgedriver_win" + winver + ".zip", zipPath);
using (ZipFile zip = ZipFile.Read(zipPath))
{
foreach (ZipEntry temp in zip)
{
temp.Extract(edgeDriverPath, ExtractExistingFileAction.OverwriteSilently);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error downloading webdriver:\n" + ex.Message);
Application.Exit();
}
}
}
What i want to do here was getting an string input from the user and if that string input is in the array i want to delete it from the file (all the items in the array is actual files in my computer that got scanned at the start of the program and become one array) is there a way to do that without foreach?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.IO;
using System.Threading;
string typed = null;
string loc = AppDomain.CurrentDomain.BaseDirectory;
if (!Directory.Exists(loc + #"\shortcuts"))
{
Directory.CreateDirectory(loc + #"\shortcuts");
}
string[] directory = Directory.GetFiles(loc + #"\shortcuts");
foreach (var filed in directory)
{
File.Move(filed, filed.ToLowerInvariant());
}
string[] file = Directory.GetFiles(loc + #"\shortcuts").Select(System.IO.Path.GetFileNameWithoutExtension).ToArray();
foreach (string dir in directory)
{
}
if (typed == "exit") System.Environment.Exit(0);
//other ifs here
else if (typed == "rem")
{
//Console.WriteLine("\nNot available at the moment\n");
////add this command
Console.WriteLine("\nWhich program entry do you wish to erase?\n");
typed = Console.ReadLine().ToLower();
if (file.Any(typed.Contains))
{
File.Delete(file.Contains(typed)); //this is the broken part and i don't know how i can get the stings from there
Console.WriteLine("hi");
}
else Console.WriteLine("\n" + typed + " is not in your registered programs list.\n");
}
Expected result was getting rid of the typed program in the folder and actual results was just an error code.
You are storing only the file name in the array, not its complete path or extension. You need to change this, and allow it to store FileName with extension.
string[] file = Directory.GetFiles(loc + #"\shortcuts").Select(System.IO.Path.GetFileName).ToArray();
and then, you need to change the If condition as follows.
if (file.Contains(typed))
{
File.Delete(Path.Combine(loc + #"\shortcuts",typed));
Console.WriteLine("hi");
}
In this Scenario, user would need to input the file name with extension.
If you want the User to input only the filename(without extension, as in your code), then, you could run into a situation where there could be two files with different extension.
"test.jpg"
"test.bmp"
Update
Based on your comment that you cannot store extensions, please find the updated code below. In this scenario, you do not need to change the array. Since you are only storing lnk files, you can append the extension to the file name to complete the path during Path.Combine.
if (file.Contains(typed))
{
File.Delete(Path.Combine(loc , #"shortcuts",$"{typed}.lnk"));
Console.WriteLine("hi");
}
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 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(); }
}
}