xref: /reactos/dll/cpl/usrmgr/groupprops.c (revision ba3f0743)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS User Manager Control Panel
4  * FILE:            dll/cpl/usrmgr/groupprops.c
5  * PURPOSE:         Group property sheet
6  *
7  * PROGRAMMERS:     Eric Kohl
8  */
9 
10 #include "usrmgr.h"
11 
12 typedef struct _GENERAL_GROUP_DATA
13 {
14     TCHAR szGroupName[1];
15 } GENERAL_GROUP_DATA, *PGENERAL_GROUP_DATA;
16 
17 
18 static VOID
19 GetTextSid(PSID pSid,
20            LPTSTR pTextSid)
21 {
22     PSID_IDENTIFIER_AUTHORITY psia;
23     DWORD dwSubAuthorities;
24     DWORD dwSidRev = SID_REVISION;
25     DWORD dwCounter;
26     DWORD dwSidSize;
27 
28     psia = GetSidIdentifierAuthority(pSid);
29 
30     dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
31 
32     dwSidSize = wsprintf(pTextSid, TEXT("S-%lu-"), dwSidRev);
33 
34     if ((psia->Value[0] != 0) || (psia->Value[1] != 0))
35     {
36         dwSidSize += wsprintf(pTextSid + lstrlen(pTextSid),
37                               TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
38                               (USHORT)psia->Value[0],
39                               (USHORT)psia->Value[1],
40                               (USHORT)psia->Value[2],
41                               (USHORT)psia->Value[3],
42                               (USHORT)psia->Value[4],
43                               (USHORT)psia->Value[5]);
44     }
45     else
46     {
47         dwSidSize += wsprintf(pTextSid + lstrlen(pTextSid),
48                               TEXT("%lu"),
49                               (ULONG)(psia->Value[5]) +
50                               (ULONG)(psia->Value[4] <<  8) +
51                               (ULONG)(psia->Value[3] << 16) +
52                               (ULONG)(psia->Value[2] << 24));
53     }
54 
55     for (dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++)
56     {
57         dwSidSize += wsprintf(pTextSid + dwSidSize, TEXT("-%lu"),
58                               *GetSidSubAuthority(pSid, dwCounter));
59     }
60 }
61 
62 
63 static VOID
64 InitGroupMembersList(HWND hwndDlg,
65                      PGENERAL_GROUP_DATA pGroupData)
66 {
67     HWND hwndLV;
68     LV_COLUMN column;
69     RECT rect;
70     TCHAR szStr[32];
71     HIMAGELIST hImgList;
72     HICON hIcon;
73 
74     NET_API_STATUS netStatus;
75     PUSER_INFO_20 pUserBuffer;
76     DWORD entriesread;
77     DWORD totalentries;
78     DWORD resume_handle = 0;
79     DWORD i;
80     LV_ITEM lvi;
81     INT iItem;
82 
83     hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
84     GetClientRect(hwndLV, &rect);
85 
86     hImgList = ImageList_Create(16,16,ILC_COLOR32 | ILC_MASK,5,5);
87     hIcon = LoadImage(hApplet,MAKEINTRESOURCE(IDI_GROUP),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
88     ImageList_AddIcon(hImgList,hIcon);
89     DestroyIcon(hIcon);
90     hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
91     ImageList_AddIcon(hImgList, hIcon);
92     DestroyIcon(hIcon);
93     hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_LOCKED_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
94     ImageList_AddIcon(hImgList, hIcon);
95     DestroyIcon(hIcon);
96 
97     (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
98     (void)ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT);
99 
100     memset(&column, 0x00, sizeof(column));
101     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
102     column.fmt = LVCFMT_LEFT;
103     column.cx = (INT)((rect.right - rect.left) * 0.40);
104     column.iSubItem = 0;
105     LoadString(hApplet, IDS_NAME, szStr, sizeof(szStr) / sizeof(szStr[0]));
106     column.pszText = szStr;
107     (void)ListView_InsertColumn(hwndLV, 0, &column);
108 
109     column.cx = (INT)((rect.right - rect.left) * 0.60);
110     column.iSubItem = 1;
111     LoadString(hApplet, IDS_DESCRIPTION, szStr, sizeof(szStr) / sizeof(szStr[0]));
112     column.pszText = szStr;
113     (void)ListView_InsertColumn(hwndLV, 1, &column);
114 
115     /* TODO: Enumerate global groups and add them to the list! */
116 
117     for (;;)
118     {
119         netStatus = NetUserEnum(NULL, 20, FILTER_NORMAL_ACCOUNT,
120                                 (LPBYTE*)&pUserBuffer,
121                                 1024, &entriesread,
122                                 &totalentries, &resume_handle);
123         if (netStatus != NERR_Success && netStatus != ERROR_MORE_DATA)
124             break;
125 
126         for (i = 0; i < entriesread; i++)
127         {
128            memset(&lvi, 0x00, sizeof(lvi));
129            lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
130            lvi.pszText = pUserBuffer[i].usri20_name;
131            lvi.state = 0;
132            lvi.iImage = (pUserBuffer[i].usri20_flags & UF_ACCOUNTDISABLE) ? 2 : 1;
133            iItem = ListView_InsertItem(hwndLV, &lvi);
134 
135            ListView_SetItemText(hwndLV, iItem, 1,
136                                 pUserBuffer[i].usri20_full_name);
137 
138            ListView_SetItemText(hwndLV, iItem, 2,
139                                 pUserBuffer[i].usri20_comment);
140         }
141 
142         NetApiBufferFree(pUserBuffer);
143 
144         /* No more data left */
145         if (netStatus != ERROR_MORE_DATA)
146             break;
147     }
148 }
149 
150 
151 static BOOL
152 AddSelectedUsersToGroup(HWND hwndDlg,
153                         PGENERAL_GROUP_DATA pGroupData)
154 {
155     HWND hwndLV;
156     INT nSelectedItems;
157     INT nItem;
158     TCHAR szUserName[UNLEN];
159     BOOL bResult = FALSE;
160     LOCALGROUP_MEMBERS_INFO_3 memberInfo;
161     NET_API_STATUS status;
162 
163     hwndLV = GetDlgItem(hwndDlg, IDC_USER_ADD_MEMBERSHIP_LIST);
164 
165     nSelectedItems = ListView_GetSelectedCount(hwndLV);
166     if (nSelectedItems > 0)
167     {
168         nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
169         while (nItem != -1)
170         {
171             /* Get the new user name */
172             ListView_GetItemText(hwndLV,
173                                  nItem, 0,
174                                  szUserName,
175                                  UNLEN);
176 
177             DebugPrintf(_TEXT("Selected user: %s"), szUserName);
178 
179             memberInfo.lgrmi3_domainandname = szUserName;
180 
181             status = NetLocalGroupAddMembers(NULL, pGroupData->szGroupName, 3,
182                                              (LPBYTE)&memberInfo, 1);
183             if (status != NERR_Success && status != ERROR_MEMBER_IN_ALIAS)
184             {
185                 TCHAR szText[256];
186                 wsprintf(szText, TEXT("Error: %u"), status);
187                 MessageBox(NULL, szText, TEXT("NetLocalGroupAddMembers"), MB_ICONERROR | MB_OK);
188             }
189             else
190             {
191                 bResult = TRUE;
192             }
193 
194             nItem = ListView_GetNextItem(hwndLV, nItem, LVNI_SELECTED);
195         }
196     }
197 
198     return bResult;
199 }
200 
201 
202 INT_PTR CALLBACK
203 AddUsersToGroupDlgProc(HWND hwndDlg,
204                       UINT uMsg,
205                       WPARAM wParam,
206                       LPARAM lParam)
207 {
208     PGENERAL_GROUP_DATA pGroupData;
209 
210     UNREFERENCED_PARAMETER(wParam);
211 
212     pGroupData = (PGENERAL_GROUP_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
213 
214     switch (uMsg)
215     {
216         case WM_INITDIALOG:
217             pGroupData = (PGENERAL_GROUP_DATA)lParam;
218             SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pGroupData);
219             InitGroupMembersList(hwndDlg, pGroupData);
220             break;
221 
222         case WM_COMMAND:
223             switch (LOWORD(wParam))
224             {
225                 case IDOK:
226                     if (AddSelectedUsersToGroup(hwndDlg, pGroupData))
227                         EndDialog(hwndDlg, IDOK);
228                     else
229                         EndDialog(hwndDlg, IDCANCEL);
230                     break;
231 
232                 case IDCANCEL:
233                     EndDialog(hwndDlg, IDCANCEL);
234                     break;
235             }
236             break;
237 
238         default:
239             return FALSE;
240     }
241 
242     return TRUE;
243 }
244 
245 
246 static VOID
247 AddUsersToGroup(HWND hwndDlg,
248                 PGENERAL_GROUP_DATA pGroupData)
249 {
250     HWND hwndLV;
251 //    NET_API_STATUS status;
252     PLOCALGROUP_MEMBERS_INFO_1 membersInfo = NULL;
253     DWORD dwRead;
254     DWORD dwTotal;
255     DWORD_PTR resumeHandle = 0;
256     DWORD i;
257     LV_ITEM lvi;
258     TCHAR szGroupName[256];
259 
260     if (DialogBoxParam(hApplet,
261                        MAKEINTRESOURCE(IDD_USER_ADD_MEMBERSHIP),
262                        hwndDlg,
263                        AddUsersToGroupDlgProc,
264                        (LPARAM)pGroupData) == IDOK)
265     {
266         hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
267 
268         (void)ListView_DeleteAllItems(hwndLV);
269 
270 //        DebugPrintf(_T("Removed all users from the list!"));
271 
272         /* Set group members */
273         NetLocalGroupGetMembers(NULL, pGroupData->szGroupName, 1, (LPBYTE*)&membersInfo,
274                                 MAX_PREFERRED_LENGTH, &dwRead, &dwTotal,
275                                 &resumeHandle);
276 
277         for (i = 0; i < dwRead; i++)
278         {
279             ZeroMemory(&lvi, sizeof(lvi));
280             lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
281             lvi.pszText = membersInfo[i].lgrmi1_name;
282             lvi.state = 0;
283             lvi.iImage = (membersInfo[i].lgrmi1_sidusage == SidTypeGroup ||
284                           membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup) ? 1 : 0;
285 
286             if (membersInfo[i].lgrmi1_sidusage == SidTypeWellKnownGroup)
287             {
288                 TCHAR szSid[256];
289 
290                 GetTextSid(membersInfo[i].lgrmi1_sid, szSid);
291 
292                 wsprintf(szGroupName,
293                          TEXT("%s (%s)"),
294                          membersInfo[i].lgrmi1_name,
295                          szSid);
296 
297                 lvi.pszText = szGroupName;
298             }
299 
300 
301             (void)ListView_InsertItem(hwndLV, &lvi);
302         }
303 
304         NetApiBufferFree(membersInfo);
305     }
306 }
307 
308 
309 static VOID
310 RemoveUserFromGroup(HWND hwndDlg,
311                     PGENERAL_GROUP_DATA pGroupData)
312 {
313     TCHAR szUserName[UNLEN];
314     TCHAR szText[256];
315     LOCALGROUP_MEMBERS_INFO_3 memberInfo;
316     HWND hwndLV;
317     INT nItem;
318     NET_API_STATUS status;
319 
320     hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
321     nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
322     if (nItem == -1)
323         return;
324 
325     /* Get the new user name */
326     ListView_GetItemText(hwndLV,
327                          nItem, 0,
328                          szUserName,
329                          UNLEN);
330 
331     /* Display a warning message because the remove operation cannot be reverted */
332     wsprintf(szText, TEXT("Do you really want to remove the user \"%s\" from the group \"%s\"?"),
333              szUserName, pGroupData->szGroupName);
334     if (MessageBox(NULL, szText, TEXT("User Accounts"), MB_ICONWARNING | MB_YESNO) == IDNO)
335         return;
336 
337     memberInfo.lgrmi3_domainandname = szUserName;
338 
339     status = NetLocalGroupDelMembers(NULL, pGroupData->szGroupName,
340                                      3, (LPBYTE)&memberInfo, 1);
341     if (status != NERR_Success)
342     {
343         TCHAR szText[256];
344         wsprintf(szText, TEXT("Error: %u"), status);
345         MessageBox(NULL, szText, TEXT("NetLocalGroupDelMembers"), MB_ICONERROR | MB_OK);
346         return;
347     }
348 
349     (void)ListView_DeleteItem(hwndLV, nItem);
350 
351     if (ListView_GetItemCount(hwndLV) == 0)
352         EnableWindow(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_REMOVE), FALSE);
353 }
354 
355 
356 static BOOL
357 OnGroupPropSheetNotify(HWND hwndDlg,
358                        PGENERAL_GROUP_DATA pGroupData,
359                        LPARAM lParam)
360 {
361     LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
362 
363     switch (((LPNMHDR)lParam)->idFrom)
364     {
365         case IDC_GROUP_GENERAL_MEMBERS:
366             switch (((LPNMHDR)lParam)->code)
367             {
368                 case NM_CLICK:
369                     EnableWindow(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_REMOVE), (lpnmlv->iItem != -1));
370                     break;
371 
372                 case LVN_KEYDOWN:
373                     if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE)
374                     {
375                         RemoveUserFromGroup(hwndDlg, pGroupData);
376                     }
377                     break;
378 
379             }
380             break;
381     }
382 
383     return FALSE;
384 }
385 
386 
387 static VOID
388 GetGeneralGroupData(HWND hwndDlg,
389                     PGENERAL_GROUP_DATA pGroupData)
390 {
391     PLOCALGROUP_INFO_1 groupInfo = NULL;
392     PLOCALGROUP_MEMBERS_INFO_2 membersInfo = NULL;
393     DWORD dwRead;
394     DWORD dwTotal;
395     DWORD_PTR resumeHandle = 0;
396     DWORD i;
397     LV_ITEM lvi;
398     HWND hwndLV;
399     LV_COLUMN column;
400     RECT rect;
401     HIMAGELIST hImgList;
402     HICON hIcon;
403     TCHAR szGroupName[256];
404 
405 
406     hwndLV = GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_MEMBERS);
407 
408     /* Create the image list */
409     hImgList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 5, 5);
410     hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_GROUP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
411     ImageList_AddIcon(hImgList, hIcon);
412     DestroyIcon(hIcon);
413     hIcon = LoadImage(hApplet, MAKEINTRESOURCE(IDI_USER), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
414     ImageList_AddIcon(hImgList, hIcon);
415     DestroyIcon(hIcon);
416 
417     (void)ListView_SetImageList(hwndLV, hImgList, LVSIL_SMALL);
418 
419     /* Set the list column */
420     GetClientRect(hwndLV, &rect);
421 
422     memset(&column, 0x00, sizeof(column));
423     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
424     column.fmt = LVCFMT_LEFT;
425     column.cx = (INT)(rect.right - rect.left);
426     column.iSubItem = 0;
427     (void)ListView_InsertColumn(hwndLV, 0, &column);
428 
429     /* Set group name */
430     SetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_NAME, pGroupData->szGroupName);
431 
432     /* Set group description */
433     NetLocalGroupGetInfo(NULL, pGroupData->szGroupName, 1, (LPBYTE*)&groupInfo);
434     SetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION, groupInfo->lgrpi1_comment);
435     NetApiBufferFree(groupInfo);
436 
437     /* Set group members */
438     NetLocalGroupGetMembers(NULL, pGroupData->szGroupName, 2, (LPBYTE*)&membersInfo,
439                             MAX_PREFERRED_LENGTH, &dwRead, &dwTotal,
440                             &resumeHandle);
441 
442     for (i = 0; i < dwRead; i++)
443     {
444         ZeroMemory(&lvi, sizeof(lvi));
445         lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
446         lvi.state = 0;
447         if (membersInfo[i].lgrmi2_sidusage == SidTypeGroup ||
448             membersInfo[i].lgrmi2_sidusage == SidTypeWellKnownGroup)
449         {
450             lvi.iImage = 0;
451         }
452         else if (membersInfo[i].lgrmi2_sidusage == SidTypeUser)
453         {
454             /* FIXME: handle locked user properly! */
455             lvi.iImage = 1;
456         }
457 
458         if (membersInfo[i].lgrmi2_sidusage == SidTypeWellKnownGroup)
459         {
460             TCHAR szSid[256];
461 
462             GetTextSid(membersInfo[i].lgrmi2_sid, szSid);
463 
464             wsprintf(szGroupName,
465                      TEXT("%s (%s)"),
466                      membersInfo[i].lgrmi2_domainandname,
467                      szSid);
468 
469             lvi.pszText = szGroupName;
470         }
471         else
472         {
473             LPWSTR ptr;
474 
475             ptr = wcschr(membersInfo[i].lgrmi2_domainandname, L'\\');
476             if (ptr != NULL)
477             {
478                 lvi.pszText = ++ptr;
479             }
480             else
481             {
482                 lvi.pszText = membersInfo[i].lgrmi2_domainandname;
483             }
484         }
485 
486         (void)ListView_InsertItem(hwndLV, &lvi);
487     }
488 
489     NetApiBufferFree(membersInfo);
490 }
491 
492 
493 static BOOL
494 SetGeneralGroupData(HWND hwndDlg,
495                     PGENERAL_GROUP_DATA pGroupData)
496 {
497     LOCALGROUP_INFO_1 groupInfo;
498     LPTSTR pszComment = NULL;
499     INT nLength;
500     NET_API_STATUS status;
501     DWORD dwIndex;
502 
503     /* Get the group description */
504     nLength = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION));
505     if (nLength == 0)
506     {
507         groupInfo.lgrpi1_comment = NULL;
508     }
509     else
510     {
511         pszComment = HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(TCHAR));
512         GetDlgItemText(hwndDlg, IDC_GROUP_GENERAL_DESCRIPTION, pszComment, nLength + 1);
513         groupInfo.lgrpi1_comment = pszComment;
514     }
515 
516     status = NetLocalGroupSetInfo(NULL, pGroupData->szGroupName, 1, (LPBYTE)&groupInfo, &dwIndex);
517     if (status != NERR_Success)
518     {
519         DebugPrintf(_T("Status: %lu  Index: %lu"), status, dwIndex);
520     }
521 
522     if (pszComment)
523         HeapFree(GetProcessHeap(), 0, pszComment);
524 
525     return TRUE;
526 }
527 
528 
529 INT_PTR CALLBACK
530 GroupGeneralPageProc(HWND hwndDlg,
531                      UINT uMsg,
532                      WPARAM wParam,
533                      LPARAM lParam)
534 {
535     PGENERAL_GROUP_DATA pGroupData;
536 
537     UNREFERENCED_PARAMETER(lParam);
538     UNREFERENCED_PARAMETER(wParam);
539     UNREFERENCED_PARAMETER(hwndDlg);
540 
541     pGroupData= (PGENERAL_GROUP_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
542 
543     switch (uMsg)
544     {
545         case WM_INITDIALOG:
546             pGroupData = (PGENERAL_GROUP_DATA)HeapAlloc(GetProcessHeap(),
547                                                         HEAP_ZERO_MEMORY,
548                                                         sizeof(GENERAL_GROUP_DATA) +
549                                                         lstrlen((LPTSTR)((PROPSHEETPAGE *)lParam)->lParam) * sizeof(TCHAR));
550             lstrcpy(pGroupData->szGroupName, (LPTSTR)((PROPSHEETPAGE *)lParam)->lParam);
551 
552             SetWindowLongPtr(hwndDlg, DWLP_USER, (INT_PTR)pGroupData);
553 
554             GetGeneralGroupData(hwndDlg,
555                                 pGroupData);
556             break;
557 
558         case WM_COMMAND:
559             switch (LOWORD(wParam))
560             {
561                 case IDC_GROUP_GENERAL_DESCRIPTION:
562                     if (HIWORD(wParam) == EN_CHANGE)
563                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
564                     break;
565 
566                 case IDC_GROUP_GENERAL_ADD:
567                     AddUsersToGroup(hwndDlg, pGroupData);
568                     break;
569 
570                 case IDC_GROUP_GENERAL_REMOVE:
571                     RemoveUserFromGroup(hwndDlg, pGroupData);
572                     break;
573             }
574             break;
575 
576         case WM_NOTIFY:
577             if (((LPPSHNOTIFY)lParam)->hdr.code == PSN_APPLY)
578             {
579                 SetGeneralGroupData(hwndDlg, pGroupData);
580                 return TRUE;
581             }
582             else
583             {
584                 return OnGroupPropSheetNotify(hwndDlg, pGroupData, lParam);
585             }
586             break;
587 
588         case WM_DESTROY:
589             HeapFree(GetProcessHeap(), 0, pGroupData);
590             break;
591     }
592 
593     return FALSE;
594 }
595 
596 
597 static VOID
598 InitGroupPropSheetPage(PROPSHEETPAGE *psp, WORD idDlg, DLGPROC DlgProc, LPTSTR pszGroup)
599 {
600     ZeroMemory(psp, sizeof(PROPSHEETPAGE));
601     psp->dwSize = sizeof(PROPSHEETPAGE);
602     psp->dwFlags = PSP_DEFAULT;
603     psp->hInstance = hApplet;
604     psp->pszTemplate = MAKEINTRESOURCE(idDlg);
605     psp->pfnDlgProc = DlgProc;
606     psp->lParam = (LPARAM)pszGroup;
607 }
608 
609 
610 BOOL
611 GroupProperties(HWND hwndDlg)
612 {
613     PROPSHEETPAGE psp[1];
614     PROPSHEETHEADER psh;
615     TCHAR szGroupName[UNLEN];
616     INT nItem;
617     HWND hwndLV;
618 
619     hwndLV = GetDlgItem(hwndDlg, IDC_GROUPS_LIST);
620     nItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED);
621     if (nItem == -1)
622         return FALSE;
623 
624     /* Get the new user name */
625     ListView_GetItemText(hwndLV,
626                          nItem, 0,
627                          szGroupName,
628                          UNLEN);
629 
630     ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
631     psh.dwSize = sizeof(PROPSHEETHEADER);
632     psh.dwFlags =  PSH_PROPSHEETPAGE | PSH_PROPTITLE;
633     psh.hwndParent = hwndDlg;
634     psh.hInstance = hApplet;
635     psh.hIcon = NULL;
636     psh.pszCaption = szGroupName;
637     psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
638     psh.nStartPage = 0;
639     psh.ppsp = psp;
640 
641     InitGroupPropSheetPage(&psp[0], IDD_GROUP_GENERAL, GroupGeneralPageProc, szGroupName);
642 
643     return (PropertySheet(&psh) == IDOK);
644 }
645