EternalWindows
アカウント管理 / ユーザーの情報

特定のユーザーの情報を取得するには、 ユーザー名を指定してNetUserGetInfoを呼び出します。

NET_API_STATUS NetUserGetInfo(
  LPCWSTR servername,
  LPCWSTR username,
  DWORD level,
  LPBYTE *bufptr
);

servernameは、この関数を実行するサーバー名を指定します。 NULLを指定した場合は、ローカルコンピュータで実行されます。 usernameは、ユーザー情報を取得したいユーザー名を指定します。 levelは、取得したい情報のレベルを指定します。 bufptrは、ユーザー情報を受け取るバッファへのアドレスを指定します。

levelに指定する値によって、取得できる情報量は大きく変化します。 たとえば、0を指定するとUSER_INFO_0構造体を使用することになり、 これにはユーザー名しか格納されていないため、 情報量としては不足しているといえます。 多くの場合は、levelに4を指定することで、 最も多くの情報を格納するUSER_INFO_4構造体を使用します。

typedef struct _USER_INFO_4 {
  LPWSTR usri4_name;
  LPWSTR usri4_password;
  DWORD  usri4_password_age;
  DWORD  usri4_priv;
  LPWSTR usri4_home_dir;
  LPWSTR usri4_comment;
  DWORD  usri4_flags;
  LPWSTR usri4_script_path;
  DWORD  usri4_auth_flags;
  LPWSTR usri4_full_name;
  LPWSTR usri4_usr_comment;
  LPWSTR usri4_parms;
  LPWSTR usri4_workstations;
  DWORD  usri4_last_logon;
  DWORD  usri4_last_logoff;
  DWORD  usri4_acct_expires;
  DWORD  usri4_max_storage;
  DWORD  usri4_units_per_week;
  PBYTE  usri4_logon_hours;
  DWORD  usri4_bad_pw_count;
  DWORD  usri4_num_logons;
  LPWSTR usri4_logon_server;
  DWORD  usri4_country_code;
  DWORD  usri4_code_page;
  PSID   usri4_user_sid;
  DWORD  usri4_primary_group_id;
  LPWSTR usri4_profile;
  LPWSTR usri4_home_dir_drive;
  DWORD  usri4_password_expired;
}USER_INFO_4, *PUSER_INFO_4, *LPUSER_INFO_4;

usri4_nameは、ユーザーの名前が格納されます。 usri4_passwordは、パスワードを表すメンバですが、NULLが格納されます。 このメンバは、NetUserSetInfoでパスワードを設定する際に使用します。 usri4_password_ageは、パスワードが最後に変更されてからの秒数が格納されます。 usri4_privは、ユーザーがどのようなグループに属しているかを表す定数が格納されます。 usri4_home_dirは、ユーザーのホームディレクトリが格納されます。 usri4_commentは、ユーザーに関連付けるコメントが格納されます。 usri4_flagsは、ユーザーに設定されたフラグが格納されます。 usri4_script_pathは、ユーザーのログオンスクリプトファイルへのパスが格納されます。 ログオンスクリプトは、ユーザーがログオンした際に自動で実行されます。 ここまで取り上げたメンバのみを取得したい場合は、 USER_INFO_1構造体を使用しても問題ありません。

usri4_auth_flagsは、ユーザーがどのようなオペレータグループに属しているかを表す定数が格納されます。 usri4_full_nameは、ユーザーのフルネームが格納されます。 usri4_usr_commentは、ユーザーのコメントが格納されます。 usri4_parmsは、Microsoft社製品で使われるデータが格納されます。 usri4_workstationsは、ユーザーがログオン可能なワークステーションの名前が格納されます。 名前が空白である場合は、ワークステーションの制限は受けません。 usri4_last_logonは、ユーザーが最後にログオンした時間が格納されます。 usri4_last_logoffは、現在使用されていません。 usri4_acct_expiresは、ユーザーが期限切れとなる時間が格納されます。 TIMEQ_FOREVERが格納されている場合は、期限切れになりません。 usri4_max_storageは、ユーザーが使用可能なディスク領域の最大値が格納されます。 USER_MAXSTORAGE_UNLIMITEDが指定されている場合は、全ての領域が使用可能です usri4_units_per_weekは、UNITS_PER_WEEKが格納されます。 usri4_logon_hoursは、一週間におけるログオンの時間帯を格納したデータへのアドレスが格納されます。 このデータは21バイトであり、各3バイトに一日のログオン時間帯が格納されていると思われます。 usri4_bad_pw_countは、ユーザーが誤ったパスワードでログオンしようとした回数が格納されます。 usri4_num_logonsは、ユーザーがログオンした回数が格納されます。 usri4_logon_serverは、ログオン要求を処理するサーバー名が格納されます。 サーバーを問わない場合は、\\*が格納されます。 usri4_country_codeは、国コードが格納されます。 usri4_code_pageは、コードページが格納されます。 ここまで取り上げたメンバのみを取得したい場合は、 USER_INFO_2構造体を使用しても問題ありません。

