Given a command-line style path to a command such as bin/server.exe or ping, how can I get the full path to this executable (as cmd or Process.Start would resolve it)?
I tried Path.GetFullPath, but it always expands relative to the working directory. It expands bin/server.exe correctly, however given ping it returns c:\users\matt\ping (non-existent). I want c:\Windows\system32\ping.exe.
Edit: I would like the same behaviour as cmd. Some considerations:
When there is a local executable with the same name as one in the path, cmd prefers the local one
cmd can expand the command server to server.bat or server.exe (adding the file extension)
I also tried Windows' command-line tool called where . It does almost I want:
Displays the location of files that match the search pattern. By default, the search is done along the current directory and in the paths specified by the PATH environment variable.
>where ping
C:\Windows\System32\PING.EXE
>where bin\server
INFO: Could not find files for the given pattern(s).
(This question is hard to search around because of the two different meanings of the word 'path')
Considering PATHEXT too, stealing from Serj-Tm's answer (sorry! +1 to him):
public static string WhereSearch(string filename)
{
var paths = new[]{ Environment.CurrentDirectory }
.Concat(Environment.GetEnvironmentVariable("PATH").Split(';'));
var extensions = new[]{ String.Empty }
.Concat(Environment.GetEnvironmentVariable("PATHEXT").Split(';')
.Where(e => e.StartsWith(".")));
var combinations = paths.SelectMany(x => extensions,
(path, extension) => Path.Combine(path, filename + extension));
return combinations.FirstOrDefault(File.Exists);
}
Sorry the indentation's a bit all-over-the-place - I was trying to make it not scroll. I don't know if the StartsWith check is really necessary - I'm not sure how CMD copes with pathext entries without a leading dot.
public static string GetFullPath(string filename)
{
return new[]{Environment.CurrentDirectory}
.Concat(Environment.GetEnvironmentVariable("PATH").Split(';'))
.Select(dir => Path.Combine(dir, filename))
.FirstOrDefault(path => File.Exists(path));
}
If you're only interested in searching the current directory and the paths specified in the PATH environment variable, you can use this snippet:
public static string GetFullPath(string fileName)
{
if (File.Exists(fileName))
return Path.GetFullPath(fileName);
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(';'))
{
var fullPath = Path.Combine(path, fileName);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
You have to search the entire disk.
Windows can respond to things like, iexplore, ping, cmd, etc, because they are in the registry under this key:
HKEY_LOCAL_MACHINE
SOFTWARE
Microsoft
Windows
CurrentVersion
App Paths
The only other way is to search the entire disk for the application.
EDIT: My understanding was, that you want to search for any random executable name, not the ones that are already known to Windows..
internal class Program
{
static void Main(string[] args)
{
string fullPath = GetExactPathFromEnvironmentVar("ping.exe");
if (!string.IsNullOrWhiteSpace(fullPath))
Console.WriteLine(fullPath);
else
Console.WriteLine("Not found");
}
static string GetExactPathFromEnvironmentVar(string program)
{
var pathVar = System.Environment.GetEnvironmentVariable("PATH");
string[] folders = pathVar.Split(';');
foreach (var folder in folders)
{
string path = Path.Combine(folder, program);
if (File.Exists(path))
{
return path;
}
}
return null;
}
}
HTH
Related
I am trying to get the path of application "WinMergeU.exe" using Path.GetFullPath() but the return value of this code is the folder of my application.
How can i get the fullpath of the application in C: drive while the return value is d using the Path.GetFullPath().
You have to search for the file, e.g.
using system.Linq;
...
// Either full path of "WinMergeU.exe" file or null (if not found)
string result = Directory
.EnumerateFiles(#"c:\", "WinMergeU.exe", SearchOption.AllDirectories)
.FirstOrDefault();
Edit: In case the application is in the C:\Program Files (see comments) we can restrict the scan:
string result = Directory
.EnumerateFiles(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"WinMergeU.exe",
SearchOption.AllDirectories)
.FirstOrDefault();
Or (if we quite sure in c:\Program Files\WinMerge path) we can just test if the file exits:
string result = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"WinMerge",
"WinMergeU.exe");
if (File.Exists(result)) {
// we have the file
}
I installed it, as it looked like a handy tool, and searched the registry.
string key = #"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WinMerge.Project.File\shell\open\command";
string path = Microsoft.Win32.Registry.GetValue(key, "", -1);
... seems to contain the value you need. In my case:
"C:\Program Files (x86)\WinMerge\WinMergeU.exe" "%1"
The method will return -1 if the key is not found.
WinMerge doesn't allow to change its installation folder AFAIK, therefore searching the whole drive C is an overkill.
This will suffice:
static string GetWinMergeFullPath()
{
const string executableName = "WinMergeU.exe";
const string installationFolder = "WinMerge";
var locations = new Environment.SpecialFolder[]
{
Environment.SpecialFolder.ProgramFiles,
Environment.SpecialFolder.ProgramFilesX86
};
string fullPath = locations
.Select(l => Path.Combine(Environment.GetFolderPath(l), installationFolder, executableName))
.Where(File.Exists)
.FirstOrDefault();
return fullPath ?? throw new FileNotFoundException(executableName);
}
EDIT
As it was commented the above won't suffice:). The below version uses two registry locations that should contain the path. One is "App Paths" which is provided by the installation, the second one is "Uninstall". While I believe that "App Paths" should be enough I still provided the second one for robustness.Note that it will work even if shell integration is unchecked in the installer! Both 32-bit and 64-bit installations are covered.
static string GetWinMergeFullPathFromRegistryEx()
{
const string executableName = "WinMergeU.exe";
const string appPathKeyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\";
const string uninstallKeyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinMerge_is1";
const string installLocationName = "InstallLocation";
var locations = new List<(string path, string value, bool withFilename)>
{
($"{appPathKeyName}{executableName}", null, true),
(uninstallKeyName, installLocationName, false),
};
List<RegistryView> views = Environment.Is64BitOperatingSystem ?
new List<RegistryView>() { RegistryView.Registry32, RegistryView.Registry64 } :
new List<RegistryView>() { RegistryView.Default };
foreach (var view in views)
{
using (RegistryKey localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
foreach (var (path, value, withFilename) in locations)
{
using (RegistryKey key = localMachine.OpenSubKey(path, false))
{
var fullpathValue = key?.GetValue(value);
if (fullpathValue != null)
{
string fullpath = (string)fullpathValue;
if (!withFilename)
fullpath = Path.Combine(fullpath, executableName);
if (File.Exists(fullpath))
return fullpath;
}
}
}
}
throw new FileNotFoundException(executableName);
}
Of course my assumption is that WinMerge was actually installed. If you should support cases when WinMerge was deployed manually then you still can use to the full scan option as a fallback.
I've a part of a string path: "\MVVM\MyFirstTest2016\MyFirstTest\bin\Debug\MyFirstTest.exe"
I want to search the above path in C: and need to get the complete full path of the directory.
I tried with Path.GetFullPath("") and other in-built methods but didnt get complete full path.
Here's the code:
The sourceDir will be the full path.
string defaultFolder = System.AppDomain.CurrentDomain.BaseDirectory;
string navigateToFolder = "\\MVVM\\MyFirstTest2016\\MyFirstTest\\bin\\Debug\\MyFirstTest.exe";
string sourceDir = Path.Combine(defaultFolder, navigateToFolder);
It sounds like your problem is similar to this question. You've got a partial path, but no idea where it is on this drive. The naive approach would be as follows.
You're first step would be to start at the root of the drive, and get the list of directories:
string[] dirs = Directory.GetDirectories(#"c:\", "*");
You'd then check if any of these strings matched the first directory of your root path (MVVM). If it does, you'd go into that folder and check if it contained the next directory. If it does, check the next and next, etc. until you've exhausted the path.
If not, you'd iterate over the directories, and run the same logic: get the directories, check if any match your first folder, etc.
So a bit of pseudo code would look like:
string directoryPath = "\MVVM\MyFirstTest2016\MyFirstTest\bin\Debug\MyFirstTest.exe"
string[] splitPath = directoryPath.split("\")
check("c:\")
public void check(string directory)
string[] directories = Directory.GetDirectories(#directory, "*")
if(checkDirectories(directories, splitPath))
// Success!
else
for(string subDirectory : directories)
string newDirectory = Path.combine(directory, subDirectory)
check(newDirectory)
public boolean checkDirectories(string[] directories, string[] splitPath)
// Horrible, but just for example - finding the file at the end
if(splitPath.size == 1)
// Get file list in current directory and check the last part of splitPath
if(directories.contains(splitPath[0])
// Recursively call checkDirectories with the sub directories of this folder, an splitPath missing the first item. This can be done using Array.Copy(splitPath, 1, newArray, 0)
Obviously that's nowhere near runnable, but it should give you the basic idea. The other question I linked earlier also has an accepted answer which will help more.
You could iterate over all directories and check if the sub directory is available.
The normal Directory.EnumerateDirectories may throw a UnauthorizedAccessException which stops the process of finding the directory.
So you could write your own EnumerateDirectories as shown below.
The example provided will return the folders found.
void Main()
{
string path = #"temp\A\B";
var parts = path.Split(new [] { Path.DirectorySeparatorChar });
var rootPath = "c:\\";
foreach(var result in DirectoryEx.EnumerateDirectories(rootPath, parts.First()))
{
var checkPath = Path.Combine(result, String.Join(""+Path.DirectorySeparatorChar, parts.Skip(1).ToArray()));
if (Directory.Exists(checkPath))
Console.WriteLine("Found : " + checkPath);
}
}
public class DirectoryEx
{
public static IEnumerable<string> EnumerateDirectories(string dir, string name)
{
IEnumerable<string> dirs;
// yield return may not be used in try with catch.
try { dirs = Directory.GetDirectories(dir); }
catch(UnauthorizedAccessException) { yield break; }
foreach (var subdir in dirs)
{
if(Path.GetFileName(subdir).Equals(name, StringComparison.OrdinalIgnoreCase))
yield return subdir;
foreach(var heir in EnumerateDirectories(subdir, name))
yield return heir;
}
}
}
In my case results in :
Found : c:\dev\temp\A\B
Found : c:\Temp\A\B
After going through all the related stuff to copying files i am unable to find an answer to my
problem of an exception occurring while i was trying to copy a file to an empty folder in WPF application. Here is the code snippet.
public static void Copy()
{
string _finalPath;
foreach (var name in files) // name is the filename extracted using GetFileName in a list of strings
{
_finalPath = filePath; //it is the destination folder path e.g,C:\Users\Neha\Pictures\11-03-2014
if(System.IO.Directory.Exists(_finalPath))
{
_finalPath = System.IO.Path.Combine(_finalPath,name);
System.IO.File.Copy(name, _finalPath , true);
}
}
}
While debugging exception is occuring at file.copy() statement which says
"FileNotFoundException was unhandled" could not find file
i already know about the combining path and other aspects of copy but i dont know why this exception is being raised.
I am a noob to WPF please help.........
Use following code:
public static void Copy()
{
string _finalPath;
var files = System.IO.Directory.GetFiles(#"C:\"); // Here replace C:\ with your directory path.
foreach (var file in files)
{
var filename = file.Substring(file.LastIndexOf("\\") + 1); // Get the filename from absolute path
_finalPath = filePath; //it is the destination folder path e.g,C:\Users\Neha\Pictures\11-03-2014
if (System.IO.Directory.Exists(_finalPath))
{
_finalPath = System.IO.Path.Combine(_finalPath, filename);
System.IO.File.Copy(file, _finalPath, true);
}
}
}
The GetFileName ()
only returns the actual name of the file (drops the path) what you want is a full path to the file. So you getting an exception because the 'name' does not exist on your drive (path is unknown)
You're variable, name, is most likely just the file name (i.e. something.jpg). When you use the File.Copy(...) method, if you do not supply an absolute path the method assumes a path relative to the executable.
Basically, if you are running your app in, for example, C:\Projects\SomeProject\bin\Debug\SomeProject.exe, then it is assuming your file is C:\Projects\SomeProject\bin\Debug\something.jpg.
One option would be to do System.IO.Directory.GetParent() a few times. Is there a more graceful way of travelling a few folders up from where the executing assembly resides?
What I am trying to do is find a text file that resides one folder above the application folder. But the assembly itself is inside the bin, which is a few folders deep in the application folder.
Other simple way is to do this:
string path = #"C:\Folder1\Folder2\Folder3\Folder4";
string newPath = Path.GetFullPath(Path.Combine(path, #"..\..\"));
Note This goes two levels up. The result would be:
newPath = #"C:\Folder1\Folder2\";
Additional Note
Path.GetFullPath normalizes the final result based on what environment your code is running on windows/mac/mobile/...
if c:\folder1\folder2\folder3\bin is the path then the following code will return the path base folder of bin folder
//string directory=System.IO.Directory.GetParent(Environment.CurrentDirectory).ToString());
string directory=System.IO.Directory.GetParent(Environment.CurrentDirectory).ToString();
ie,c:\folder1\folder2\folder3
if you want folder2 path then you can get the directory by
string directory = System.IO.Directory.GetParent(System.IO.Directory.GetParent(Environment.CurrentDirectory).ToString()).ToString();
then you will get path as c:\folder1\folder2\
You can use ..\path to go one level up, ..\..\path to go two levels up from path.
You can use Path class too.
C# Path class
This is what worked best for me:
string parentOfStartupPath = Path.GetFullPath(Path.Combine(Application.StartupPath, #"../"));
Getting the 'right' path wasn't the problem, adding '../' obviously does that, but after that, the given string isn't usable, because it will just add the '../' at the end.
Surrounding it with Path.GetFullPath() will give you the absolute path, making it usable.
public static string AppRootDirectory()
{
string _BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
return Path.GetFullPath(Path.Combine(_BaseDirectory, #"..\..\"));
}
Maybe you could use a function if you want to declare the number of levels and put it into a function?
private String GetParents(Int32 noOfLevels, String currentpath)
{
String path = "";
for(int i=0; i< noOfLevels; i++)
{
path += #"..\";
}
path += currentpath;
return path;
}
And you could call it like this:
String path = this.GetParents(4, currentpath);
C#
string upTwoDir = Path.GetFullPath(Path.Combine(System.AppContext.BaseDirectory, #"..\..\"));
The following method searches a file beginning with the application startup path (*.exe folder). If the file is not found there, the parent folders are searched until either the file is found or the root folder has been reached. null is returned if the file was not found.
public static FileInfo FindApplicationFile(string fileName)
{
string startPath = Path.Combine(Application.StartupPath, fileName);
FileInfo file = new FileInfo(startPath);
while (!file.Exists) {
if (file.Directory.Parent == null) {
return null;
}
DirectoryInfo parentDir = file.Directory.Parent;
file = new FileInfo(Path.Combine(parentDir.FullName, file.Name));
}
return file;
}
Note: Application.StartupPath is usually used in WinForms applications, but it works in console applications as well; however, you will have to set a reference to the System.Windows.Forms assembly. You can replace Application.StartupPath by
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) if you prefer.
I use this strategy to find configuration and resource files. This allows me to share them for multiple applications or for Debug and Release versions of an application by placing them in a common parent folder.
Hiding a looped call to Directory.GetParent(path) inside an static method is the way to go.
Messing around with ".." and Path.Combine will ultimately lead to bugs related to the operation system or simply fail due to mix up between relative paths and absolute paths.
public static class PathUtils
{
public static string MoveUp(string path, int noOfLevels)
{
string parentPath = path.TrimEnd(new[] { '/', '\\' });
for (int i=0; i< noOfLevels; i++)
{
parentPath = Directory.GetParent(parentPath ).ToString();
}
return parentPath;
}
}
this may help
string parentOfStartupPath = Path.GetFullPath(Path.Combine(Application.StartupPath, #"../../")) + "Orders.xml";
if (File.Exists(parentOfStartupPath))
{
// file found
}
If you know the folder you want to navigate to, find the index of it then substring.
var ind = Directory.GetCurrentDirectory().ToString().IndexOf("Folderame");
string productFolder = Directory.GetCurrentDirectory().ToString().Substring(0, ind);
I have some virtual directories and I cannot use Directory methods. So, I made a simple split/join function for those interested. Not as safe though.
var splitResult = filePath.Split(new[] {'/', '\\'}, StringSplitOptions.RemoveEmptyEntries);
var newFilePath = Path.Combine(filePath.Take(splitResult.Length - 1).ToArray());
So, if you want to move 4 up, you just need to change the 1 to 4 and add some checks to avoid exceptions.
Path parsing via System.IO.Directory.GetParent is possible, but would require to run same function multiple times.
Slightly simpler approach is to threat path as a normal string, split it by path separator, take out what is not necessary and then recombine string back.
var upperDir = String.Join(Path.DirectorySeparatorChar, dir.Split(Path.DirectorySeparatorChar).SkipLast(2));
Of course you can replace 2 with amount of levels you need to jump up.
Notice also that this function call to Path.GetFullPath (other answers in here) will query whether path exists using file system. Using basic string operation does not require any file system operations.
I was using hard-coded directory path to Program Files to move file. I would now like to use the correct method to find the folder in Program Files.
I have found this method doing some Googling and it is what i would like to use:
static string ProgramFilesx86()
{
if( 8 == IntPtr.Size || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"))))
{
return Environment.GetEnvironmentVariable("ProgramFiles(x86)");
}
return Environment.GetEnvironmentVariable("ProgramFiles");
}
I unfortunately am not sure how to implement and use this method.
Where do i insert the method in my app?
How do i use the above instead of this:
if (File.Exists(#"C:\PROGRA~1\TEST\ok.txt"))
File.Delete(#"C:\PROGRA~1\TEST\ok.txt");
File.Copy(#"C:\PROGRA~1\PROGRAMFOLDER\ok.txt", #"C:\PROGRA~1\TEST\ok.txt");
It's much easier to get the special folders like Program Files using
Environment.SpecialFolders
string programFilesFolder =
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
Continuing that example you could do something like this
string pathToFile =
Path.Combine(programFilesFolder, #"TEST\ok.txt");
if (File.Exists(pathToFile))
File.Delete(pathToFile);
UPDATE
Modified the code example to always get the 32-bit Program Files folder whether you're running 32- or 64-bit OS as #Mario pointed out that's what your original code was doing.
string fileName = Path.Combine( ProgramFilesx86(), applicationPath, #"ok.txt");
if (File.Exists( fileName ) )
{
File.remove( fileName );
}
string sourceFile = Path.Combine( ProgramFilesx86(), #"\PROGRAMFOLDER", "ok.txt" );
File.Copy( sourceFile, fileName);
Edit:
You should not use this method. The program folder depends on the capability of the programs and not the system! You must know whether they install to the ProgramFiles or ProgramFilesX86 folder.
And then use Eric J.'s answer.
string sourceFolder =
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string source =
Path.Combine(sourceFolder, #"ok.txt");
string targetFolderPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string target = Path.Combine(targetFolderPath, #"ok.txt");
if (File.Exists(source))
File.Delete(source);
File.Copy(targetFolderPath, source);