C# reference to the desktop - c#

I am using a file stream to write out a file.
I was hoping to be able to write the file to the desktop.
If I have something like
tw = new StreamWriter("NameOflog file.txt");
I would like to be able to have some sort of #desktop identified in front of the file name that would automatically insert the path to the desktop.
Does this exist in C#? Or do I have to look for desktop paths on a computer by computer (or OS by OS) basis?

Quick google search reveals this one:
string strPath = Environment.GetFolderPath(
System.Environment.SpecialFolder.DesktopDirectory);
EDIT: This will work for Windows, but Mono supports it, too.

You want to use Environment.GetFolderPath, passing in SpecialFolder.DesktopDirectory.
There's also SpecialFolder.Desktop which represents the logical desktop location - it's not clear what the difference between the two is though.

Something like:
string logPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
"NameOflog file.txt");
tw = new StreamWriter(logPath);

You want Environment.SpecialFolder
string fileName = "NameOflog file.txt";
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName);
tw = new StreamWriter(path);

yep.
you can use environmental variables.
like
tw = new StreamWriter("%USERPROFILE%\Desktop\mylogfile.txt");
but i would not recommend to automatically write a log file to the users desktop.
you should add the link to the file to your start menu folder.
or even populate them in the event log. (much better)

Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory))

I also use the method mentioned above.
But here are a couple different options that work too (just to have a more comprehensive list):
using System;
using System.Runtime.InteropServices;
using System.Text;
class Program
{
// 1st way
private const int MAX_PATH = 260;
private const int CSIDL_DESKTOP = 0x0000;
private const int CSIDL_COMMON_DESKTOPDIRECTORY = 0x0019; // Can get to All Users desktop even on .NET 2.0,
// where Environment.SpecialFolder.CommonDesktopDirectory is not available
[DllImport("shell32.dll")]
private static extern bool SHGetSpecialFolderPath(IntPtr hwndOwner, StringBuilder lpszPath, int nFolder, bool fCreate);
static string GetDesktopPath()
{
StringBuilder currentUserDesktop = new StringBuilder(MAX_PATH);
SHGetSpecialFolderPath((IntPtr)0, currentUserDesktop, CSIDL_DESKTOP, false);
return currentUserDesktop.ToString();
}
// 2nd way
static string YetAnotherGetDesktopPath()
{
Guid PublicDesktop = new Guid("C4AA340D-F20F-4863-AFEF-F87EF2E6BA25");
IntPtr pPath;
if (SHGetKnownFolderPath(PublicDesktop, 0, IntPtr.Zero, out pPath) == 0)
{
return System.Runtime.InteropServices.Marshal.PtrToStringUni(pPath);
}
return string.Empty;
}
}
Then:
string fileName = Path.Combine(GetDesktopPath(), "NameOfLogFile.txt");

Related

Is it possible to create a program that can redirect folder icon directory depending on the file location?