usri4_user_sidは、ユーザーのSIDが格納されます。 usri4_primary_group_idは、プライマリグループのRIDが格納されます。 usri4_profileは、ユーザープロファイルへのパスが格納されます。 usri4_home_dir_driveは、ユーザーのホームディレクトリが存在するドライブ文字が格納されます。 usri4_password_expiredは、パスワードの期限が切れているかどうかを示す値が格納されます。 0の場合は、期限切れではありません。 USER_INFO_4構造体は、Windows XPから使用可能な構造体であるため、 それ以前の環境でUSER_INFO_4構造体と同等の情報を取得したい場合は、 levelに3を指定してUSER_INFO_3構造体を使用することになります。 ただし、USER_INFO_3構造体にはユーザーのSIDではなくRIDが格納されます。

次に、usri4_flagsに格納される定数の一部を示します。

定数 意味
UF_SCRIPT ログオン時にスクリプトを実行することを示す。 この定数は必ず指定されるべきである。
UF_ACCOUNTDISABLE アカウントが無効であることを示す。 この場合、そのアカウントでログオンすることができない。
UF_PASSWD_NOTREQD パスワードが不要であることを示すが、正確な意味は不明。 パスワードが設定されているユーザーにも、この定数が含まれていることがある。 空のパスワードによるログオンはコンソール以外で失敗することがあるが、 それはこの定数と関係ないようである。
UF_PASSWD_CANT_CHANGE パスワードが変更できないことを示す。
UF_DONT_EXPIRE_PASSWD パスワードの有効期限が無期限であることを示す。 この定数が指定されていない場合、次回ログオンの際にパスワードの設定が必要になる。
UF_NORMAL_ACCOUNT 通常のアカウントであることを示す。 この定数は、ユーザーの追加時に自動的に設定される。

これらの定数が設定されていることを視覚的に確認したい場合は、 管理ツールの「コンピュータの管理」から「ローカルユーザーとグループ」を選択し、 ユーザーのプロパティを表示させればよいでしょう。 たとえば、「パスワードを無期限にする」というチェックが入っている場合は、 そのユーザーにUF_DONT_EXPIRE_PASSWDが設定されていることを意味します。

今回のプログラムは、左のリストボックスにユーザー名を列挙し、 選択されたユーザーの情報を右のリストボックスに表示します。

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

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

