How to set Environment variables permanently in C#

31.1k views Asked by At

I am using the following code to get and set environment variables.

public static string Get( string name, bool ExpandVariables=true ) {
    if ( ExpandVariables ) {
        return System.Environment.GetEnvironmentVariable( name );
    } else {
        return (string)Microsoft.Win32.Registry.LocalMachine.OpenSubKey( @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" ).GetValue( name, "", Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames );
    }
}

public static void Set( string name, string value ) {
    System.Environment.SetEnvironmentVariable( name, value );
}

The problem I face, is even when the program is running as administrator, the environment variable lasts only as long as the program is running. I have confirmed this by running a Get on the variable I set in a previous instance.

Example usage of above

Set("OPENSSL_CONF", @"c:\openssl\openssl.cfg");

And to retrieve

MessageBox.Show( Get("OPENSSL_CONF") );

While the program is running, after using Set, the value is returned using Get without any issue. The problem is the environment variable isn't permanent (being set on the system).

It also never shows up under advanced properties.

Thanks in advance.

4

There are 4 answers

1
Yuval Itzchakov On BEST ANSWER

While the program is running, after using Set, the value is returned using Get without any issue. The problem is the environment variable isn't permanent (being set on the system).

Thats because the overload of SetEnvironmentVariable that you're using stores in the process variables. From the docs:

Calling this method is equivalent to calling the SetEnvironmentVariable(String, String, EnvironmentVariableTarget) overload with a value of EnvironmentVariableTarget.Process for the target argument.

You need to use the overload specifying EnvironmentVariableTarget.Machine instead:

public static void Set(string name, string value) 
{
    Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Machine);
}
0
ChrisM On

According to MSDN the method you are using is just modifying the variable for the runtime of the process.

Try the overload described here: https://msdn.microsoft.com/library/96xafkes%28v=vs.110%29.aspx

2
Mrinal Kamboj On

This kind of question has already been asked multiple times, check the following links for more information:

Set Env Variable - 1

Set Env Variable - 2

Set Env Variable - Tutorial

0
Bimo On

Here's an example that permanently updates the User PATH variable by programmatically editing the registry:

// Admin Permission not-required:
//      HKCU\Environment\Path
// Admin Permission required:
//      HKLM\SYSTEM\CurrentControlSet\Control
//         \Session Manager\Environment\Path

public static void UserPathAppend(string path, int verbose=1) {
    string oldpath = UserPathGet();

    List<string> newpathlist = oldpath.Split(';').ToList();
    newpathlist.Add(path);

    string newpath = String.Join(";", newpathlist.ToArray());

    UserPathSet(newpath);

    UpdateEnvPath();

    if (verbose!=0) {
        System.Windows.MessageBox.Show(
            "PATH APPEND:\n\n"
            + path + "\n\n"
            + "OLD HKCU PATH:\n\n"
            +  oldpath + "\n\n"
            + "NEW HKCU PATH:\n\n"
            +  newpath + "\n\n"
            + "REGISTRY KEY MODIFIED:\n\n"
            + "HKCU\\Environment\\Path\n\n"
            + "NOTE:\n\n"
            + "'Command Path' is a concat of 'HKLM Path' & 'HKCU Path'.\n",
            "Updated Current User Path Environment Variable"
        );
    }
}

public static void UserPathPrepend(string path, int verbose=1) {
    string oldpath = UserPathGet();

    List<string> newpathlist = oldpath.Split(';').ToList();
    newpathlist.Insert(0, path);

    string newpath = String.Join(";", newpathlist.ToArray());

    UserPathSet(newpath);

    UpdateEnvPath();

    if (verbose != 0) {
        System.Windows.MessageBox.Show(
            "PATH PREPEND:\n\n"
            + path + "\n\n"
            + "OLD HKCU PATH:\n\n"
            +  oldpath + "\n\n"
            + "NEW HKCU PATH:\n\n"
            +  newpath + "\n\n"
            + "REGISTRY KEY MODIFIED:\n\n"
            + "HKCU\\Environment\\Path\n\n"
            + "NOTE:\n\n"
            + "'Command Path' is a concat of 'HKLM Path' & 'HKCU Path'.\n",
            "Updated Current User Path Environment Variable"
        );
    }
}

public static string UserPathGet()
{
    // Reads Registry Path "HKCU\Environment\Path"
    string subKey = "Environment";

    Microsoft.Win32.RegistryKey sk = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(subKey);

    if (sk == null)
        return null;
    else
        return sk.GetValue("Path").ToString();
}

public static void UserPathSet(string newpath)
{
    // Writes Registry Path "HKCU\Environment\Path"
    string subKey = "Environment";

    Microsoft.Win32.RegistryKey sk1 = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(subKey);
    sk1.SetValue("Path", newpath);
}

//===========================================================
// Private: This part required if you don't want to logout 
//          and login again to see Path Variable update
//===========================================================

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageTimeout(IntPtr hWnd, 
            uint Msg, UIntPtr wParam, string lParam, 
            SendMessageTimeoutFlags fuFlags, 
            uint uTimeout, out UIntPtr lpdwResult);

private enum SendMessageTimeoutFlags : uint
{
    SMTO_NORMAL = 0x0, SMTO_BLOCK = 0x1, 
    SMTO_ABORTIFHUNG = 0x2, SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}

private static void UpdateEnvPath() {
    // SEE: https://support.microsoft.com/en-us/help/104011/how-to-propagate-environment-variables-to-the-system
    // Need to send WM_SETTINGCHANGE Message to 
    //    propagage changes to Path env from registry
    IntPtr HWND_BROADCAST = (IntPtr)0xffff;
    const UInt32 WM_SETTINGCHANGE = 0x001A;
    UIntPtr result;
    IntPtr settingResult
        = SendMessageTimeout(HWND_BROADCAST,
                             WM_SETTINGCHANGE, (UIntPtr)0,
                             "Environment",
                             SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                             5000, out result);
}