Background
Yo, guys. So first off, here is an anime folder that displays all my anime series with different folder icons. All of the said custom icons are located in a hidden folder named "icons". This format is what I've laid out in other folders of their corresponding years.
Problem/Situation
As we all know, when we move/tamper the "icons" folder or the directory of the anime folder, the link gets broken and the custom icons of the anime series disappear. This problem can be remedied by either assigning the custom icons again or renaming the directory.
I tend to copy anime folders to other hard drives. What I want to know if it's possible to create a program that automatically fixes the directory link of the anime folder depending on the location it's in.
For example, let's say I copy the 2018 anime folder from directory "G:\2018" and place it in Desktop. The custom folder icon of, let's say, Slow Start is still G:\2018\icons\Slow_Start_Folder_Icon.ico. The program, when run, will automatically change the directory link to C:\Users****\Desktop\2018\icons\Slow_Start_Folder_Icon.ico. Of course, simultaneously with the other anime folders.
Any enlightening answers are greatly appreciated. Thanks in advance also.
UPDATE 3: This is my current working code, with ideas and advice gotten from #Jimi. Tested and it worked for me.
private static void UpdateIcons() //for updating folder icons to new directory
{
int start, end;
string filedir = Environment.CurrentDirectory, olddir = null, newdir = null;
IEnumerable<string> Ini = Directory.EnumerateFiles(filedir, "desktop.ini", SearchOption.AllDirectories);
foreach (string deskini in Ini)
{
File.SetAttributes(deskini, FileAttributes.Archive);
foreach (var line in File.ReadAllLines(deskini))
{
if (line.Contains("IconResource"))
{
start = line.IndexOf("icons");
Console.WriteLine(line);
Console.WriteLine(line.Length.ToString());
Console.WriteLine(line.IndexOf("icons").ToString());
if (line.Contains(","))
{
end = line.IndexOf(",");
olddir = line.Substring(start, (end - start));
Console.Write(end - start);
}
else
{
end = line.Length - 1;
Console.Write(end);
olddir = line.Substring(start, (end - start + 1));
Console.Write(end - start);
}
newdir = File.ReadAllText(deskini);
newdir = newdir.Replace(line, "IconResource=" + filedir + "\\" + olddir);
File.WriteAllText(deskini, newdir, Encoding.Unicode);
Console.WriteLine(olddir);
Console.WriteLine(newdir);
}
}
File.SetAttributes(deskini, FileAttributes.Hidden);
}
}
The application accepts a command line parameter, where the Icons repository (a folder name) can be specified. If none is provided, it assumes the Icons repository is named "icons", found in the current executing path.
Assembly.GetExecutingAssembly is used to determine the executable path. The Location property returns a standard-formatted Path.
Directory.GetDirectories is used to get the directory listing, including the SubDirectories, of the directories in the current path. The current path and the icons directory is then excluded from the list, since those Folders are not subject to customization.
Directory.GetFiles() is the use to build the list of the icons files contained in the Icons folder.
To update the Folders customization (Explorer can be somewhat lazy when it comes to update these changes: it uses the IconCache.db storage when not otherwise instructed) we PInvoke SHGetSetFolderCustomSettings.
This Shell API creates or updates the desktop.ini file, located inside each customized folder. This file references the Icon used as the Folder Custom Icon.
This function allows a swift update of the folder new aspect. The update is, in this case, immediate. No need to delete the icons cache or restart Explorer.
Related informations:
How to Customize Folders with Desktop.ini
This document is part of The Windows Shell document, The Book of Shell operations.
An old definition of SHFOLDERCUSTOMSETTINGS, which is missing in the official documentation.
Compile this code as Release and copy the exeutable in the upper level folder where the customized sub folders are listed.
NOTE:
In this example, I'm randomly picking the Icon to assign to a folder from the Icons repository (using a Random value). You need define your own logic to assign the right icon to a specific Folder.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
public enum FolderCustomSettingsMask : uint
{
InfoTip = 0x00000004,
Clsid = 0x00000008,
IconFile = 0x00000010,
Logo = 0x00000020,
Flags = 0x00000040
}
public enum FolderCustomSettingsRW : uint
{
Read = 0x00000001,
ForceWrite = 0x00000002,
ReadWrite = Read | ForceWrite
}
[SecurityCritical]
[DllImport("Shell32.dll", CharSet = CharSet.Auto)]
static extern uint SHGetSetFolderCustomSettings(ref SHFOLDERCUSTOMSETTINGS pfcs, string pszPath, FolderCustomSettingsRW dwReadWrite);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct SHFOLDERCUSTOMSETTINGS
{
public uint dwSize;
public FolderCustomSettingsMask dwMask;
public IntPtr pvid;
public string pszWebViewTemplate;
public uint cchWebViewTemplate;
public string pszWebViewTemplateVersion;
public string pszInfoTip;
public uint cchInfoTip;
public IntPtr pclsid;
public uint dwFlags;
public string pszIconFile;
public uint cchIconFile;
public int iIconIndex;
public string pszLogo;
public uint cchLogo;
}
static void Main(string[] args)
{
string CurrentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string IconsFolder = args.Length > 0 ? args[0] : Path.Combine(CurrentPath, #"icons");
UpdateFolderIcons(CurrentPath, IconsFolder);
Console.WriteLine($"Press any key to terminate...");
Console.ReadKey();
}
[SecuritySafeCritical]
public static void UpdateFolderIcons(string BaseFolder, string IconsFolder)
{
Console.WriteLine($"Current path: {BaseFolder}");
Console.WriteLine($"Processing...");
Console.WriteLine();
List<string> ExcludeDirList = new List<string>() {BaseFolder, IconsFolder};
List<string> DirList = Directory.GetDirectories(BaseFolder, "*", SearchOption.AllDirectories).ToList();
DirList = DirList.Except(ExcludeDirList).ToList();
if (DirList.Count == 0) {
Console.WriteLine($"No directories found!");
return;
}
Console.WriteLine($"{DirList.Count} Directories found");
Console.WriteLine("Updating...");
Console.WriteLine();
int UpdateCount = 0;
List<string> IconNames = Directory.GetFiles(Path.Combine(BaseFolder, IconsFolder), "*").ToList();
if (IconNames.Count == 0) {
Console.WriteLine($"No icons found in {IconsFolder}");
return;
}
foreach (string sPath in DirList)
{
string IconName = IconNames[random.Next(0, IconNames.Count)];
SHFOLDERCUSTOMSETTINGS FolderSettings = new SHFOLDERCUSTOMSETTINGS {
dwMask = FolderCustomSettingsMask.IconFile,
pszIconFile = IconName,
iIconIndex = 0
};
uint hResult = SHGetSetFolderCustomSettings(
ref FolderSettings, sPath + char.MinValue, FolderCustomSettingsRW.ForceWrite);
UpdateCount += 1;
}
Console.WriteLine($"{UpdateCount} Files Updated");
Console.WriteLine();
}

Get localized friendly names for all winrt/metro apps installed from WPF application

My WPF application needs to list the localized names of all Metro/WinRT applications installed for the user. I created a repo to store a working sample for the code presented: https://github.com/luisrigoni/metro-apps-list
1) Using PackageManager.FindPackagesForUser() method
var userSecurityId = WindowsIdentity.GetCurrent().User.Value;
var packages = packageManager.FindPackagesForUser(userSecurityId);
foreach (var package in packages)
Debug.WriteLine(package.Id.Name);
}
// output:
// Microsoft.BingFinance
// Microsoft.BingMaps
// Microsoft.BingSports
// Microsoft.BingTravel
// Microsoft.BingWeather
// Microsoft.Bing
// Microsoft.Camera
// microsoft.microsoftskydrive
// microsoft.windowscommunicationsapps
// microsoft.windowsphotos
// Microsoft.XboxLIVEGames
// Microsoft.ZuneMusic
// Microsoft.ZuneVideo
These outputs don't seems too friendly to show to the user...
2) Reading the AppxManifest.xml of each of these apps
var userSecurityId = WindowsIdentity.GetCurrent().User.Value;
var packages = packageManager.FindPackagesForUser(userSecurityId);
foreach (var package in packages)
{
var dir = package.InstalledLocation.Path;
var file = Path.Combine(dir, "AppxManifest.xml");
var obj = SerializationExtensions.DeSerializeObject<Package>(file);
if (obj.Applications != null)
{
foreach (var application in obj.Applications)
{
Debug.WriteLine(application.VisualElements.DisplayName);
}
}
}
// output:
// ms-resource:AppTitle
// ms-resource:AppDisplayName
// ms-resource:BingSports
// ms-resource:AppTitle
// ms-resource:AppTitle
// ms-resource:app_name
// ms-resource:manifestDisplayName
// ms-resource:ShortProductName
// ms-resource:mailAppTitle
// ms-resource:chatAppTitle
// ms-resource:///resources/residTitle
// ms-resource:///strings/peopleAppName
// ms-resource:///photo/residAppName
// ms-resource:34150
// ms-resource:33273
// ms-resource:33270
Definitely not friendly...
Update 1) Increasing above item (2) with SHLoadIndirectString funcion (hint by Erik F)
[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
private static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
static internal string ExtractStringFromPRIFile(string pathToPRI, string resourceKey)
{
string sWin8ManifestString = string.Format("#{{{0}? {1}}}", pathToPRI, resourceKey);
var outBuff = new StringBuilder(1024);
int result = SHLoadIndirectString(sWin8ManifestString, outBuff, outBuff.Capacity, IntPtr.Zero);
return outBuff.ToString();
}
[...]
foreach (var application in obj.Applications)
{
Uri uri = new Uri(application.VisualElements.DisplayName);
var resourceKey = string.Format("ms-resource://{0}/resources/{1}", package.Id.Name, uri.Segments.Last());
Debug.WriteLine(ExtractStringFromPRIFile("<path/to/pri>", resourceKey));
}
[...]
// output:
// Finance
// Maps
// Sports
// Travel
// Weather
// Bing
// Camera
// SkyDrive
// Mail
// Messaging
// Calendar
// People
// Photos
// Games
// Music
// Video
Much, much better. We already have english labels. But how to extract other language resources?
I'm expecting retrieve the same label that is shown on Start Screen for each app, something like "Finanças", "Esportes", "Clima" if my language is pt-BR; "Finances", "Sports", "Weather" if my language is en-US.
[Q] Is there another way to get the application names? Maybe native/Win32 (DISM API/...)? Is possible to load the .pri file of each app to get the localized name?
As said, an updated working sample is here: https://github.com/luisrigoni/metro-apps-list
Using SHLoadIndirectString, you should be able to construct a fully-qualified reference for Package name and resource ID of the form #{PackageFullName?resource-id}
Documented here:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb759919(v=vs.85).aspx
You'll have to transform the manifest string into the proper form, though. It should be:
ms-resource://PackageName/Resources/Id
PackageName is the name rather than the full name. Resources isn't strictly required but it's the default and it's usually there. I'd try to look up the resource without inserting resources and then try again if that fails.
For example, the camera app has "ms-resource:manifestDisplayName" in the manifest, so first you should try(*):
#{Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe? ms-resource://Microsoft.Camera/manifestAppDescription}
When that fails, insert "resources" and try:
#{Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe? ms-resource://Microsoft.Camera/resources/manifestAppDescription}
That should work. You'll want to try both forms because blindly inserting "resources" will break apps like skydrive, communications and photos which insert the first part of the path directly.
Still a bit of a pain, but better than dumping and parsing gigantic XML files.
(*) "Microsoft.Camera_6.2.8376.0_x64__8wekyb3d8bbwe" is taken from an example - you'll obviously want the FullName of the one that's actually present on your system.
Looks like you're stuck with makepri.exe dump /if <prifile>.pri /of <outfile>.xml. Then all you have to do is parse/deserialize the XML file.
In addition to what Erik F told above along with updated question from Luis Rigoni (OP) here are further tips:
I found that path to PRI is better solution that giving package name. Many a times SHLoadIndirectString doesn't resolve the resource when just package name is given. Path to PRI is the package's install location + resources.pri . Example: C:\Program Files\WindowsApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\Resources.pri.
The VisualElements/DisplayName may contain the full url to the resource. If so, you don't have to further format it using package name and 'resources' folder like ms-resource://{0}/resources/{1}. If the DisplayName contains the package name itself, then you can assume that it is a full url.
Like Erik F pointed out, when SHLoadIndirectString fails, try again without the /resources/ folder.
Also sometimes the resources folder itself will be part of VisualElements/DisplayName. Example: ms-resource:///MSWifiResources/AppDisplayName. Also, notice the three ///. Yes, you will have to take care of that. You just have to take MSWifiResources/AppDisplayName and suffix it to ms-resource:///MSWifiResources/AppDisplayName
.
[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
//If VisualElements/DisplayName contains ms-resource: then call the below
//function. identity is nothing but package name that can be retrieved from
//Identity/Name element in AppxManifest.xml.
private static string GetName(string installPath, string name, string identity) {
StringBuilder sb = new StringBuilder();
int result;
//if name itself contains the package name then assume full url else
//format the resource url
var resourceKey = (name.ToLower().Contains(identity.ToLower())) ? name : string.Format("ms-resource://{0}/resources/{1}", identity, name.Split(':')[[1]].TrimStart('/'));
string source = string.Format("#{{{0}? {1}}}", Path.Combine(installPath, "resources.pri"), resourceKey);
result = SHLoadIndirectString(source, sb, -1, IntPtr.Zero);
if (result == 0)
return sb.ToString();
//if the above fails then we try the url without /resources/ folder
//because some apps do not place the resources in that resources folder
resourceKey = string.Format("ms-resource://{0}/{1}", identity, name.Split(':')[[1]].TrimStart('/'));
source = string.Format("#{{{0}? {1}}}", Path.Combine(installPath, "resources.pri"), resourceKey);
result = SHLoadIndirectString(source, sb, -1, IntPtr.Zero);
if (result == 0)
return sb.ToString();
return string.Empty;
}
Actually, you can do better than makepri - check out the ResourceIndexer:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.resources.management.resourceindexer.aspx
You should be able to give IndexFileContentsAsync a PRI file and get back all of the resource candidates in the file. You'll have to reassemble and reinterpret them, but it will get you all of the possible resource values.
For Windows 8 apps, at least.
For apps which take advantage of resource packages (introduced in Windows 8.1), the resources.pri in the package contains only the defaults. To get the resources for the any other installed languages (or scale factors) you'll need to also index the PRI files from the additional resource packages.

Find the parent directory in c#

If path = "\ProgramFiles\MobileApp\es-gl\a.dll". I want to get "\ProgramFiles\MobileApp\es-gl" alone. Just want to know the parent directory of the file a.dll. Is there Any inbuilt method in c#? I am using .net Compact Framework
System.IO.Path.GetDirectoryName(path)
I also needed such a function to find the parent directory of a folder seamlessly. So I created one myself:
public static string ExtractFolderFromPath(string fileName, string pathSeparator, bool includeSeparatorAtEnd)
{
int pos = fileName.LastIndexOf(pathSeparator);
return fileName.Substring(0,(includeSeparatorAtEnd ? pos+1 : pos));
}
Just send pathSeparator ("\" for windows and "/" for unix-like paths).
set last parameter true if you want separator included at the end. for ex:
C:\foo\
I'm not sure but I think the FileInfo and DirectoryInfo classes are supported on the Compact Framework.
Try this:
FileInfo myFile = new FileInfo("\ProgramFiles\MobileApp\es-gl\a.dll");
string parentDirectory = myFile.Directory.Name;
According to the MSDN documentation you could also do this:
FileInfo myFile = new FileInfo("\ProgramFiles\MobileApp\es-gl\a.dll");
string parentDirectory = myFile.DirectoryName;
Check out these MSDN links for more info:
http://msdn.microsoft.com/en-us/library/system.io.fileinfo_members(v=vs.71)
http://msdn.microsoft.com/en-us/library/system.io.fileinfo.directory(v=vs.71)
There is a Parent directory on FileInfo(System.IO namespace). Example code :
var file = new FileInfo(#"\ProgramFiles\MobileApp\es-gl\a.dll");
var parent = file.Directory.Parent;
You can just use the methods of the string class.
string path = #"\ProgramFiles\MobileApp\es-gl\a.dll";
string newPath = path.Substring(0, path.LastIndexOf('\\'));
var directory = Path.GetDirectoryName(#"c:\some\path\to\a\file.txt");
// returns "c:\some\path\to\a"
MSDN

hijack program’s command to run notepad

I have a utility programs’s EXE file, when i run this file there is a winform only and there is button when we click on it, it run windows’s notepad. Now I want to hijack this program’s command to run notepad and instead of running notepad I want to run MS Word. I know C# and VB.NET. What I need to do this ?
You can try to add in folder with this program your own program called notepad.exe that should do only one thing: run word.
If you want to do it programatically in C then you should read this page - maybe it helps: Intercepted: Windows Hacking via DLL Redirection
You can use a trick to replace programs with another by making changes to the registry. This will work even if the program you are running uses absolute paths to run notepad. It overrides any instance of the running program with the chosen one no matter where it resides. And you won't have to patch the file. The key you'd be interested in is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
Add a key with the name of the program and add a Debugger string with the path to the program you want to replace it with. Of course you need to have permissions to make the necessary modifications. This page explains how you can replace Windows Notepad with another program. You can apply the same process here.
Though you'll probably not want to have this permanent change, so you can write up a program to temporarily add/change the key, run your program then change it back. Here's a complete one I just whipped up to temporarily replace Notepad with Word for a demonstration. Seems to work perfectly fine (though as always, use at your own risk). Just make all the necessary changes to fit your situation.
using System.Diagnostics;
using Microsoft.Win32;
namespace ProgramLauncher
{
class Program
{
// change the following constants as needed
const string PROGRAM_NAME = #"notepad.exe";
const string REPLACEMENT_PATH = #"C:\Program Files (x86)\Microsoft Office\Office12\WINWORD.EXE";
const string RUNNING_PATH = #"C:\Windows\notepad.exe";
// root key
const string KEY = #"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options";
static void Main(string[] args)
{
using (var rootKey = Registry.LocalMachine.OpenSubKey(KEY, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
var oldPath = default(string);
var needsRestoration = false;
try
{
oldPath = BackupKey(rootKey, PROGRAM_NAME, REPLACEMENT_PATH);
needsRestoration = true;
Process.Start(RUNNING_PATH).WaitForExit();
}
finally
{
if (needsRestoration)
RestoreKey(rootKey, PROGRAM_NAME, oldPath);
}
}
}
static string BackupKey(RegistryKey rootKey, string programName, string newPath)
{
Debug.Assert(rootKey != null);
Debug.Assert(!string.IsNullOrEmpty(programName));
Debug.Assert(!string.IsNullOrEmpty(newPath) && System.IO.File.Exists(newPath));
if (newPath.Contains(" "))
newPath = string.Format("\"{0}\"", newPath);
using (var programKey = rootKey.CreateSubKey(programName, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
var oldDebugger = programKey.GetValue("Debugger") as string;
programKey.SetValue("Debugger", newPath, RegistryValueKind.String);
return oldDebugger;
}
}
static void RestoreKey(RegistryKey rootKey, string programName, string oldPath)
{
Debug.Assert(rootKey != null);
Debug.Assert(!string.IsNullOrEmpty(programName));
if (oldPath != null)
{
using (var programKey = rootKey.OpenSubKey(programName, RegistryKeyPermissionCheck.ReadWriteSubTree))
programKey.SetValue("Debugger", oldPath);
}
else
{
rootKey.DeleteSubKey(programName);
}
}
}
}

How do I find a file that may not be fully-qualified by using the environment path?

I have an executable name, like "cmd.exe" and need to resolve it's fully-qualified path. I know the exe appears in one of the directories listed in the PATH environment variable. Is there a way to resolve the full path without parsing and testing each directory in the PATH variable? basically I don't want to do this:
foreach (string entry in Environment.GetEnvironmentVariable("PATH").Split(';'))
...
There has to be a better way, right?
Here's another approach:
string exe = "cmd.exe";
string result = Environment.GetEnvironmentVariable("PATH")
.Split(';')
.Where(s => File.Exists(Path.Combine(s, exe)))
.FirstOrDefault();
Result: C:\WINDOWS\system32
The Path.Combine() call is used to handle paths that don't end with a trailing slash. This will properly concatenate the strings to be used by the File.Exists() method.
You could Linq it with
string path = Environment
.GetEnvironmentVariable("PATH")
.Split(';')
.FirstOrDefault(p => File.Exists(p + filename));
A little more readable maybe?
Dan
Well, I did find the following; however, I think I'll stick to the managed implementation.
static class Win32
{
[DllImport("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern bool PathFindOnPath([MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszFile, IntPtr unused);
public static bool FindInPath(String pszFile, out String fullPath)
{
const int MAX_PATH = 260;
StringBuilder sb = new StringBuilder(pszFile, MAX_PATH);
bool found = PathFindOnPath(sb, IntPtr.Zero);
fullPath = found ? sb.ToString() : null;
return found;
}
}
This seems like a pretty good way of doing it already -- as far as I know, searching through the directories in the PATH environment variable is what Windows does anyway when it's trying to resolve a path.
I ended up writing this function:
private static string GetExecutablePath(string executableFileName)
{
var path = Environment
.GetEnvironmentVariable("PATH")!
.Split(';')
.Select(s => Path.Combine(s, executableFileName))
.FirstOrDefault(x => File.Exists(x));
if (path == null)
{
throw new Exception($"Cannot find {executableFileName}. Is it installed on your computer?");
}
return path;
}
In my case, I wanted to find the path to python.exe, so I call the function like this:
GetExecutablePath("python.exe")
Which in my case returns:
"C:\\Program Files\\Python39\\python.exe"

Categories

Resources