設置環境變量遇到的難題,cmd管理員方式與普通方式的區別,通過C#代碼設置環境變量


在使用mingw64的過程中,需要手工添加環境變量,作為一個懶人,這怎么可以呢?於是想用命令的方式實現,結果遇到問題了,死活實現不了,

之前用過TDM-GCC,人家的安裝完就可以用,還有試用過rust,人家在安裝程序中已經明確告訴了會在注冊表添加修改路徑(HKEY_CURRENT_USER\Environment->Path),也是安裝完就可以用,

他們都是怎么實現的?搜遍全網,也沒有找到解決方案,下面總結一下我的測試結論:

用戶變量在 HKEY_CURRENT_USER\Environment->Path
系統變量在 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment
      或者 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
ControlSet001:系統真實的配置信息。
CurrentControlSet:運行時配置。windows啟動時會從ControlSet001復制一份副本,作為操作系統當前的配置信息。
      我們對於計算機配置所作的修改都是直接寫入到 CurrentControlSet,
      在重啟過程中,windows會用CurrentControlSet的內容覆蓋掉ControlSet001,以保證這兩個控件組一致。

----------------------------------------------------------------------------------------------------------------------

在我的電腦->系統設置->環境變量界面,用戶變量或系統變量的path字段,手工添加,輸入設置的路徑,
在注冊表HKCU\Environment(用戶變量)、ControlSet001或CurrentControlSet(系統變量)會立即出現,且在新打開的命令行窗口立即有效

而在cmd窗口中set設置的環境變量為臨時變量,如:set PATH=%PATH%;C:\mingw64
結論:以命令行方式對環境變量的操作只對當前窗口的應用有效,關閉窗口即消失!
----------------------------------------------------------------------------------------------------------------------

使用setx設置為永久環境變量,適用於bat中:
@setx PATH "%PATH%;%~dp0bin" /m
/m:設置計算機環境(需以管理員權限運行),即系統變量,默認設置是用戶環境,即用戶變量。
盡管使用了/m,%PATH%本身還是會讀取所有的變量,即系統變量和用戶變量,還會把系統路徑變量替換為具體值。
C:\WINDOWS\system32>SETX MYPATH "%PATH%" /m
警告: 正保存的數據被裁斷到 1024 字符。
成功: 指定的值已得到保存。
C:\WINDOWS\system32>setx PATH "%PATH%;C:\mingw64" /m
警告: 正保存的數據被裁斷到 1024 字符。
成功: 指定的值已得到保存。
結論:可以設置成功系統變量,並進入注冊表,但是默認是短字符串類型REG_SZ,而系統的path是長字符串類型REG_EXPAND_SZ;
      系統的REG_EXPAND_SZ類型path被替換為REG_SZ類型path,被裁斷造成路徑丟失,此方法完全行不通。
------------------------------------------------------------------------------------------------------------------------------
在cmd命令行下使用wmic永久修改Windows環境變量
獲取 Temp 環境變量的用戶和變量值
    wmic ENVIRONMENT where name="Temp" get UserName,VariableValue
修改 OS 環境變量值為Windows_NT,這會覆蓋掉原有的變量值
    wmic ENVIRONMENT where name="os" set VariableValue="Windows_NT"
新增系統環境變量 myTemp,值為 %OS%%SystemDrive%
    wmic ENVIRONMENT create name="myTemp",username="<system>",VariableValue="%OS%%SystemDrive%"
刪除 myTemp 環境變量
    wmic ENVIRONMENT where "name='myTemp'" delete
用法說明:
1、where關鍵字后跟的參數必須是一個連續的字符串,如果參數字符串含有空格需要用英文雙引號"將參數括起來,
若字符串中有多個限定詞,比如既有 name 又有 username,則需要使用 and 關鍵字來連接這些限定詞。
       
2、在讀取環境變量值時不需要管理員權限,但在創建、寫入環境變量值時必須具備管理員權限。
修改 PATH 環境變量值,新增路徑 C:\tcc
    wmic ENVIRONMENT where "name='PATH' and username='<system>'" set VariableValue="%PATH%;C:\mingw64"
結論:在新打開的命令行窗口有效,
      %PATH%本身還是會讀取所有的變量,即系統變量和用戶變量,還會把系統路徑變量替換為具體值,此方法不可取
------------------------------------------------------------------------------------------------------------------
結論:ControlSet001或CurrentControlSet改一處則另一處同時變
      直接修改注冊表ControlSet001或CurrentControlSet(或HKCU\Environment),在系統設置-環境變量界面立即出現,
      但命令行窗口中只有以管理員權限運行才生效,普通模式必須重啟才能生效(不重啟的話每次打開cmd都得執行set命令才生效)。
      另:圖形界面的路徑刪除后注冊表和命令行窗口同時立即起作用,即路徑同時消失,
      但注冊表刪除后,圖形界面立即有效即消失,只有管理員命令行窗口立即有效,普通模式路徑仍然可用,必須重啟才能生效消失
      普通命令行窗口怎么才能立即生效呢,總是慢半拍,是何原因呢?
===================================================================
最近總算找到原因了,原來需要發一個全局的廣播:
C\C++: SendMessage(HWND_BROADCAST,WM_SETTINGCHANGE,0,(LPARAM)TEXT("Environment"));
或者 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment"), SMTO_ABORTIFHUNG, 5000, 0);
 C#里實現代碼如下:
        const int HWND_BROADCAST = 0xffff;
        const int WM_SETTINGCHANGE = 0x001A;
        public enum SendMessageTimeoutFlags : uint
        {
            SMTO_NORMAL = 0x0000,
            SMTO_BLOCK = 0x0001,
            SMTO_ABORTIFHUNG = 0x0002,
            SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
        }
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam,
             SendMessageTimeoutFlags flags, uint timeout, out IntPtr result);
        static void SetUserPath(string path)   //設置用戶環境變量
        {
            RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(@"Environment", true);
            Console.WriteLine("Path: {0}  {1}", registryKey.GetValue("Path"), registryKey.GetValueKind("Path"));
            registryKey.SetValue("Path", registryKey.GetValue("Path") + path, RegistryValueKind.ExpandString);
            SendMessageTimeout(new IntPtr(HWND_BROADCAST), WM_SETTINGCHANGE, IntPtr.Zero, "Environment",
                SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, out IntPtr result);
            Console.WriteLine("result: {0}", result);
        }

        static void SetSystemPath(string path)    //設置系統環境變量
        {
            String subKeyPath = @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment";
            RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(subKeyPath, true);   //需要管理員權限運行
            Console.WriteLine("Path: {0}  {1}", registryKey.GetValue("Path"), registryKey.GetValueKind("Path"));
            registryKey.SetValue("Path", registryKey.GetValue("Path") + path, RegistryValueKind.ExpandString);
            SendMessageTimeout(new IntPtr(HWND_BROADCAST), WM_SETTINGCHANGE, IntPtr.Zero, "Environment",
                SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, out IntPtr result);
            Console.WriteLine("result: {0}", result);
        }
        //手動修改環境變量時,系統自動維護其類型,長度較短時為REG_SZ(String),達到一定長度后為REG_EXPAND_SZ(ExpandString)
        //REG_SZ型注冊表值項的名稱是長度固定的文本字符串,最大長度不能超過255個字符;REG_EXPAND_SZ是長度可變的數據字符串。

 調用代碼:

        SetUserPath(@"C:\tcc");
        SetSystemPath(@"C:\tcc"); //需要管理員權限

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM