首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

使用 C# 建立本機 Windows 帳戶:從 net.exe 命令到 DirectoryServices 完整方案

     編輯:關於C語言

前言

在 Windows 環境下以程式化方式建立本機使用者帳戶,主要有兩種路徑:

  1. 呼叫系統命令:透過 System.Diagnostics.Process 執行 net user / net localgroup (本文重點)
  2. 使用 DirectoryServices (System.DirectoryServices):直接操作 WinNT 提供者,物件導向、無需解析文字輸出、支援更豐富屬性設定

本文詳細介紹命令列模式的實作細節、參數完整說明、權限考量,並提供 DirectoryServices 替代方案概覽。

核心實作:Process.Start 執行 net.exe

基本帳戶建立流程

using System;
using System.Diagnostics;

namespace LocalAccountManager
{
    public class AccountCreatorViaCmd
    {
        /// 
        /// 建立本機標準使用者帳戶
        /// 
        /// 帳戶名稱
        /// 密碼 (null 或空字串表示無密碼)
        /// 顯示名稱 (可選)
        /// 描述 (可選)
        /// 是否成功
        public static bool CreateLocalUser(string username, string password, 
            string fullName = "", string description = "")
        {
            try
            {
                // 1. 建立帳戶: net user username password /add
                string args = $"user \"{username}\" ";
                args += string.IsNullOrEmpty(password) ? '""' : $"\"{password}\"";
                args += " /add";
                
                if (!string.IsNullOrEmpty(fullName))
                    args += $" /fullname:\"{fullName}\"";
                if (!string.IsNullOrEmpty(description))
                    args += $" /comment:\"{description}\"";

                if (!RunNetCommand(args))
                    return false;

                // 2. (可選) 設定密碼永不過期
                // RunNetCommand($"user \"{username}\" /expires:never");
                
                // 3. (可選) 禁止使用者變更密碼
                // RunNetCommand($"user \"{username}\" /passwordchg:no");

                Console.WriteLine($"帳戶 {username} 建立成功");
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"建立帳戶失敗: {ex.Message}");
                return false;
            }
        }

        /// 
        /// 將使用者加入指定本機群組 (如 Administrators)
        /// 
        public static bool AddToLocalGroup(string username, string groupName)
        {
            string args = $"localgroup \"{groupName}\" \"{username}\" /add";
            return RunNetCommand(args);
        }

        /// 
        /// 修改使用者密碼
        /// 
        public static bool ChangePassword(string username, string newPassword)
        {
            string args = $"user \"{username}\" \"{newPassword}\"";
            return RunNetCommand(args);
        }

        /// 
        /// 刪除本機使用者
        /// 
        public static bool DeleteUser(string username)
        {
            string args = $"user \"{username}\" /delete";
            return RunNetCommand(args);
        }

        /// 
        /// 啟用/停用帳戶
        /// 
        public static bool SetAccountActive(string username, bool active)
        {
            string args = $"user \"{username}\" /active:{(active ? "yes" : "no")}";
            return RunNetCommand(args);
        }

        /// 
        /// 執行 net.exe 命令的核心方法
        /// 
        private static bool RunNetCommand(string arguments)
        {
            var startInfo = new ProcessStartInfo
            {
                FileName = "net.exe",
                Arguments = arguments,
                UseShellExecute = false,        // 必須 false 才能重導向輸出
                CreateNoWindow = true,          // 不顯示視窗
                RedirectStandardOutput = true,  // 捕捉標準輸出
                RedirectStandardError = true,   // 捕捆錯誤輸出
                StandardOutputEncoding = System.Text.Encoding.GetEncoding(950), // 繁體中文系統代碼頁
                StandardErrorEncoding = System.Text.Encoding.GetEncoding(950),
                Verb = "runas"                  // 要求管理員權限 (UAC 提升)
            };

            try
            {
                using Process? proc = Process.Start(startInfo);
                if (proc == null)
                {
                    Console.WriteLine("無法啟動 net.exe");
                    return false;
                }

                // 讀取輸出避免緩衝區填滿導致死結
                string output = proc.StandardOutput.ReadToEnd();
                string error = proc.StandardError.ReadToEnd();
                
                proc.WaitForExit(); // 等待結束

                if (proc.ExitCode != 0)
                {
                    Console.WriteLine($"指令失敗 (ExitCode: {proc.ExitCode})");
                    Console.WriteLine($"輸出: {output}");
                    Console.WriteLine($"錯誤: {error}");
                    return false;
                }

                Console.WriteLine($"指令成功: net.exe {arguments}");
                if (!string.IsNullOrWhiteSpace(output))
                    Console.WriteLine($"輸出: {output.Trim()}");
                return true;
            }
            catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 740)
            {
                Console.WriteLine("錯誤: 需要系統管理員權限執行 (UAC 提升失敗)");
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"執行例外: {ex.Message}");
                return false;
            }
        }
    }
}

