xref: /reactos/dll/cpl/sysdm/userprofile.c (revision 177ae91b)
1 /*
2  * PROJECT:     ReactOS System Control Panel Applet
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/cpl/sysdm/userprofile.c
5  * PURPOSE:     Computer settings for networking
6  * COPYRIGHT:   Copyright Thomas Weidenmueller <w3seek@reactos.org>
7  *              Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8  *
9  */
10 
11 #include "precomp.h"
12 #include <sddl.h>
13 #include <winnls.h>
14 
15 #include <debug.h>
16 
17 typedef struct _PROFILEDATA
18 {
19     DWORD dwRefCount;
20     DWORD dwState;
21     BOOL bUnknownProfile;
22     PWSTR pszFullName;
23     PWSTR pszProfilePath;
24 } PROFILEDATA, *PPROFILEDATA;
25 
26 
27 static
28 BOOL
29 OnProfileTypeInit(
30     _In_ HWND hwndDlg,
31     _In_ PPROFILEDATA pProfileData)
32 {
33     PWSTR pszRawBuffer = NULL, pszCookedBuffer = NULL;
34     INT nLength;
35 
36     nLength = LoadStringW(hApplet, IDS_USERPROFILE_TYPE_TEXT, (PWSTR)&pszRawBuffer, 0);
37     pszRawBuffer = NULL;
38     if (nLength == 0)
39         return FALSE;
40 
41     pszRawBuffer = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(WCHAR));
42     if (pszRawBuffer == NULL)
43         return FALSE;
44 
45     LoadStringW(hApplet, IDS_USERPROFILE_TYPE_TEXT, pszRawBuffer, nLength + 1);
46 
47     pszCookedBuffer = HeapAlloc(GetProcessHeap(), 0, (nLength + wcslen(pProfileData->pszFullName) + 1) * sizeof(WCHAR));
48     if (pszCookedBuffer == NULL)
49         goto done;
50 
51     swprintf(pszCookedBuffer, pszRawBuffer, pProfileData->pszFullName);
52 
53     /* Set the full text */
54     SetDlgItemText(hwndDlg, IDC_USERPROFILE_TYPE_TEXT, pszCookedBuffer);
55 
56     /* FIXME: Right now, we support local user profiles only! */
57     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_TYPE_ROAMING), FALSE);
58     Button_SetCheck(GetDlgItem(hwndDlg, IDC_USERPROFILE_TYPE_LOCAL), BST_CHECKED);
59     EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
60 
61 done:
62     if (pszCookedBuffer != NULL)
63         HeapFree(GetProcessHeap(), 0, pszCookedBuffer);
64 
65     if (pszRawBuffer != NULL)
66         HeapFree(GetProcessHeap(), 0, pszRawBuffer);
67 
68     return TRUE;
69 }
70 
71 
72 static
73 INT_PTR
74 CALLBACK
75 UserProfileTypeDlgProc(
76     _In_ HWND hwndDlg,
77     _In_ UINT uMsg,
78     _In_ WPARAM wParam,
79     _In_ LPARAM lParam)
80 {
81     switch (uMsg)
82     {
83         case WM_INITDIALOG:
84             OnProfileTypeInit(hwndDlg, (PPROFILEDATA)lParam);
85             return TRUE;
86 
87         case WM_DESTROY:
88             break;
89 
90         case WM_COMMAND:
91             switch (LOWORD(wParam))
92             {
93                 case IDOK:
94                 case IDCANCEL:
95                     EndDialog(hwndDlg,
96                               LOWORD(wParam));
97                     return TRUE;
98             }
99             break;
100     }
101 
102     return FALSE;
103 }
104 
105 
106 static
107 BOOL
108 ChangeUserProfileType(
109     _In_ HWND hwndDlg)
110 {
111     HWND hwndListView;
112     LVITEM Item;
113     INT iSelected;
114 
115     DPRINT("ChangeUserProfileType(%p)\n", hwndDlg);
116 
117     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
118     if (hwndListView == NULL)
119         return FALSE;
120 
121     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
122     if (iSelected == -1)
123         return FALSE;
124 
125     ZeroMemory(&Item, sizeof(LVITEM));
126     Item.mask = LVIF_PARAM;
127     Item.iItem = iSelected;
128     Item.iSubItem = 0;
129     if (!ListView_GetItem(hwndListView, &Item))
130         return FALSE;
131 
132     if (Item.lParam == 0)
133         return FALSE;
134 
135     if (DialogBoxParam(hApplet,
136                        MAKEINTRESOURCE(IDD_USERPROFILE_TYPE),
137                        hwndDlg,
138                        UserProfileTypeDlgProc,
139                        (LPARAM)Item.lParam) == IDOK)
140     {
141         /* FIXME: Update the profile list view */
142         return TRUE;
143     }
144 
145     return FALSE;
146 }
147 
148 
149 static
150 BOOL
151 DeleteUserProfile(
152     _In_ HWND hwndDlg)
153 {
154     WCHAR szTitle[64], szRawText[128], szCookedText[256];
155     HWND hwndListView;
156     LVITEM Item;
157     INT iSelected;
158     PPROFILEDATA pProfileData;
159 
160     DPRINT("DeleteUserProfile()\n");
161 
162     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
163     if (hwndListView == NULL)
164         return FALSE;
165 
166     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
167     if (iSelected == -1)
168         return FALSE;
169 
170     ZeroMemory(&Item, sizeof(LVITEM));
171     Item.mask = LVIF_PARAM;
172     Item.iItem = iSelected;
173     Item.iSubItem = 0;
174     if (!ListView_GetItem(hwndListView, &Item))
175         return FALSE;
176 
177     if (Item.lParam == 0)
178         return FALSE;
179 
180     pProfileData = (PPROFILEDATA)Item.lParam;
181     if (pProfileData->dwRefCount != 0)
182         return FALSE;
183 
184     LoadStringW(hApplet, IDS_USERPROFILE_CONFIRM_DELETE_TITLE, szTitle, ARRAYSIZE(szTitle));
185     LoadStringW(hApplet, IDS_USERPROFILE_CONFIRM_DELETE, szRawText, ARRAYSIZE(szRawText));
186     swprintf(szCookedText, szRawText, pProfileData->pszFullName);
187 
188     if (MessageBoxW(hwndDlg,
189                     szCookedText,
190                     szTitle,
191                     MB_ICONQUESTION | MB_YESNO) == IDYES)
192     {
193         /* FIXME: Delete the profile here! */
194         return TRUE;
195     }
196 
197     return FALSE;
198 }
199 
200 
201 static
202 INT_PTR
203 CALLBACK
204 CopyUserProfileDlgProc(
205     _In_ HWND hwndDlg,
206     _In_ UINT uMsg,
207     _In_ WPARAM wParam,
208     _In_ LPARAM lParam)
209 {
210     switch (uMsg)
211     {
212         case WM_INITDIALOG:
213             return TRUE;
214 
215         case WM_DESTROY:
216             break;
217 
218         case WM_COMMAND:
219             switch (LOWORD(wParam))
220             {
221                 case IDOK:
222                 case IDCANCEL:
223                     EndDialog(hwndDlg,
224                               LOWORD(wParam));
225                     return TRUE;
226             }
227             break;
228     }
229 
230     return FALSE;
231 }
232 
233 
234 static
235 BOOL
236 CopyUserProfile(
237     _In_ HWND hwndDlg)
238 {
239     HWND hwndListView;
240     LVITEM Item;
241     INT iSelected;
242 
243     DPRINT("CopyUserProfile()\n");
244 
245     hwndListView = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
246     if (hwndListView == NULL)
247         return FALSE;
248 
249     iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
250     if (iSelected == -1)
251         return FALSE;
252 
253     ZeroMemory(&Item, sizeof(LVITEM));
254     Item.mask = LVIF_PARAM;
255     Item.iItem = iSelected;
256     Item.iSubItem = 0;
257     if (!ListView_GetItem(hwndListView, &Item))
258         return FALSE;
259 
260     if (Item.lParam == 0)
261         return FALSE;
262 
263     if (DialogBoxParam(hApplet,
264                        MAKEINTRESOURCE(IDD_USERPROFILE_COPY),
265                        hwndDlg,
266                        CopyUserProfileDlgProc,
267                        (LPARAM)Item.lParam) == IDOK)
268     {
269         /* FIXME: Update the profile list view */
270         return TRUE;
271     }
272 
273     return FALSE;
274 }
275 
276 
277 static VOID
278 SetListViewColumns(
279     _In_ HWND hwndListView)
280 {
281     LV_COLUMN column;
282     RECT rect;
283     TCHAR szStr[32];
284 
285     GetClientRect(hwndListView, &rect);
286 
287     SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
288 
289     memset(&column, 0x00, sizeof(column));
290     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
291     column.fmt = LVCFMT_LEFT;
292     column.cx = (INT)((rect.right - rect.left) * 0.40);
293     column.iSubItem = 0;
294     LoadString(hApplet, IDS_USERPROFILE_NAME, szStr, ARRAYSIZE(szStr));
295     column.pszText = szStr;
296     (void)ListView_InsertColumn(hwndListView, 0, &column);
297 
298     column.fmt = LVCFMT_RIGHT;
299     column.cx = (INT)((rect.right - rect.left) * 0.15);
300     column.iSubItem = 1;
301     LoadString(hApplet, IDS_USERPROFILE_SIZE, szStr, ARRAYSIZE(szStr));
302     column.pszText = szStr;
303     (void)ListView_InsertColumn(hwndListView, 1, &column);
304 
305     column.fmt = LVCFMT_LEFT;
306     column.cx = (INT)((rect.right - rect.left) * 0.15);
307     column.iSubItem = 2;
308     LoadString(hApplet, IDS_USERPROFILE_TYPE, szStr, ARRAYSIZE(szStr));
309     column.pszText = szStr;
310     (void)ListView_InsertColumn(hwndListView, 2, &column);
311 
312     column.fmt = LVCFMT_LEFT;
313     column.cx = (INT)((rect.right - rect.left) * 0.15);
314     column.iSubItem = 3;
315     LoadString(hApplet, IDS_USERPROFILE_STATUS, szStr, ARRAYSIZE(szStr));
316     column.pszText = szStr;
317     (void)ListView_InsertColumn(hwndListView, 3, &column);
318 
319     column.fmt = LVCFMT_LEFT;
320     column.cx = (INT)((rect.right - rect.left) * 0.15) - GetSystemMetrics(SM_CYHSCROLL);
321     column.iSubItem = 4;
322     LoadString(hApplet, IDS_USERPROFILE_MODIFIED, szStr, ARRAYSIZE(szStr));
323     column.pszText = szStr;
324     (void)ListView_InsertColumn(hwndListView, 4, &column);
325 }
326 
327 
328 static
329 BOOL
330 GetProfileSize(
331     _In_ PWSTR pszProfilePath,
332     _Inout_ PULONGLONG pullProfileSize)
333 {
334     HANDLE hFile = INVALID_HANDLE_VALUE;
335     WIN32_FIND_DATA FindData;
336     DWORD dwProfilePathLength;
337     ULARGE_INTEGER Size;
338     BOOL bResult = TRUE;
339 
340     dwProfilePathLength = wcslen(pszProfilePath);
341 
342     wcscat(pszProfilePath, L"\\*.*");
343 
344     hFile = FindFirstFileW(pszProfilePath, &FindData);
345     if (hFile == INVALID_HANDLE_VALUE)
346     {
347         if ((GetLastError() != ERROR_FILE_NOT_FOUND) &&
348             (GetLastError() != ERROR_PATH_NOT_FOUND))
349             bResult = FALSE;
350 
351         goto done;
352     }
353 
354     do
355     {
356         if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
357         {
358             if ((_wcsicmp(FindData.cFileName, L".") == 0) ||
359                 (_wcsicmp(FindData.cFileName, L"..") == 0))
360                 continue;
361 
362             pszProfilePath[dwProfilePathLength + 1] = UNICODE_NULL;
363             wcscat(pszProfilePath, FindData.cFileName);
364 
365             if (!GetProfileSize(pszProfilePath, pullProfileSize))
366             {
367                 bResult = FALSE;
368                 goto done;
369             }
370         }
371         else
372         {
373             Size.u.LowPart = FindData.nFileSizeLow;
374             Size.u.HighPart = FindData.nFileSizeHigh;
375             *pullProfileSize += Size.QuadPart;
376         }
377     }
378     while (FindNextFile(hFile, &FindData));
379 
380 done:
381     pszProfilePath[dwProfilePathLength] = UNICODE_NULL;
382 
383     if (hFile != INVALID_HANDLE_VALUE)
384         FindClose(hFile);
385 
386     return bResult;
387 }
388 
389 
390 static
391 BOOL
392 GetProfileName(
393     _In_ PSID pProfileSid,
394     _In_ DWORD dwNameBufferSize,
395     _Out_ PWSTR pszNameBuffer,
396     _Out_ PBOOL pbUnknownProfile)
397 {
398     WCHAR szAccountName[128], szDomainName[128];
399     DWORD dwAccountNameSize, dwDomainNameSize;
400     SID_NAME_USE Use;
401 
402     dwAccountNameSize = ARRAYSIZE(szAccountName);
403     dwDomainNameSize = ARRAYSIZE(szDomainName);
404     if (!LookupAccountSidW(NULL,
405                            pProfileSid,
406                            szAccountName,
407                            &dwAccountNameSize,
408                            szDomainName,
409                            &dwDomainNameSize,
410                            &Use))
411     {
412         /* Unknown account */
413         LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_UNKNOWN, pszNameBuffer, dwNameBufferSize);
414         *pbUnknownProfile = TRUE;
415     }
416     else
417     {
418         /* Show only the user accounts */
419         if (Use != SidTypeUser)
420             return FALSE;
421 
422         if (szAccountName[0] == UNICODE_NULL)
423         {
424             /* Deleted account */
425             LoadStringW(hApplet, IDS_USERPROFILE_ACCOUNT_DELETED, pszNameBuffer, dwNameBufferSize);
426         }
427         else
428         {
429             /* Normal account */
430             wsprintf(pszNameBuffer, L"%s\\%s", szDomainName, szAccountName);
431         }
432         *pbUnknownProfile = FALSE;
433     }
434 
435     return TRUE;
436 }
437 
438 
439 static
440 VOID
441 FormatProfileSize(
442     _Out_ LPWSTR Buffer,
443     _In_ double size)
444 {
445     const LPWSTR units[] = {L"MB", L"GB", L"TB"};
446     int i = 0, j;
447 
448     size /= 1024;
449     size /= 1024;
450 
451     while (size >= 1024 && i < 3)
452     {
453         size /= 1024;
454         i++;
455     }
456 
457     if (size < 10)
458         j = 2;
459     else if (size < 100)
460         j = 1;
461     else
462         j = 0;
463 
464     swprintf(Buffer, L"%.*f %s", j, size, units[i]);
465 }
466 
467 
468 static VOID
469 AddUserProfile(
470     _In_ HWND hwndListView,
471     _In_ PSID pProfileSid,
472     _In_ HKEY hProfileKey)
473 {
474     WCHAR szTempProfilePath[MAX_PATH], szProfilePath[MAX_PATH];
475     WCHAR szNameBuffer[256];
476     PPROFILEDATA pProfileData = NULL;
477     DWORD dwProfileData, dwSize, dwType, dwState = 0, dwRefCount = 0;
478     DWORD dwProfilePathLength;
479     PWSTR ptr;
480     INT nId, iItem;
481     LV_ITEM lvi;
482     WIN32_FIND_DATA FindData;
483     HANDLE hFile;
484     SYSTEMTIME SystemTime;
485     ULONGLONG ullProfileSize;
486     BOOL bUnknownProfile;
487     DWORD dwError;
488 
489     /* Get the profile path */
490     dwSize = MAX_PATH * sizeof(WCHAR);
491     dwError = RegQueryValueExW(hProfileKey,
492                                L"ProfileImagePath",
493                                NULL,
494                                &dwType,
495                                (LPBYTE)szTempProfilePath,
496                                &dwSize);
497     if (dwError != ERROR_SUCCESS)
498         return;
499 
500     /* Expand it */
501     ExpandEnvironmentStringsW(szTempProfilePath,
502                               szProfilePath,
503                               MAX_PATH);
504 
505     /* Check if the profile path exists */
506     hFile = FindFirstFileW(szProfilePath, &FindData);
507     if (hFile == INVALID_HANDLE_VALUE)
508         return;
509 
510     FindClose(hFile);
511 
512     /* Get the length of the profile path */
513     dwProfilePathLength = wcslen(szProfilePath);
514 
515     /* Check for the ntuser.dat file */
516     wcscat(szProfilePath, L"\\ntuser.dat");
517     hFile = FindFirstFileW(szProfilePath, &FindData);
518     if (hFile == INVALID_HANDLE_VALUE)
519         return;
520 
521     FindClose(hFile);
522     szProfilePath[dwProfilePathLength] = UNICODE_NULL;
523 
524     /* Get the profile size */
525     ullProfileSize = 0ULL;
526     GetProfileSize(szProfilePath, &ullProfileSize);
527 
528     /* Get the profile name */
529     if (!GetProfileName(pProfileSid,
530                         ARRAYSIZE(szNameBuffer),
531                         szNameBuffer,
532                         &bUnknownProfile))
533         return;
534 
535     /* Get the profile state value */
536     dwSize = sizeof(dwState);
537     if (RegQueryValueExW(hProfileKey,
538                          L"State",
539                          NULL,
540                          &dwType,
541                          (LPBYTE)&dwState,
542                          &dwSize) != ERROR_SUCCESS)
543     {
544         dwState = 0;
545     }
546 
547     /* Get the profile reference counter */
548     dwSize = sizeof(dwRefCount);
549     if (RegQueryValueExW(hProfileKey,
550                          L"RefCount",
551                          NULL,
552                          &dwType,
553                          (LPBYTE)&dwRefCount,
554                          &dwSize) != ERROR_SUCCESS)
555     {
556         dwRefCount = 0;
557     }
558 
559     /* Create and fill the profile data entry */
560     dwProfileData = sizeof(PROFILEDATA) +
561                     ((wcslen(szNameBuffer) + 1) * sizeof(WCHAR)) +
562                     ((wcslen(szProfilePath) + 1) * sizeof(WCHAR));
563     pProfileData = HeapAlloc(GetProcessHeap(),
564                              HEAP_ZERO_MEMORY,
565                              dwProfileData);
566     if (pProfileData == NULL)
567         return;
568 
569     pProfileData->dwRefCount = dwRefCount;
570     pProfileData->dwState = dwState;
571     pProfileData->bUnknownProfile = bUnknownProfile;
572 
573     ptr = (PWSTR)((ULONG_PTR)pProfileData + sizeof(PROFILEDATA));
574     pProfileData->pszFullName = ptr;
575 
576     wcscpy(pProfileData->pszFullName, szNameBuffer);
577 
578     ptr = (PWSTR)((ULONG_PTR)ptr + ((wcslen(pProfileData->pszFullName) + 1) * sizeof(WCHAR)));
579     pProfileData->pszProfilePath = ptr;
580     wcscpy(pProfileData->pszProfilePath, szProfilePath);
581 
582     /* Add the profile and set its name */
583     memset(&lvi, 0x00, sizeof(lvi));
584     lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
585     lvi.pszText = pProfileData->pszFullName;
586     lvi.state = 0;
587     lvi.lParam = (LPARAM)pProfileData;
588     iItem = ListView_InsertItem(hwndListView, &lvi);
589 
590     /* Set the profile size */
591     FormatProfileSize(szNameBuffer, (double)ullProfileSize);
592     ListView_SetItemText(hwndListView, iItem, 1, szNameBuffer);
593 
594     /* Set the profile type */
595     if (dwState & 0x0010) // PROFILE_UPDATE_CENTRAL
596         nId = IDS_USERPROFILE_ROAMING;
597     else
598         nId = IDS_USERPROFILE_LOCAL;
599 
600     LoadStringW(hApplet, nId, szNameBuffer, ARRAYSIZE(szNameBuffer));
601 
602     ListView_SetItemText(hwndListView, iItem, 2, szNameBuffer);
603 
604     /* FIXME: Set the profile status */
605     if (dwState & 0x0001) // PROFILE_MANDATORY
606         nId = IDS_USERPROFILE_MANDATORY;
607     else if (dwState & 0x0010) // PROFILE_UPDATE_CENTRAL
608         nId = IDS_USERPROFILE_ROAMING;
609     else
610         nId = IDS_USERPROFILE_LOCAL;
611 
612     LoadStringW(hApplet, nId, szNameBuffer, ARRAYSIZE(szNameBuffer));
613 
614     ListView_SetItemText(hwndListView, iItem, 3, szNameBuffer);
615 
616     /* Set the profile modified time */
617     FileTimeToSystemTime(&FindData.ftLastWriteTime,
618                          &SystemTime);
619 
620     GetDateFormatW(LOCALE_USER_DEFAULT,
621                    DATE_SHORTDATE,
622                    &SystemTime,
623                    NULL,
624                    szNameBuffer,
625                    ARRAYSIZE(szNameBuffer));
626 
627     ListView_SetItemText(hwndListView, iItem, 4, szNameBuffer);
628 }
629 
630 
631 static VOID
632 UpdateButtonState(
633     _In_ HWND hwndDlg,
634     _In_ HWND hwndListView)
635 {
636     LVITEM Item;
637     INT iSelected;
638     BOOL bChange = FALSE;
639     BOOL bCopy = FALSE;
640     BOOL bDelete = FALSE;
641     PPROFILEDATA pProfileData;
642 
643     if (ListView_GetSelectedCount(hwndListView) != 0)
644     {
645         iSelected = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED);
646         if (iSelected != -1)
647         {
648             Item.mask = LVIF_PARAM;
649             Item.iItem = iSelected;
650             Item.iSubItem = 0;
651             if (ListView_GetItem(hwndListView, &Item))
652             {
653                 if (Item.lParam != 0)
654                 {
655                     pProfileData = (PPROFILEDATA)Item.lParam;
656 
657                     if (pProfileData->bUnknownProfile)
658                     {
659                         bDelete = TRUE;
660                         bCopy = FALSE;
661                     }
662                     else
663                     {
664                         bDelete = (pProfileData->dwRefCount == 0);
665                         bCopy = (pProfileData->dwRefCount == 0);
666                     }
667                 }
668             }
669 
670             bChange = TRUE;
671         }
672     }
673 
674     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_CHANGE), bChange);
675     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), bDelete);
676     EnableWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), bCopy);
677 }
678 
679 
680 static VOID
681 AddUserProfiles(
682     _In_ HWND hwndDlg,
683     _In_ HWND hwndListView,
684     _In_ BOOL bAdmin)
685 {
686     HKEY hKeyUserProfiles = INVALID_HANDLE_VALUE;
687     HKEY hProfileKey;
688     DWORD dwIndex;
689     WCHAR szProfileSid[64];
690     DWORD dwSidLength;
691     FILETIME ftLastWrite;
692     DWORD dwSize;
693     HANDLE hToken = NULL;
694     PTOKEN_USER pTokenUser = NULL;
695     PSID pProfileSid;
696     PWSTR pszProfileSid;
697 
698     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
699         return;
700 
701     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
702     if (dwSize == 0)
703         goto done;
704 
705     pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwSize);
706     if (pTokenUser == NULL)
707         goto done;
708 
709     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
710         goto done;
711 
712     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
713                       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
714                       0,
715                       KEY_READ,
716                       &hKeyUserProfiles))
717         goto done;
718 
719     if (bAdmin)
720     {
721         for (dwIndex = 0; ; dwIndex++)
722         {
723             dwSidLength = ARRAYSIZE(szProfileSid);
724             if (RegEnumKeyExW(hKeyUserProfiles,
725                               dwIndex,
726                               szProfileSid,
727                               &dwSidLength,
728                               NULL,
729                               NULL,
730                               NULL,
731                               &ftLastWrite))
732                 break;
733 
734             if (RegOpenKeyExW(hKeyUserProfiles,
735                               szProfileSid,
736                               0,
737                               KEY_READ,
738                               &hProfileKey) == ERROR_SUCCESS)
739             {
740                 if (ConvertStringSidToSid(szProfileSid, &pProfileSid))
741                 {
742                     AddUserProfile(hwndListView,
743                                    pProfileSid,
744                                    hProfileKey);
745                     LocalFree(pProfileSid);
746                 }
747 
748                 RegCloseKey(hProfileKey);
749             }
750         }
751     }
752     else
753     {
754         if (ConvertSidToStringSidW(pTokenUser->User.Sid, &pszProfileSid))
755         {
756             if (RegOpenKeyExW(hKeyUserProfiles,
757                               pszProfileSid,
758                               0,
759                               KEY_READ,
760                               &hProfileKey) == ERROR_SUCCESS)
761             {
762                 AddUserProfile(hwndListView,
763                                pTokenUser->User.Sid,
764                                hProfileKey);
765                 RegCloseKey(hProfileKey);
766             }
767 
768             LocalFree(pszProfileSid);
769         }
770     }
771 
772     if (ListView_GetItemCount(hwndListView) != 0)
773         ListView_SetItemState(hwndListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
774 
775     UpdateButtonState(hwndDlg, hwndListView);
776 
777 done:
778     if (hKeyUserProfiles != INVALID_HANDLE_VALUE)
779         RegCloseKey(hKeyUserProfiles);
780 
781     if (pTokenUser != NULL)
782         HeapFree(GetProcessHeap(), 0, pTokenUser);
783 
784     if (hToken != NULL)
785         CloseHandle(hToken);
786 }
787 
788 
789 static VOID
790 OnInitUserProfileDialog(HWND hwndDlg)
791 {
792     BOOL bAdmin;
793 
794     bAdmin = IsUserAdmin();
795 
796     /* Initialize the list view control */
797     SetListViewColumns(GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST));
798 
799     /* Hide the delete and copy buttons for non-admins */
800     ShowWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_DELETE), bAdmin ? SW_SHOW : SW_HIDE);
801     ShowWindow(GetDlgItem(hwndDlg, IDC_USERPROFILE_COPY), bAdmin ? SW_SHOW : SW_HIDE);
802 
803     /* Add the profiles to the list view */
804     AddUserProfiles(hwndDlg, GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST), bAdmin);
805 }
806 
807 
808 static
809 VOID
810 OnDestroy(
811     _In_ HWND hwndDlg)
812 {
813     HWND hwndList;
814     INT nItems, i;
815     LVITEM Item;
816 
817     hwndList = GetDlgItem(hwndDlg, IDC_USERPROFILE_LIST);
818 
819     nItems = ListView_GetItemCount(hwndList);
820     for (i = 0; i < nItems; i++)
821     {
822         Item.iItem = i;
823         Item.iSubItem = 0;
824         if (ListView_GetItem(hwndList, &Item))
825         {
826             if (Item.lParam != 0)
827                 HeapFree(GetProcessHeap(), 0, (PVOID)Item.lParam);
828         }
829     }
830 }
831 
832 
833 static
834 VOID
835 OnNotify(
836     _In_ HWND hwndDlg,
837     _In_ NMHDR *nmhdr)
838 {
839     if (nmhdr->idFrom == IDC_USERACCOUNT_LINK && nmhdr->code == NM_CLICK)
840     {
841         ShellExecuteW(hwndDlg, NULL, L"usrmgr.cpl", NULL, NULL, 0);
842     }
843     else if (nmhdr->idFrom == IDC_USERPROFILE_LIST)
844     {
845         if (nmhdr->code == LVN_ITEMCHANGED)
846             UpdateButtonState(hwndDlg, nmhdr->hwndFrom);
847         else if (nmhdr->code == NM_DBLCLK)
848             ChangeUserProfileType(hwndDlg);
849     }
850 }
851 
852 
853 /* Property page dialog callback */
854 INT_PTR CALLBACK
855 UserProfileDlgProc(HWND hwndDlg,
856                    UINT uMsg,
857                    WPARAM wParam,
858                    LPARAM lParam)
859 {
860     switch (uMsg)
861     {
862         case WM_INITDIALOG:
863             OnInitUserProfileDialog(hwndDlg);
864             return TRUE;
865 
866         case WM_DESTROY:
867             OnDestroy(hwndDlg);
868             break;
869 
870         case WM_COMMAND:
871             switch (LOWORD(wParam))
872             {
873                 case IDOK:
874                 case IDCANCEL:
875                     EndDialog(hwndDlg,
876                               LOWORD(wParam));
877                     return TRUE;
878 
879                 case IDC_USERPROFILE_CHANGE:
880                     ChangeUserProfileType(hwndDlg);
881                     break;
882 
883                 case IDC_USERPROFILE_DELETE:
884                     DeleteUserProfile(hwndDlg);
885                     break;
886 
887                 case IDC_USERPROFILE_COPY:
888                     CopyUserProfile(hwndDlg);
889                     break;
890             }
891             break;
892 
893         case WM_NOTIFY:
894             OnNotify(hwndDlg, (NMHDR *)lParam);
895             break;
896     }
897 
898     return FALSE;
899 }
900