void GetTimeData(DWORD dwTime, LPWSTR lpszTime);
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 hwndListBoxLeft = NULL;
	static HWND hwndListBoxRight = NULL;

	switch (uMsg) {

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

		hwndListBoxLeft = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndListBoxRight = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)2, ((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(hwndListBoxLeft, LB_ADDSTRING, 0, (LPARAM)pUserInfo[i].usri0_name);
			
		NetApiBufferFree(pUserInfo);

		return 0;
	}

	case WM_COMMAND: {
		WCHAR        szBuf[1024];
		WCHAR        szTime[256];
		LPWSTR       lpszSid;
		DWORD        dwIndex;
		PUSER_INFO_4 pUserInfo;
	
		if ((HWND)lParam == hwndListBoxRight || HIWORD(wParam) != LBN_SELCHANGE)
			return 0;
		
		SendMessage(hwndListBoxRight, LB_RESETCONTENT, 0, 0);
		
		dwIndex = (DWORD)SendMessage(hwndListBoxLeft, LB_GETCURSEL, 0, 0);
		SendMessage(hwndListBoxLeft, LB_GETTEXT, dwIndex, (LPARAM)szBuf);

		if (NetUserGetInfo(NULL, szBuf, 4, (LPBYTE *)&pUserInfo) != NERR_Success)
			return 0;

		wsprintfW(szBuf, L"name : %s", pUserInfo->usri4_name);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"password_age : %d", pUserInfo->usri4_password_age);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

		switch (pUserInfo->usri4_priv) {
		case USER_PRIV_GUEST:
			lstrcpyW(szBuf, L"priv : USER_PRIV_GUEST");
			break;
		case USER_PRIV_USER:
			lstrcpyW(szBuf, L"priv : USER_PRIV_USER");
			break;
		case USER_PRIV_ADMIN:
			lstrcpyW(szBuf, L"priv : USER_PRIV_ADMIN");
			break;
		default:
			lstrcpyW(szBuf, L"priv : ");
			break;
		}
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"home_dir : %s", pUserInfo->usri4_home_dir);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"comment : %s", pUserInfo->usri4_comment);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"script_path : %s", pUserInfo->usri4_script_path);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
	
		lstrcpyW(szBuf, L"auth_flags :");
		if (pUserInfo->usri4_auth_flags & AF_OP_PRINT)
			lstrcatW(szBuf, L" AF_OP_PRINT");
		if (pUserInfo->usri4_auth_flags & AF_OP_COMM)
			lstrcatW(szBuf, L" AF_OP_COMM");
		if (pUserInfo->usri4_auth_flags & AF_OP_SERVER)
			lstrcatW(szBuf, L" AF_OP_SERVER");
		if (pUserInfo->usri4_auth_flags & AF_OP_ACCOUNTS)
			lstrcatW(szBuf, L" AF_OP_ACCOUNTS");
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"full_name : %s", pUserInfo->usri4_full_name);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

		wsprintfW(szBuf, L"usr_comment : %s", pUserInfo->usri4_usr_comment);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"workstations : %s", pUserInfo->usri4_workstations);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		GetTimeData(pUserInfo->usri4_last_logon, szTime);
		wsprintfW(szBuf, L"last_logon : %s", szTime);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
			lstrcpyW(szTime, L"TIMEQ_FOREVER"); 
		else
			GetTimeData(pUserInfo->usri4_acct_expires, szTime);
		wsprintfW(szBuf, L"acct_expires : %s", szTime);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

		if (pUserInfo->usri4_acct_expires == USER_MAXSTORAGE_UNLIMITED)
			wsprintfW(szBuf, L"max_storage : %s", L"USER_MAXSTORAGE_UNLIMITED");
		else
			wsprintfW(szBuf, L"max_storage : %x", pUserInfo->usri4_max_storage);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

		wsprintfW(szBuf, L"bad_pw_count : %d", pUserInfo->usri4_bad_pw_count);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"num_logons : %d", pUserInfo->usri4_num_logons);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"logon_server : %s", pUserInfo->usri4_logon_server);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"profile : %s", pUserInfo->usri4_profile);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		
		wsprintfW(szBuf, L"home_dir_drive : %s", pUserInfo->usri4_home_dir_drive);
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

		if (ConvertSidToStringSidW(pUserInfo->usri4_user_sid, &lpszSid)) {
			wsprintf(szBuf, L"user_sid : %s", lpszSid);
			SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
			LocalFree(lpszSid);
		}

		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"");
		SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"flags:");
		if (pUserInfo->usri4_flags & UF_SCRIPT)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_SCRIPT");
		if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_ACCOUNTDISABLE");
		if (pUserInfo->usri4_flags & UF_HOMEDIR_REQUIRED)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_HOMEDIR_REQUIRED");
		if (pUserInfo->usri4_flags & UF_PASSWD_NOTREQD)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_PASSWD_NOTREQD");
		if (pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_PASSWD_CANT_CHANGE");
		if (pUserInfo->usri4_flags & UF_LOCKOUT)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_LOCKOUT");
		if (pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_DONT_EXPIRE_PASSWD");
		if (pUserInfo->usri4_flags & UF_NORMAL_ACCOUNT)
			SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)L"UF_NORMAL_ACCOUNT");

		NetApiBufferFree(pUserInfo);
	
		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBoxLeft, 0, 0, LOWORD(lParam) / 3, HIWORD(lParam), TRUE);
		MoveWindow(hwndListBoxRight, LOWORD(lParam) / 3, 0, LOWORD(lParam) - (LOWORD(lParam) / 3), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

void GetTimeData(DWORD dwTime, LPWSTR lpszTime)
{
	LONGLONG   ll;
	FILETIME   fileTime;
	SYSTEMTIME systemTimeUtc, systemTimeLocal;
	
	ll = Int32x32To64(dwTime, 10000000) + 116444736000000000;

	fileTime.dwLowDateTime  = (DWORD) ll;
	fileTime.dwHighDateTime = (DWORD)(ll >> 32);
	FileTimeToSystemTime(&fileTime, &systemTimeUtc);

	SystemTimeToTzSpecificLocalTime(NULL, &systemTimeUtc, &systemTimeLocal);

	wsprintf(lpszTime, L"%d/%d/%d %d:%d", systemTimeLocal.wYear, systemTimeLocal.wMonth, systemTimeLocal.wDay, systemTimeLocal.wHour, systemTimeLocal.wMinute);
}

WM_CREATEでは、前節のようにNetUserEnumを呼び出してユーザーを列挙しています。 これを選択するとWM_COMMANDが送られるため、 そこで選択されたユーザー名を取得し、NetUserGetInfoの第2引数に指定します。 第3引数に4を指定しているため、第4引数にはUSER_INFO_4構造体が返ることになります。 後のコードは、USER_INFO_4構造体のメンバを単に表示するだけですが、 一部のメンバについて少し補足しておきます。 まず、usri4_privからです。

switch (pUserInfo->usri4_priv) {
case USER_PRIV_GUEST:
	lstrcpyW(szBuf, L"priv : USER_PRIV_GUEST");
	break;
case USER_PRIV_USER:
	lstrcpyW(szBuf, L"priv : USER_PRIV_USER");
	break;
case USER_PRIV_ADMIN:
	lstrcpyW(szBuf, L"priv : USER_PRIV_ADMIN");
	break;
default:
	lstrcpyW(szBuf, L"priv : ");
	break;
}
SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

usri4_privは、ユーザーがどのようなグループに属しているかを表す定数が格納されています。 USER_PRIV_GUESTならばそのユーザーはGuestsに属し、 USER_PRIV_USERならばUsers、USER_PRIV_ADMINならばAdministratorsという具合になります。 次に、usri4_auth_flagsの処理を示します。

lstrcpyW(szBuf, L"auth_flags :");
if (pUserInfo->usri4_auth_flags & AF_OP_PRINT)
	lstrcatW(szBuf, L" AF_OP_PRINT");
if (pUserInfo->usri4_auth_flags & AF_OP_COMM)
	lstrcatW(szBuf, L" AF_OP_COMM");
if (pUserInfo->usri4_auth_flags & AF_OP_SERVER)
	lstrcatW(szBuf, L" AF_OP_SERVER");
if (pUserInfo->usri4_auth_flags & AF_OP_ACCOUNTS)
	lstrcatW(szBuf, L" AF_OP_ACCOUNTS");
SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

usri4_auth_flagsは、ユーザーがどのようなオペレータグループに属しているかを表す定数が格納されています。 たとえば、AF_OP_PRINTが格納されている場合は、Print Operatorsに属していることを意味します。 これらは複数個格納されている場合があるため、論理積をとるようにしています。 次に、usri4_last_logonの処理を示します。

GetTimeData(pUserInfo->usri4_last_logon, szTime);
wsprintfW(szBuf, L"last_logon : %s", szTime);
SendMessageW(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);

last_logonには、ユーザーが最後にログオンした時間が格納されています。 GetTimeDataという自作関数は、この時間をSYSTEMTIME構造体に変換し、 この構造体のメンバで現在の時間を表す文字列を作成して返します。

void GetTimeData(DWORD dwTime, LPWSTR lpszTime)
{
	LONGLONG   ll;
	FILETIME   fileTime;
	SYSTEMTIME systemTimeUtc, systemTimeLocal;
	
	ll = Int32x32To64(dwTime, 10000000) + 116444736000000000;

	fileTime.dwLowDateTime  = (DWORD) ll;
	fileTime.dwHighDateTime = (DWORD)(ll >> 32);
	FileTimeToSystemTime(&fileTime, &systemTimeUtc);

	SystemTimeToTzSpecificLocalTime(NULL, &systemTimeUtc, &systemTimeLocal);

	wsprintf(lpszTime, L"%d/%d/%d %d:%d", systemTimeLocal.wYear, systemTimeLocal.wMonth, systemTimeLocal.wDay, systemTimeLocal.wHour, systemTimeLocal.wMinute);
}

dwTimeには、1970年1月1日からの秒数が格納されています。 これはGMTと呼ばれ、特にこの秒数をSYSTEMTIME構造体で表した場合は、UTC(世界協定時刻)と呼ばれます。 SYSTEMTIME構造体を取得するためには、FileTimeToSystemTimeでFILETIME構造体からSYSTEMTIME構造体に変換する必要があるため、 まずはFILETIME構造体をdwTimeで初期化することになります。 Int32x32To64は、第1引数と第2引数を掛けた結果をLONGLONG型で返します。 116444736000000000という値を足しているのは、dwTimeが経過時間という相対的な時間を表しているだけであり、 これだけでは絶対時間を表すことができないためです。 dwLowDateTimeにLONGLONG型の下位32ビット、dwHighDateTimeに上位32ビットを指定したら、 FileTimeToSystemTimeでSYSTEMTIME構造体に変換します。 そして最後に、取得したUTCをローカル時刻に変換するために、 SystemTimeToTzSpecificLocalTimeを呼び出します。

次に、usri4_user_sidの処理を示します。

if (ConvertSidToStringSidW(pUserInfo->usri4_user_sid, &lpszSid)) {
	wsprintf(szBuf, L"user_sid : %s", lpszSid);
	SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
	LocalFree(lpszSid);
}

ConvertSidToStringSidは、SIDを文字列形式に変換する関数です。 ユーザーのSIDは、最後のRIDの部分だけがユーザー毎で異なることになります。 ConvertSidToStringSidを呼び出すためには、sddl.hのインクルードが必要になります。

最後に、usri4_flagsに含まれている定数を表示しています。 Guestには、パスワードが変更できないことを示すUF_PASSWD_CANT_CHANGEが含まれるでしょう。 また、Administratorは無効にされることがよくありますが、 このときにはUF_ACCOUNTDISABLEが含まれるはずです。


戻る