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