使用範例

class Program
{
    static void Main()
    {
        // 需以系統管理員身分執行!
        
        // 1. 建立一般使用者
        AccountCreatorViaCmd.CreateLocalUser("devuser", "P@ssw0rd123", 
            "開發測試帳戶", "自動建立的測試帳號");
        
        // 2. 建立管理員帳戶
        AccountCreatorViaCmd.CreateLocalUser("admin01", "Admin@2024", "系統管理員");
        AccountCreatorViaCmd.AddToLocalGroup("admin01", "Administrators");
        
        // 3. 修改密碼
        // AccountCreatorViaCmd.ChangePassword("devuser", "NewP@ssw0rd");
        
        // 4. 停用帳戶
        // AccountCreatorViaCmd.SetAccountActive("devuser", false);
        
        // 5. 刪除帳戶
        // AccountCreatorViaCmd.DeleteUser("olduser");
        
        Console.WriteLine("作業完成,按任意鍵離開...");
        Console.ReadKey();
    }
}

net user / net localgroup 參數完整參考表

net user 語法

net user  [password | *] [/add | /delete] [options] [/domain]
參數 說明 範例
username 帳戶名稱 (最長 20 字元) john
password 指定密碼 P@ssw0rd
* 提示輸入密碼 (互動模式) net user john * /add
/add 建立新帳戶
/delete 刪除帳戶
/domain 在網域控制站執行 (非本機)
/active:{yes|no} 啟用/停用帳戶 /active:no
/comment:"text" 帳戶描述 /comment:"測試帳號"
/fullname:"name" 顯示全名 /fullname:"John Doe"
/expires:{date|never} 帳戶到期日 (格式依系統地區) /expires:2024/12/31
/passwordchg:{yes|no} 允許/禁止使用者變更密碼 /passwordchg:no
/passwordreq:{yes|no} 要求/不要求密碼 /passwordreq:yes
/times:{times|all} 允許登入時間 (如 M-F,8am-5pm) /times:M-F,08:00-17:00
/homedir:path 主目錄路徑 /homedir:C:\Users\john
/profilepath:path 漫遊設定檔路徑
/scriptpath:path 登入腳本路徑
/workstations:{names|*} 限制可登入的工作站電腦名 /workstations:PC01,PC02

net localgroup 語法

net localgroup  [username[...]] [/add | /delete] [/domain]
常用內建群組 說明
Administrators 完全控制電腦
Users 標準使用者 (預設)
Guests 受限訪客存取
Power Users 較舊版本相容性群組
Remote Desktop Users 允許遠端桌面連線
IIS_IUSRS IIS 應用程式池身分

關鍵注意事項與坑點

⚠ 必須以系統管理員權限執行

  • 專案屬性 → Application → Manifest → 勾選 "Enable ClickOnce security settings" → 編輯 app.manifest,將 <requestedExecutionLevel> 設為 requireAdministrator
  • 或啟動 Visual Studio / 編譯後的 .exe 時「以系統管理員身分執行」
  • ProcessStartInfo.Verb = "runas" 會觸發 UAC 提升提示

⚠ 編碼問題 (中文系統)

  • net.exe 輸出編碼為系統作用代碼頁 (繁中通常為 CP950 / Big5)
  • 必須設定 StandardOutputEncoding = Encoding.GetEncoding(950) 正確讀取中文訊息
  • 若在 UTF-8 環境 (如 Docker Linux 容器) 執行需額外處理

