在 Windows 系統編程中,用戶帳戶管理是一項基礎且關鍵的任務。無論是系統管理工具、自動化部署腳本,還是安全審計程序,都需要以程式化方式創建、刪除、查詢用戶,以及調整群組隸屬關係。Windows 提供了 NetUserAdd、NetUserDel、NetUserEnum、NetLocalGroupAddMembers 等 NetAPI 函數,配合 netapi32.lib,即可在 C/C++ 中輕鬆實現這些操作。
| 函數 | 用途 | 關鍵參數 |
|---|---|---|
NetUserAdd | 新增本地用戶 | 伺服器名、層級(如 USER_INFO_1)、用戶結構體、錯誤碼輸出 |
NetUserDel | 刪除本地用戶 | 伺服器名、用戶名 |
NetUserEnum | 列舉所有本地用戶 | 伺服器名、層級(如 USER_INFO_3)、過濾條件、緩衝區、續傳句柄 |
NetLocalGroupAddMembers | 將用戶加入本地群組 | 伺服器名、群組名、層級(LOCALGROUP_MEMBERS_INFO_3)、成員陣列、成員數 |
下列程式示範:取得用戶總數、列舉所有用戶、新增管理員用戶 alex、將其加入 Administrators 群組、最後刪除該用戶。程式需以管理員權限執行。
#define MAX_BUFF 1024*4
#include <stdio.h>
#include <windows.h>
#include <assert.h>
#include <lm.h>
#pragma comment(lib, "netapi32.lib")
char Temp[MAX_BUFF] = {0};
int UserEnum();
int GetUserNum();
int wmain(int argc, wchar_t *argv[])
{
USER_INFO_1 ui;
DWORD dwLevel = 1;
DWORD dwError = 0;
NET_API_STATUS nStatus;
LPWSTR UserName = L"alex";
// 1. 取得用戶總數
int UserNum = GetUserNum();
wprintf(L"Current user count: %d\n", UserNum);
// 2. 列舉所有用戶
UserEnum();
// 3. 準備新增用戶資料
ui.usri1_name = UserName;
ui.usri1_password = UserName; // 密碼同用戶名(示範用)
ui.usri1_priv = USER_PRIV_USER;
ui.usri1_home_dir = NULL;
ui.usri1_comment = NULL;
ui.usri1_flags = UF_SCRIPT; // 不要求密碼過期
ui.usri1_script_path = NULL;
// 4. 新增用戶
nStatus = NetUserAdd(NULL, dwLevel, (LPBYTE)&ui, &dwError);
if (nStatus == NERR_Success)
fwprintf(stderr, L"User %s has been successfully added.\n", UserName);
else
fwprintf(stderr, L"Add user failed: %d (error param: %d)\n", nStatus, dwError);
// 5. 加入 Administrators 群組
LPWSTR szGroup = L"Administrators";
LOCALGROUP_MEMBERS_INFO_3 LGMInfo;
LGMInfo.lgrmi3_domainandname = UserName;
nStatus = NetLocalGroupAddMembers(NULL, szGroup, 3, (LPBYTE)&LGMInfo, 1);
if (nStatus == NERR_Success)
fwprintf(stderr, L"User %s added to %s.\n", UserName, szGroup);
else
fwprintf(stderr, L"Add to group failed: %d\n", nStatus);
// 6. 刪除用戶(示範完整生命週期)
nStatus = NetUserDel(NULL, UserName);
if (nStatus == NERR_Success)
fwprintf(stderr, L"User %s has been successfully deleted.\n", UserName);
else
fwprintf(stderr, L"Delete user failed: %d\n", nStatus);
return 0;
}
// 取得本地用戶總數
int GetUserNum()
{
DWORD dwLevel = 1;
DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
NET_API_STATUS nStatus;
LPUSER_INFO_1 pBuf = NULL;
nStatus = NetUserEnum(NULL, dwLevel, FILTER_NORMAL_ACCOUNT,
(LPBYTE*)&pBuf, dwPrefMaxLen,
&dwEntriesRead, &dwTotalEntries, NULL);
if (pBuf) NetApiBufferFree(pBuf);
return (nStatus == NERR_Success) ? (int)dwTotalEntries : 0;
}
// 列舉並印出所有用戶名稱與權限
int UserEnum()
{
LPUSER_INFO_3 pBuf = NULL;
LPUSER_INFO_3 pTmpBuf;
DWORD i, dwLevel = 3, dwPrefMaxLen = MAX_PREFERRED_LENGTH;
DWORD dwEntriesRead = 0, dwTotalEntries = 0, dwResumeHandle = 0;
NET_API_STATUS nStatus;
LPTSTR pszServerName = NULL;
do {
nStatus = NetUserEnum(pszServerName, dwLevel, FILTER_NORMAL_ACCOUNT,
(LPBYTE*)&pBuf, dwPrefMaxLen,
&dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA)) {
if ((pTmpBuf = pBuf) != NULL) {
for (i = 0; i < dwEntriesRead; i++) {
if (pTmpBuf == NULL) {
printf("Access violation.\n");
break;
}
sprintf(Temp, "%-20S ", pTmpBuf->usri3_name);
switch (pTmpBuf->usri3_priv) {
case USER_PRIV_GUEST: strcat(Temp, "(Guest) "); break;
case USER_PRIV_USER: strcat(Temp, "(User) "); break;
case USER_PRIV_ADMIN: strcat(Temp, "(Administrator) "); break;
default: strcat(Temp, "(Unknown) "); break;
}
printf("%s\n", Temp);
pTmpBuf++;
}
}
} else {
printf("NetUserEnum failed: %d\n", nStatus);
}
if (pBuf) {
NetApiBufferFree(pBuf);
pBuf = NULL;
}
} while (nStatus == ERROR_MORE_DATA);
printf("List system accounts completed.\n");
return 0;
}
requireAdministrator 或以「以系統管理員身分執行」啟動。NetUserEnum 分配的緩衝區必須呼叫 NetApiBufferFree 釋放,否則會洩漏。NERR_UserExists (2231)、ERROR_ACCESS_DENIED (5)、NERR_InvalidComputer (2351) 等,應依 nStatus 做細緻處理。wmain 與 LPWSTR,確保中文用戶名亦能正確處理。NetUserGetInfo / NetUserSetInfo 讀寫完整用戶屬性(全名、描述、帳戶到期日、登入時段等)。NetLocalGroupEnum 列舉所有本地群組,NetLocalGroupGetMembers 查詢群組成員。NULL 改為目標機器名(如 L"\\\\SERVER01"),前提是具備 RPC 存取權限且防火牆開放 445/139 埠。掌握 NetAPI 用戶管理函數,能讓 C/C++ 程式直接與 Windows 安全子系統對話,是開發系統級工具、自動化運維腳本的核心技能。實務上建議將上述操作封裝為 RAII 風格的 C++ 類別,並加入完整的錯誤處理與日誌記錄,打造可維護、可複用的系統管理元件。