EternalWindows
アカウント管理 / ユーザーの列挙

何らかのセキュリティを設定するためには、まずはその設定の対象を取得しなければなりません。 たとえば、特定のユーザーをログオンできないようにしたいのであれば、 そのユーザーの情報が必要ですし、特定のグループに特権を割り当てたいのであれば、 そのグループに関する情報が必要になります。 こうしたアカウントを管理する関数は、Network Management関数と呼ばれ、 ユーザーやグループの他に、サーバーやワークステーション、コンピュータ名などを 管理する場合にも呼び出すことができます。 今回は、システムに存在するユーザーを列挙するNetUserEnumを取り上げます。

NET_API_STATUS NetUserEnum(
  LPCWSTR servername,
  DWORD level,
  DWORD filter,
  LPBYTE *bufptr,
  DWORD prefmaxlen,
  LPDWORD entriesread,
  LPDWORD totalentries,
  LPDWORD resume_handle
);

servernameは、この関数を実行するサーバーの名前を指定します。 NULLを指定した場合は、ローカルコンピュータで実行されます。 levelは、取得するデータのレベルを指定します。 filterは、列挙したいユーザーをフィルタする定数を指定します。 ドメインが構成されていない環境では、FILTER_NORMAL_ACCOUNTを指定します。 bufptrは、バッファへのアドレスを受け取る変数のアドレスを指定します。 このバッファに、列挙されたユーザー情報が格納されます。 prefmaxlenは、取得したいデータのサイズを指定します。 全てのデータを取得したい場合は、MAX_PREFERRED_LENGTHを指定します。 entriesreadは、列挙されたエントリの数を受け取る変数のアドレスを指定します。 totalentriesは、全てのエントリの数を受け取る変数のアドレスを指定します。 resume_handleは、レジュームハンドルを受け取る変数のアドレスを指定します。 通常は必要がないため、NULLを指定します。 関数が成功した場合は、NERR_Success が返ります。

Network Management関数では、データを取得する際にレベルを指定することがよくあります。 レベルによって取得できるデータの量が変化するため、 どのレベルを指定すれば目的のデータを取得できるのかを把握しておく必要があります。 たとえば、ユーザー名だけを取得したい場合であれば、 levelに0を指定し、bufptrにUSER_INFO_0構造体を指定することになります。

typedef struct _USER_INFO_0 {
  LPWSTR usri0_name;
}USER_INFO_0, *PUSER_INFO_0, *LPUSER_INFO_0;

usri0_nameは、ユーザーの名前が格納されます。 Network Management関数で使用される文字列は、常にUNICODEとなります。

Network Management関数によって確保されたバッファは、NetApiBufferFreeで開放します。

NET_API_STATUS NetApiBufferFree(
  LPVOID Buffer
);

Bufferは、開放したいバッファを指定します。

今回のプログラムは、システムに存在するユーザーを列挙します。 Network Management関数を呼び出す場合は、lm.hのインクルードとnetapi32.libへのリンクが必要になります。

#include <windows.h>
#include <lm.h>

#pragma comment (lib, "netapi32.lib")

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		DWORD        i;
		DWORD        dwEntryCount;
		DWORD        dwTotalEntries;
		PUSER_INFO_0 pUserInfo;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		if (NetUserEnum(NULL, 0, 0, (LPBYTE *)&pUserInfo, MAX_PREFERRED_LENGTH, &dwEntryCount, &dwTotalEntries, NULL) != NERR_Success)
			return -1;

		for (i = 0; i < dwEntryCount; i++)
			SendMessageW(hwndListBox, LB_ADDSTRING, 0, (LPARAM)pUserInfo[i].usri0_name);
			
		NetApiBufferFree(pUserInfo);

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

NetUserEnumの第2引数に0を指定しているため、取得するデータはレベル0のUSER_INFO_0構造体になります。 また、第3引数に0を指定しているため、全てのユーザーが列挙されることになります。 関数が成功したら、列挙できたエントリの数だけpUserInfoにアクセスし、 ユーザー名をリストボックスに追加していきます。 エントリの数は、dwTotalEntriesから参照しても問題ありません。 なお、システムに存在するユーザーを列挙するのではなく、 現在ログオンしているユーザーを列挙したい場合は、 NetWkstaUserEnumを呼び出すことになります。

NetQueryDisplayInformationについて

NetQueryDisplayInformationは、NetUserEnumと同じくユーザーを列挙できる関数ですが、 NetUserEnumと比べてより迅速に列挙が可能であるという利点があります。 次に、コード例を示します。

#include <windows.h>
#include <lm.h>

#pragma comment (lib, "netapi32.lib")

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		DWORD             i;
		DWORD             dwEntryCount;
		PNET_DISPLAY_USER pDisplayUser;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		if (NetQueryDisplayInformation(NULL, 1, 0, 100, MAX_PREFERRED_LENGTH, &dwEntryCount, (PVOID *)&pDisplayUser) != NERR_Success)
			return -1;

		for (i = 0; i < dwEntryCount; i++)
			SendMessageW(hwndListBox, LB_ADDSTRING, 0, (LPARAM)pDisplayUser[i].usri1_name);
		
		NetApiBufferFree(pDisplayUser);

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

第2引数はレベルであり、1を指定した場合はユーザーを列挙できるようになります。 逆に、2を指定した場合はコンピュータ情報に関する列挙となり、 3を指定した場合はグループの列挙となります。 第3引数は、取得開始とするエントリのインデックスであり、通常は0を指定します。 第4引数は、取得したいエントリの数であり、ここでは上限値である100を指定しています。 第6引数は、列挙されたエントリの数を受け取り、第7引数は列挙されたエントリを格納するバッファが返ります。 このバッファは、NET_DISPLAY_USER構造体で表すことができます。



戻る