ℹ 特殊帳戶限制

  • ASPNET / IIS_IUSRS 等服務帳戶屬於 Users 群組,無法建立其他帳戶或修改群組
  • ASP.NET Core 應用建議使用 受控服務帳戶 (gMSA) 或指定身分執行應用程式池

進階替代方案:System.DirectoryServices (WinNT Provider)

命令列模式雖簡單但有缺點:需解析文字輸出、難以處理複雜屬性、效能較差。DirectoryServices 提供物件導向操作:

優點比較

特性 net.exe (Process) DirectoryServices
程式碼風格 命令列字串拼湊 強型別物件操作
錯誤處理 ExitCode + 文字解析 標準 .NET Exception
屬性設定 有限參數 完整屬性支援 (PasswordExpired, UserFlags 等)
效能 啟動進程開銷大 進程內 COM 呼叫,較快
相依性 無 (系統內建) 需參考 System.DirectoryServices.dll

DirectoryServices 建立使用者範例

using System.DirectoryServices;

public static void CreateUserDS(string username, string password, string fullName)
{
    // 連接本機 SAM 資料庫
    using DirectoryEntry computer = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
    
    // 建立使用者
    using DirectoryEntry user = computer.Children.Add(username, "user");
    user.Invoke("SetPassword", new object[] { password });
    user.Invoke("Put", new object[] { "Description", "透過 DirectoryServices 建立" });
    user.Invoke("Put", new object[] { "FullName", fullName });
    user.CommitChanges(); // 寫入
    
    // 設定密碼永不過期 (UserFlags: ADS_UF_DONT_EXPIRE_PASSWD = 0x10000)
    int flags = (int)user.Properties["UserFlags"].Value;
    user.Properties["UserFlags"].Value = flags | 0x10000;
    user.CommitChanges();
    
    Console.WriteLine("DirectoryServices 建立成功");
}

加入 Administrators 群組

using DirectoryEntry group = new DirectoryEntry("WinNT://" + Environment.MachineName + "/Administrators,group");
group.Invoke("Add", new object[] { "WinNT://" + Environment.MachineName + "/" + username });
group.CommitChanges();

安全性最佳實務

  1. 避免硬編碼密碼:從 Azure Key Vault、環境變數、加密設定檔讀取
  2. 最小權限原則:預設建立標準使用者,僅必要時加入 Administrators
  3. 密碼複雜度:符合群組原則要求 (長度、大小寫、數字、符號)
  4. 稽核記錄:記錄建立/修改/刪除操作的時間、操作者、目標帳戶
  5. 清理機制:臨時測試帳戶設定到期日 (/expires) 自動失效

常見錯誤代碼對照

Exit Code 意義 處理建議
0 成功 -
1 參數錯誤 / 語法錯誤 檢查引數格式、特殊字元逸出
2 存取被拒 確認以管理員執行、目標帳戶權限
3 系統找不到指定路徑/帳戶 帳戶不存在、群組名稱錯誤
5 存取被拒 (權限不足) UAC 未提升、群組原則限制
87 參數錯誤 (ERROR_INVALID_PARAMETER) 參數格式不符、密碼不符複雜度要求
1326 登入失敗 (未知使用者/密碼錯誤) 網域操作時認證問題
2224 帳戶已存在 建立前先檢查或捕捉此代碼

總結

使用 Process.Start 呼叫 net.exe 是以 C# 建立本機 Windows 帳戶最直接、零相依性的方法,適合簡單自動化腳本、安裝程式、部署工具。關鍵在於:正確處理 UAC 提升、編碼設定、參數逸出、錯誤代碼解讀。對於企業級應用、需大量帳戶管理、或需設定進階屬性 (智慧卡、到期日、漫遊設定檔等) 的場景,強烈建議改用 System.DirectoryServices (WinNT Provider) 或更現代的 System.DirectoryServices.AccountManagement (SAM) 命名空間,獲得型別安全、豐富 API 與更好效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved