GetEnvironmentVariable() and SetEnvironmentVariable() for PATH Variable - c#

I want to extend the current PATH variable with a C# program. Here I have several problems:
Using GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine) replaces the placeholders (i.e. '%SystemRoot%\system32' is replaced by the current path 'C:\Windows\system32'). Updating the PATH variable, I dont want to replace the placeholder with the path.
After SetEnvironmentVariable no program can't be opened from the command box anymore (i.e. calc.exe in the command box doesn't work). Im using following code:
String oldPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("PATH", oldPath + ";%MYDIR%", EnvironmentVariableTarget.Machine);
After editing and changing the PATH variable with Windows everything works again. (I thing changes are required, otherwise it is not overwritten)

You can use the registry to read and update:
string keyName = #"SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
//get non-expanded PATH environment variable
string oldPath = (string)Registry.LocalMachine.CreateSubKey(keyName).GetValue("Path", "", RegistryValueOptions.DoNotExpandEnvironmentNames);
//set the path as an an expandable string
Registry.LocalMachine.CreateSubKey(keyName).SetValue("Path", oldPath + ";%MYDIR%", RegistryValueKind.ExpandString);

You can use WMI to retrieve the raw values (not sure about updating them though):
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_Environment WHERE Name = 'PATH'");
foreach (ManagementBaseObject managementBaseObject in searcher.Get())
Console.WriteLine(managementBaseObject["VariableValue"]);
Check WMI Reference on MSDN

You could try this mix. It gets the Path variables from the registry, and adds the "NewPathEntry" to Path, if not already there.
static void Main(string[] args)
{
string NewPathEntry = #"%temp%\data";
string NewPath = "";
bool MustUpdate = true;
string RegKeyName = #"SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
string path = (string)Microsoft.Win32.Registry.LocalMachine.OpenSubKey(RegKeyName).GetValue
("Path", "", Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames);
string[] paths = path.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string subPath in paths)
{
NewPath += subPath + ";";
if (subPath.ToLower() == NewPathEntry.ToLower())
{
MustUpdate = false;
}
}
if (MustUpdate == true)
{
Environment.SetEnvironmentVariable("Path", NewPath + NewPathEntry, EnvironmentVariableTarget.Machine);
}
}

You could go through the registry...
string keyName = #"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
//get raw PATH environment variable
string path = (string)Registry.GetValue(keyName, "Path", "");
//... Make some changes
//update raw PATH environment variable
Registry.SetValue(keyName, "Path", path);

While working on the application we had to have an option to use Oracle instantclient from user-defined folder. In order to use the instantclient we had to modify the environment path variable and add this folder before calling any Oracle related functionality.
Here is method that we use for that:
/// <summary>
/// Adds an environment path segments (the PATH varialbe).
/// </summary>
/// <param name="pathSegment">The path segment.</param>
public static void AddPathSegments(string pathSegment)
{
LogHelper.Log(LogType.Dbg, "EnvironmentHelper.AddPathSegments", "Adding path segment: {0}", pathSegment);
string allPaths = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process);
if (allPaths != null)
allPaths = pathSegment + "; " + allPaths;
else
allPaths = pathSegment;
Environment.SetEnvironmentVariable("PATH", allPaths, EnvironmentVariableTarget.Process);
}
Note that this has to be called before anything else, possibly as the first line in your Main file (not sure about console applications).

