What is difference between File.Exists("") and FileInfo exists - c#

I have an *.exe file in \ProgramFiles(x86)\MyAppFolder.
In x86 application I check if the file exists (64 bit system).
simple:
bool fileExists = File.Exists(#"\ProgramFiles(x86)\MyAppFolder\Manager.exe");
The result is:
"fileExists == false" (the file is really there). It's Virtualization as I understand.That issue described here Its ok.
But next code:
bool fileExists = new FileInfo("\\Path").Exists;
"fileExists == true"
Why is the result different in 1st and 2nd cases?
var controller = new ServiceController(Product.ServiceName);
_manager.Enabled = controller.Status == ServiceControllerStatus.Running;
var info = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
var s = File.Exists(#"D:\TFS\GL_SOURCES\Teklynx_LPM\Dev\Server\Debug\Manager.exe");
string pathToManager = string.Empty;
if (info.Parent != null)
{
var pathToModule = info.Parent.FullName;
pathToManager = Path.Combine(pathToModule,"Manager.exe").Replace(" ",string.Empty);
}
//works good
var fileInfo = new FileInfo(pathToManager);
var managerSeparator = new ToolStripSeparator()
{
Visible = _manager.Visible = fileInfo.Exists // true
};
//Does not work
var managerSeparator = new ToolStripSeparator()
{
Visible = _manager.Visible = File.Exists(pathToManager ) // false
};
Thanks!

This is about the only difference and it has more to do with the nature of FileInfo:
FileInfo fileInfo = new FileInfo("myFile.txt"); // non-existent file
Console.WriteLine(fileInfo.Exists); // false
File.Create("myFile.txt");
Console.WriteLine(File.Exists("myFile.txt")); // true
Console.WriteLine(fileInfo.Exists); // false
So as you can see the value of fileInfo.Exists is cached the first time you use it.
Other than that, they do the same thing behind the scenes.

There is no difference, these methods use the exact same internal helper method inside the .NET Framework. Something you can see with a decompiler or the Reference Source source code, the helper method name is File.FillAttributeInfo().
Having duplication like this in the .NET Framework is pretty unusual, not exactly a Good Thing to have more than one way to accomplish the same thing. The File class is however special, it got added after a usability study conducted when .NET 1.0 shipped. The test subjects just had the basic BCL classes to work with, like FileStream and FileInfo, and otherwise only had MSDN documentation available. The test results were not very good, the File class got added to help programmers fall in the pit of success writing very basic file manipulation code. Like File.Exists() and File.ReadAllLines().
So it doesn't have anything to do with the classes, you are just using them wrong. Like not actually using the same path. Do go easy on the forward slashes, the mapping to backward slashes happens inside Windows and is inconsistently implemented in other code. Using // certainly doesn't do what you hope it does.

I've replicated your scenario using the below Linqpad script
var f = #"C:\Program Files (x86)\MyAppFolder\manager.exe";
bool fileExists = File.Exists(f);
bool fileInfoExists = new FileInfo(f).Exists;
fileExists.Dump();
fileInfoExists.Dump();
Ran this both when the file existed and when it did not and both produced the same output each time.
Maybe try this on your system and see if you still see differences.

in your first case, the file path is incorrect, you need spaces in "Program Files (x86)".
Secondly, the Path.Combine will return a Directory path so you'll end up with something like "C:\Program Files (x86)\MyAppFolder\Manager.exe\"
so it's a bad idea.
Both methods work the same way, so make sure you check that the path is correct.

The difference between File.Exists() and new FileInfo().Exists on it's behavior when full path (directory name + file name) is long:
var f = #"C:\Program Files (x86)\MyAppFolder\many_subfolders\manager.exe";
//f.length > 260 characters
bool fileExists = File.Exists(f); //return false, even if the file exists
// Throw exception: "The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
bool fileInfoExists = new FileInfo(f).Exists;

I just found this thread and wanted to update it as I had an issue with FileInfo vs File.Exists.
Let's take a scenario where we create a fileinfo object for a file that currently doesn't exist on a UNC share.
bool fileInfo = new FileInfo(#"\\uncshare\name\filename.txt");
At this point, the file does not exist, but if I create it using another tool (other code or outside my app) and then do this...
fileInfo.Refresh();
bool exists = fileInfo.Exists;
The result is false, it does not exist, even though it does and the fileInfo was refreshed.
To get the correct result requires..
bool exists = File.Exists(f);
Hope that helps others.

Related

Parsing string containing Special Folder

I've allowed for custom paths to be entered and wanted the default to be something along the lines of:
%UserProfile%/Documents/foo, of course this needs to successfully parse the string and though it will work in Windows Explorer, I was wondering if I'm missing a library call or option for parsing this correctly.
DirectoryInfo's constructor certainly doesn't work, treating %UserProfile% like any other folder name.
If there is no good way, I'll manually parse it to substitute %foo% with the actual special folder location if it is in the Special Folders Enumeration.
Edit:
Code that does what I'm looking for (though would prefer a proper .NET library call):
var path = #"%UserProfile%/Documents/foo";
var specialFolders = Regex.Matches(path, "%(?<possibleSpecial>.+)%");
foreach (var spec in specialFolders.AsEnumerable())
{
if (Enum.TryParse<Environment.SpecialFolder>(spec.Groups["possibleSpecial"].Value, out var sf))
{
path = Regex.Replace(path, spec.Value, Environment.GetFolderPath(sf));
}
}
Use Environment.ExpandEnvironmentVariables on the path before using it.
var pathWithEnv = #"%UserProfile%/Documents/foo";
var path = Environment.ExpandEnvironmentVariables(pathWithEnv);
// your code...

Issue in file access with absolute paths

I've created a .NetCore console application in which am trying to check if a file exists using its absolute path, but am facing issues, I always get a false response even though the file exists. Even though I pass absolute path as parameter to API, it always prefixes the current working directory, so the path gets evaluated as doesn't exists.
I'm running this code on a windows 10 desktop and the application is created using .NetCore 2.1. I've tried various different methods to evaluate the existence of file like FileInfo Class instance and File.Exists static method. They've failed so far. I've diagnosed the issue, but I couldn't find a way to fix it.
using System;
using System.IO;
namespace FileAccess
{
class Program
{
static void Main(string[] args)
{
FileInfo fileInfo = new FileInfo(#"‪D:\ScriptData\test.zip");
Console.WriteLine($"Full Name: {fileInfo.FullName}");
Console.WriteLine($"FileInfo.Exists: {fileInfo.Exists}");
Console.Write($"File.Exists with #: {File.Exists(#"‪D:\ScriptData\test.zip")}")
Console.ReadLine();
}
}
}
The output of the code is:
Full Name: D:\Work\Samples\FileAccess\FileAccess\bin\Debug\netcoreapp2.1\?D:\ScriptData\test.zip
False
False
Even though am passing the absolute path, it prefixes the current working directory to the path I've passed. I've checked the Access to the file, its all fine, still I get false as response for both the cases.
Screenshot of Error
Screenshot of Debug Info
Judging your screen shot and the output, there is an invisible character at the start of the file path. That will cause .NET not to recognize it is an absolute path and automatically it will make it an absolute path itself.
If you use this code, you will notice that the inserted ? causes the problem here:
System.IO.FileInfo fi = new System.IO.FileInfo(#"?D:\some_file.ext");
Which outputs: C:\Users\...\ConsoleApp8\bin\Debug\netcoreapp2.2\?D:\some_file.ext.
Instead of:
System.IO.FileInfo fi = new System.IO.FileInfo(#"D:\some_file.ext");
Which outputs: D:\some_file.ext.
If you put your code in a HEX editor, you will see there is indeed a character before D:.
Thank goodness you cut and paste your original code! I know you did because when I cut and paste your code I can see that you have invisible characters after the open quote and before the D:\.
These two lines look identical but they're not! Cut and paste them if you don't believe me!
Your code:
FileInfo fileInfo = new FileInfo(#"‪D:\ScriptData\test.zip");
Fixed code:
FileInfo fileInfo = new FileInfo(#"D:\ScriptData\test.zip");
Here's what the binary editor shows.
You've got E2 80 AA secretly stuck in your source code file at the beginning of your filename. Which happens to be the UTF-8 representation of the LEFT-TO-RIGHT EMBEDDING character.

Find Particular File Types

I've encountered an peculiar issue when utilizing System.IO. When you iterate through a directory for a file with a type of File, without an extension, the file isn't detected.
// Successful:
var files = DirectoryInfo(server.Path).GetFiles("sitemap*.*);
var contents = Directory.GetFiles(server.Path, "sitemap*.*", ...);
The above code would suffice in most instances, however if you have other types with identical name, they'll be collected as well.
Our issue is encountered when you only want the sitemap.file.
// Invalid Code:
var files = DirectoryInfo(server.Path).GetFiles("sitemap*.file);
var contents = Directory.GetFiles(server.Path, "sitemap*.file", ...);
var examples = DirectoryInfo(server.Path).GetFiles("sitemap*);
The array is empty, it doesn't find any of the raw .file extension files. I'm assuming the issue occurs because it doesn't actually have an extension.
How do you circumvent this limitation?
Update: I know I could do something along these lines with FileInfo[], but was hoping to find a more simple approach then iteration, then compare with:
var files = DirectoryInfo(server.Path).GetFiles("sitemap*.*);
foreach(var file in files)
if(file.Extension != ".gz" && file.Extension != ".xml")
{
// Do something with File.
}
Especially if you have a wide assortment of extensions within your directory. You would think it would account for such a type, or lack there of.
I believe you are looking to search files starting with sitemap and doesn't have any extension. Use "sitemap*." pattern.
var contents = Directory.GetFiles(server.Path, "sitemap*.");
Notice the last dot (.) in the pattern, that specifies to get those files which doesn't have any extension associated with it.
This will give you files like:
sitemap1
sitemap2
and will exclude files like:
sitemap1.gz
sitempa2.gz
The file doesn't have an extension. Configure your Explorer to not hide extensions and you'll see.
If you're only looking for extensionless files, change your if to:
if(string.IsNullOrEmpty(file.Extension))

How to get the newest (last modified) directory [C#]

Currently my application uses string[] subdirs = Directory.GetDirectories(path) to get the list of subdirectories, and now I want to extract the path to the latest (last modified) subdirectory in the list.
What is the easiest way to accomplish this?
(efficiency is not a major concern - but robustness is)
Non-recursive:
new DirectoryInfo(path).GetDirectories()
.OrderByDescending(d=>d.LastWriteTimeUtc).First();
Recursive:
new DirectoryInfo(path).GetDirectories("*",
SearchOption.AllDirectories).OrderByDescending(d=>d.LastWriteTimeUtc).First();
without using LINQ
DateTime lastHigh = new DateTime(1900,1,1);
string highDir;
foreach (string subdir in Directory.GetDirectories(path)){
DirectoryInfo fi1 = new DirectoryInfo(subdir);
DateTime created = fi1.LastWriteTime;
if (created > lastHigh){
highDir = subdir;
lastHigh = created;
}
}
Try this:
string pattern = "*.txt"
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern)
orderby f.LastWriteTime descending
select f).First();
http://zamirsblog.blogspot.com/2012/07/c-find-most-recent-file-in-directory.html
Be warned: You might need to call Refresh() on your Directory Info object to get the correct information:
e.g. in Laramie's answer you'd edit to:
DirectoryInfo fi1 = new DirectoryInfo(subdir);
fi1.Refresh();
DateTime created = fi1.LastWriteTime;
Otherwise you might get outdated info like I did:
"Calls must be made to Refresh before attempting to get the attribute
information, or the information will be outdated."
http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh(v=vs.71).aspx
You can use Directory.GetLastWriteTime (or Directory.GetLastWriteTimeUtc, it doesn't really matter in this case when you're just doing relative comparisons).
Although do you just want to look at the "modified" time as reported by the OS, or do you want to find the directory with the most recently-modified file inside it? They don't always match up (that is, the OS doesn't always update the containing directory "last modified" time when it modifies a file).
If you are building a windows service and you want to be notified when a new file or directory is created you could also use a FileSystemWatcher. Admittedly not as easy, but interesting to play with. :)

Securely enforcing user-input file paths within subdirectories

I know the solid security recommendation of avoiding accepting user input that you then use to choose a path to read/write a file. However, assuming you have a base directory you want to keep within (such as the root of an ftp folder), how do you best ensure that a given user input keeps us within that folder?
For instance,
Path.Combine(_myRootFolder, _myUserInput)
could still take us outside of _myRootFolder. And this could also be dodgy
newPath = Path.Combine(_myRootFolder, _myUserInput)
if (newPath.StartsWith(_myRootFolder))
...
given something like "/back/to/myrootfolder/../../and/out/again" from the user. What are the strategies for this? Am I missing a blindingly obvious .NET method I can use?
Within ASP.NET applications you can use Server.MapPath(filename) which will throw an exception if the path generated goes outside of your application root.
If all you want is a safe file name and you just want all files in there it becomes simpler;
FileInfo file = new FileInfo(
Server.MapPath(
Path.Combine(#"c:\example\mydir", filename)));
If you're outside of ASP.NET like you indicate then you could use Path.GetFullPath.
string potentialPath = Path.Combine(#"c:\myroot\", fileName);
if (Path.GetFullPath(potentialPath) != potentialPath)
// Potential path transversal
Or you call Path.GetFullPath and then check the start of it matches the directory you want locked to.
I know, that this thread is quiet old, but to prevent following readers from writing code with potential security errors, I think I should point out, that using Path.Combine(arg1, arg2) isn't save when arg2 is directly based on user input.
When arg2 is for example "C:\Windows\System32\cmd.exe" the arg1 parameter will be completely ignored and you grant the users of your API or server application full access to the whole file system.
So please be very careful with using this method!
I came up with this solution that should (afaik) be secure:
public static string SecurePathCombine(params string[] paths)
{
string combinedPath = "";
foreach (string path in paths)
{
string newPath = Path.Combine(combinedPath, path);
if (!newPath.StartsWith(combinedPath))
return null;
combinedPath = newPath;
}
if (Path.GetFullPath(combinedPath) != combinedPath)
return null;
return combinedPath;
}
Edit: There is a new Path.Join() method now. Please use that one instead of the code above.
I believe Path.FullPath will do what you need (I didn't test this though):
string newPath = Path.Combine(_myRootFolder, _myUserInput);
string newPath = Path.FullPath(newPath);
if (newPath.StartsWith(_myRootFolder)) ...
Well, in your example of an FTP server, you should set the users home-directory, and permissions appropriately, such that they can't navigate out of the folder. Any reason you can't do that?
You can parse input string and cut ../ with regex.

Categories

Resources