2

Is there any way to get the path environmental variable as a literal string instead of expanded?

i.e.

%systemroot%\System32;%systemroot%\;etc...

instead of

C:\Windows\System32;C:\Windows\;etc...

I've tried using

Environment.GetSystemVariable("Path");

But that gives me the expanded directories and I don't see anything else under Environment that will get me that.

Edit:

PathUnExpandEnvStrings only works if the entire path is an environmental variable.

i.e.

C:\Windows\System32;C:\Windows;etc...

becomes

C:\Windows\System32;%systemroot%;etc...

using

Registry.GetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "Path", null);

Still gets the values as C:\Windows\System32;C:\Windows;etc...

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
Jeffrey
  • 528
  • 5
  • 14

1 Answers1

1

If you go the registry route you can pass RegistryValueOptions.DoNotExpandEnvironmentNames to the RegistryKey.GetValue() instance method to get the unexpanded path...

using System;
using Microsoft.Win32;

namespace SO60725684
{
    class Program
    {
        static void Main()
        {
            const string environmentKeyPath = @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
            const string valueName = "TEMP";

            using (RegistryKey environmentKey = Registry.LocalMachine.OpenSubKey(environmentKeyPath))
            {
                foreach (RegistryValueOptions options in Enum.GetValues(typeof(RegistryValueOptions)))
                {
                    object valueData = environmentKey.GetValue(valueName, "<default>", options);

                    Console.WriteLine($"{valueName} ({options}) = \"{valueData}\"");
                }
            }
        }
    }
}

This prints...

Temp (None) = "C:\WINDOWS\TEMP"
Temp (DoNotExpandEnvironmentNames) = "%SystemRoot%\TEMP"

I'm using the "TEMP" variable just for the brevity of its value, but it works just as well for "Path".

If you don't want to rely on the backing storage of an environment variable, there is also a Win32_Environment management class that exposes the value without expansion...

using System;
using System.Management;

namespace SO60725684
{
    class Program
    {
        static void Main()
        {
            string[] propertiesToDisplay = new string[] { "Name", "SystemVariable", "UserName", "VariableValue" };
            ObjectQuery query = new SelectQuery("Win32_Environment", "Name = 'TEMP'", propertiesToDisplay);

            using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
            using (ManagementObjectCollection resultCollection = searcher.Get())
            {
                foreach (ManagementBaseObject resultInstance in resultCollection)
                {
                    PropertyDataCollection properties = resultInstance.Properties;

                    foreach (string propertyName in propertiesToDisplay)
                    {
                        PropertyData property = properties[propertyName];

                        Console.WriteLine($"{property.Name}: {property.Value}");
                    }
                    Console.WriteLine();
                }
            }
        }
    }
}

This prints...

Name: TEMP
SystemVariable: True
UserName: <SYSTEM>
VariableValue: %SystemRoot%\TEMP

Name: TEMP
SystemVariable: False
UserName: NT AUTHORITY\SYSTEM
VariableValue: %USERPROFILE%\AppData\Local\Temp

Name: TEMP
SystemVariable: False
UserName: ComputerName\UserName
VariableValue: %USERPROFILE%\AppData\Local\Temp

As you can see, you get multiple results when the variable is defined for multiple users, so you'll need to do further filtering on the SystemVariable and UserName properties to get the one you want.

Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
  • @Jeffrey I have updated my answer with a `System.Management` alternative as well. If you need a `PathUnExpandEnvStrings`-like solution that works on partial paths, I recently posted [this PowerShell answer](https://stackoverflow.com/a/60090703/150605) that attempts to unexpand arbitrary paths using a set of environment variable paths. Hopefully that's readily readable/translatable for C#. – Lance U. Matthews Mar 17 '20 at 20:40