Using Registry.GetValue will expand the placeholders, so I recommend using Registry.LocalMachine.OpenSubKey, then get the value from the sub key with options set to not expand environment variables. Once you've manipulated the path to your liking, use the registry to set the value again. This will prevent Windows "forgetting" your path as you mentioned in the second part of your question.
const string pathKeyName = #"SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
var pathKey = Registry.LocalMachine.OpenSubKey(pathKeyName);
var path = (string)pathKey.GetValue("PATH", "", RegistryValueOptions.DoNotExpandEnvironmentNames);
// Manipulate path here, storing in path
Registry.SetValue(String.Concat(#"HKEY_LOCAL_MACHINE\", pathKeyName), "PATH", path);

Related

How to save the path of an application to a variable by using the registry

I am trying to save the path of an application to a variable by using the registry.
What i want to achieve is:
1) Check if this application has an entry in the registry? (if it was installed or not)
2) If yes, I want to save the path to a variable which I can use later to use a program which is located in this path
So far i got
public void Run1()
{
Console.WriteLine("Reading OCR path from registry");
string keyName = #"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Tomcat\Common";
string valueName = "OCR_path";
string OCRPath = Microsoft.Win32.Registry.GetValue(keyName, valueName, null) as string;
if (string.IsNullOrWhiteSpace(OCRPath))
{
string text = "3";
System.IO.File.WriteAllText(#"C:\Users\Public\TestFolder\OCR-Toolkit-Check.txt", text);
}
}
Console.WriteLine("Reading OCR path from Registry:");
string keyName = #"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Tomcat\Common";
string valueName = "OCR_path";
if (Microsoft.Win32.Registry.GetValue(keyName, valueName, null) != null)
{
object variable = Microsoft.Win32.Registry.GetValue(keyName, valueName, null);
}
You might want to do the null-check, after reading it to a variable. Besides, you can cast the object variable to string afterwards.
I used a different approach and it works now. The path is saved into the variable OCRPath
public void Run1()
{
Console.WriteLine("Reading OCR path from registry");
string keyName = #"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Tomcat\Common";
string valueName = "OCR_path";
string OCRPath = Microsoft.Win32.Registry.GetValue(keyName, valueName, null) as string;
if (string.IsNullOrWhiteSpace(OCRPath))
{
string text = "3";
System.IO.File.WriteAllText(#"C:\Users\Public\TestFolder\OCR-Toolkit-Check.txt", text);
}
}
Holy moly, do not hardcode "Wow6432Node". You can get away with that on a 64 bit system opening the registry in 64-bit mode, but if you open the registry in 32-bit mode it will create a horrid thing you don't want to see. Also if you have a 32-bit OS, there is not supposed to be a "Wow6432Node" folder so you will end up creating stuff in places you shouldn't.
If you don't require opening the registry using only privileges and can rely on the permissions of the user to create/open/read keys then Microsoft already has Microsoft.Win32.Registry to help you.
string sPath = null;
RegistryKey hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey appKey = hklm.OpenSubKey(#"SOFTWARE\Tomcat\Common");
if(appKey != null)
{
object oPath = appKey.GetValue("OCR_path", null);
if(oPath != null && oPath is string)
{
sPath = oPath.ToString();
}
}

Could not find a part of the path C# asp.net when I generated user session folder in particular directory

string UserFolder = Session["Username"].ToString();
if (!Directory.Exists("~/MisReports/EmailAttachment/"+UserFolder))
{
Directory.CreateDirectory("~/MisReports/EmailAttachment/"+UserFolder);
}
filePathE = Server.MapPath("~/MisReports/EmailAttachment/" + UserFolder + "/");
filePathE = filePathE + a + ".pdf";
bool isExist = File.Exists(filePathE);
if (isExist)
{
File.Delete(filePathE);
}
report.ExportToDisk(ExportFormatType.PortableDocFormat, filePathE);
I get the error
Could not find a part of the path
if (!Directory.Exists("~/MisReports/EmailAttachment/"+UserFolder))
{
Directory.CreateDirectory("~/MisReports/EmailAttachment/"+UserFolder);
}
in this area code does not enter if check although that the folder has not been created
The ~ is probably not evaluated if you are running on Windows. Windows is not Unix. Read up on Path.Combine, Environment.GetFolderPath and Environment.SpecialFolder on MSDN. You should build a path with code like
string directoryName = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), "MisReports/EmailAttachment", UserFolder);

Get path to executable from command (as cmd does)

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

How to convert a relative path to an absolute path in a Windows application?

How do I convert a relative path to an absolute path in a Windows application?
I know we can use server.MapPath() in ASP.NET. But what can we do in a Windows application?
I mean, if there is a .NET built-in function that can handle that...
Have you tried:
string absolute = Path.GetFullPath(relative);
? Note that that will use the current working directory of the process, not the directory containing the executable. If that doesn't help, please clarify your question.
If you want to get the path relative to your .exe then use
string absolute = Path.Combine(Application.ExecutablePath, relative);
This one works for paths on different drives, for drive-relative paths and for actual relative paths. Heck, it even works if the basePath isn't actually absolute; it always uses the current working directory as final fallback.
public static String GetAbsolutePath(String path)
{
return GetAbsolutePath(null, path);
}
public static String GetAbsolutePath(String basePath, String path)
{
if (path == null)
return null;
if (basePath == null)
basePath = Path.GetFullPath("."); // quick way of getting current working directory
else
basePath = GetAbsolutePath(null, basePath); // to be REALLY sure ;)
String finalPath;
// specific for windows paths starting on \ - they need the drive added to them.
// I constructed this piece like this for possible Mono support.
if (!Path.IsPathRooted(path) || "\\".Equals(Path.GetPathRoot(path)))
{
if (path.StartsWith(Path.DirectorySeparatorChar.ToString()))
finalPath = Path.Combine(Path.GetPathRoot(basePath), path.TrimStart(Path.DirectorySeparatorChar));
else
finalPath = Path.Combine(basePath, path);
}
else
finalPath = path;
// resolves any internal "..\" to get the true full path.
return Path.GetFullPath(finalPath);
}
It's a bit older topic, but it might be useful for someone.
I have solved a similar problem, but in my case, the path was not at the beginning of the text.
So here is my solution:
public static class StringExtension
{
private const string parentSymbol = "..\\";
private const string absoluteSymbol = ".\\";
public static String AbsolutePath(this string relativePath)
{
string replacePath = AppDomain.CurrentDomain.BaseDirectory;
int parentStart = relativePath.IndexOf(parentSymbol);
int absoluteStart = relativePath.IndexOf(absoluteSymbol);
if (parentStart >= 0)
{
int parentLength = 0;
while (relativePath.Substring(parentStart + parentLength).Contains(parentSymbol))
{
replacePath = new DirectoryInfo(replacePath).Parent.FullName;
parentLength = parentLength + parentSymbol.Length;
};
relativePath = relativePath.Replace(relativePath.Substring(parentStart, parentLength), string.Format("{0}\\", replacePath));
}
else if (absoluteStart >= 0)
{
relativePath = relativePath.Replace(".\\", replacePath);
}
return relativePath;
}
}
Example:
Data Source=.\Data\Data.sdf;Persist Security Info=False;
Data Source=..\..\bin\Debug\Data\Data.sdf;Persist Security Info=False;

Path.Combine absolute with relative path strings

I'm trying to join a Windows path with a relative path using Path.Combine.
However, Path.Combine(#"C:\blah",#"..\bling") returns C:\blah\..\bling instead of C:\bling\.
Does anyone know how to accomplish this without writing my own relative path resolver (which shouldn't be too hard)?
What Works:
string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);
(result: absolutePath="C:\bling.txt")
What doesn't work
string relativePath = "..\\bling.txt";
Uri baseAbsoluteUri = new Uri("C:\\blah\\");
string absolutePath = new Uri(baseAbsoluteUri, relativePath).AbsolutePath;
(result: absolutePath="C:/blah/bling.txt")
Call Path.GetFullPath on the combined path http://msdn.microsoft.com/en-us/library/system.io.path.getfullpath.aspx
> Path.GetFullPath(Path.Combine(#"C:\blah\",#"..\bling"))
C:\bling
(I agree Path.Combine ought to do this by itself)
Path.GetFullPath(#"c:\windows\temp\..\system32")?
For windows universal apps Path.GetFullPath() is not available, you can use the System.Uri class instead:
Uri uri = new Uri(Path.Combine(#"C:\blah\",#"..\bling"));
Console.WriteLine(uri.LocalPath);
This will give you exactly what you need (path does NOT have to exist for this to work)
DirectoryInfo di = new DirectoryInfo(#"C:\blah\..\bling");
string cleanPath = di.FullName;
Path.GetFullPath() does not work with relative paths.
Here's the solution that works with both relative + absolute paths. It works on both Linux + Windows and it keeps the .. as expected in the beginning of the text (at rest they will be normalized). The solution still relies on Path.GetFullPath to do the fix with a small workaround.
It's an extension method so use it like text.Canonicalize()
/// <summary>
/// Fixes "../.." etc
/// </summary>
public static string Canonicalize(this string path)
{
if (path.IsAbsolutePath())
return Path.GetFullPath(path);
var fakeRoot = Environment.CurrentDirectory; // Gives us a cross platform full path
var combined = Path.Combine(fakeRoot, path);
combined = Path.GetFullPath(combined);
return combined.RelativeTo(fakeRoot);
}
private static bool IsAbsolutePath(this string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
return
Path.IsPathRooted(path)
&& !Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)
&& !Path.GetPathRoot(path).Equals(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal);
}
private static string RelativeTo(this string filespec, string folder)
{
var pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) folder += Path.DirectorySeparatorChar;
var folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString()
.Replace('/', Path.DirectorySeparatorChar));
}
Be careful with Backslashes, don't forget them (neither use twice:)
string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
//OR:
//string relativePath = "\\..\\bling.txt";
//string baseDirectory = "C:\\blah";
//THEN
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);

Categories