xref: /reactos/dll/win32/shell32/wine/control.c (revision 93d8a1b7)
1 /* Control Panel management
2  *
3  * Copyright 2001 Eric Pouech
4  * Copyright 2008 Owen Rudge
5  * Copyright 2022 Raymond Czerny
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <assert.h>
23 
24 #define WIN32_NO_STATUS
25 #define _INC_WINDOWS
26 
27 #include <windef.h>
28 #include <winbase.h>
29 #define NO_SHLWAPI_REG
30 #include <shlwapi.h>
31 #include <shellapi.h>
32 #include <wine/debug.h>
33 
34 #include <strsafe.h>
35 
36 #include "cpanel.h"
37 #include "wine/unicode.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
40 
41 void Control_UnloadApplet(CPlApplet* applet)
42 {
43     unsigned	i;
44 
45     for (i = 0; i < applet->count; i++)
46         applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].data);
47 
48     if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
49     FreeLibrary(applet->hModule);
50 #ifndef __REACTOS__
51     list_remove( &applet->entry );
52 #endif
53     HeapFree(GetProcessHeap(), 0, applet->cmd);
54     HeapFree(GetProcessHeap(), 0, applet);
55 }
56 
57 CPlApplet*	Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
58 {
59 #ifdef __REACTOS__
60     ACTCTXW ActCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID};
61     ULONG_PTR cookie;
62     BOOL bActivated;
63     WCHAR fileBuffer[MAX_PATH];
64 #endif
65     CPlApplet*	applet;
66     DWORD len;
67     unsigned 	i;
68     CPLINFO	info;
69     NEWCPLINFOW newinfo;
70 
71     if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
72        return applet;
73 
74     len = ExpandEnvironmentStringsW(cmd, NULL, 0);
75     if (len > 0)
76     {
77         if (!(applet->cmd = HeapAlloc(GetProcessHeap(), 0, (len+1) * sizeof(WCHAR))))
78         {
79             WARN("Cannot allocate memory for applet path\n");
80             goto theError;
81         }
82         ExpandEnvironmentStringsW(cmd, applet->cmd, len+1);
83     }
84     else
85     {
86         WARN("Cannot expand applet path\n");
87         goto theError;
88     }
89 
90     applet->hWnd = hWnd;
91 
92 #ifdef __REACTOS__
93     StringCchCopy(fileBuffer, MAX_PATH, applet->cmd);
94     if (PathIsFileSpecW(fileBuffer))
95         SearchPath(NULL, fileBuffer, NULL, MAX_PATH, fileBuffer, NULL);
96     ActCtx.lpSource = fileBuffer;
97     ActCtx.lpResourceName = (LPCWSTR)123;
98     applet->hActCtx = CreateActCtx(&ActCtx);
99     bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE);
100 #endif
101 
102     if (!(applet->hModule = LoadLibraryW(applet->cmd))) {
103         WARN("Cannot load control panel applet %s\n", debugstr_w(applet->cmd));
104 	goto theError;
105     }
106     if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
107         WARN("Not a valid control panel applet %s\n", debugstr_w(applet->cmd));
108 	goto theError;
109     }
110     if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) {
111         WARN("Init of applet has failed\n");
112 	goto theError;
113     }
114     if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) {
115         WARN("No subprogram in applet\n");
116         applet->proc(applet->hWnd, CPL_EXIT, 0, 0);
117 	goto theError;
118     }
119 
120     applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
121                          FIELD_OFFSET( CPlApplet, info[applet->count] ));
122 
123     for (i = 0; i < applet->count; i++) {
124        ZeroMemory(&newinfo, sizeof(newinfo));
125        newinfo.dwSize = sizeof(NEWCPLINFOA);
126        applet->info[i].helpfile[0] = 0;
127        /* proc is supposed to return a null value upon success for
128 	* CPL_INQUIRE and CPL_NEWINQUIRE
129 	* However, real drivers don't seem to behave like this
130 	* So, use introspection rather than return value
131 	*/
132        applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
133        applet->info[i].data = info.lData;
134 #ifdef __REACTOS__
135        applet->info[i].idIcon = info.idIcon;
136 #endif
137 
138        if (info.idIcon != CPL_DYNAMIC_RES)
139 	   applet->info[i].icon = LoadIconW(applet->hModule, MAKEINTRESOURCEW(info.idIcon));
140        if (info.idName != CPL_DYNAMIC_RES)
141 	   LoadStringW(applet->hModule, info.idName,
142 		       applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
143        if (info.idInfo != CPL_DYNAMIC_RES)
144 	   LoadStringW(applet->hModule, info.idInfo,
145 		       applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
146 
147        /* some broken control panels seem to return incorrect values in CPL_INQUIRE,
148           but proper data in CPL_NEWINQUIRE. if we get an empty string or a null
149           icon, see what we can get from CPL_NEWINQUIRE */
150 
151        if (!applet->info[i].name[0]) info.idName = CPL_DYNAMIC_RES;
152 
153        /* zero-length szInfo may not be a buggy applet, but it doesn't hurt for us
154           to check anyway */
155 
156        if (!applet->info[i].info[0]) info.idInfo = CPL_DYNAMIC_RES;
157 
158        if (applet->info[i].icon == NULL)
159            info.idIcon = CPL_DYNAMIC_RES;
160 
161        if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) ||
162            (info.idInfo == CPL_DYNAMIC_RES)) {
163 	   applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);
164 
165 	   applet->info[i].data = newinfo.lData;
166 	   if (info.idIcon == CPL_DYNAMIC_RES) {
167 	       if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i);
168 	       applet->info[i].icon = newinfo.hIcon;
169 	   }
170 	   if (newinfo.dwSize == sizeof(NEWCPLINFOW)) {
171 	       if (info.idName == CPL_DYNAMIC_RES)
172 	           memcpy(applet->info[i].name, newinfo.szName, sizeof(newinfo.szName));
173 	       if (info.idInfo == CPL_DYNAMIC_RES)
174 	           memcpy(applet->info[i].info, newinfo.szInfo, sizeof(newinfo.szInfo));
175 	       memcpy(applet->info[i].helpfile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile));
176 	   } else {
177 	       if (info.idName == CPL_DYNAMIC_RES)
178                    MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName,
179 	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR),
180 			               applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR));
181 	       if (info.idInfo == CPL_DYNAMIC_RES)
182                    MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo,
183 	                               sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR),
184 			               applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR));
185                MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile,
186 	                           sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR),
187 			           applet->info[i].helpfile,
188                                    sizeof(applet->info[i].helpfile) / sizeof(WCHAR));
189            }
190        }
191     }
192 
193 #ifdef __REACTOS__
194     if (bActivated)
195         DeactivateActCtx(0, cookie);
196 #endif
197 
198 #ifndef __REACTOS__
199     list_add_head( &panel->applets, &applet->entry );
200 #endif
201 
202     return applet;
203 
204  theError:
205     FreeLibrary(applet->hModule);
206     HeapFree(GetProcessHeap(), 0, applet->cmd);
207     HeapFree(GetProcessHeap(), 0, applet);
208     return NULL;
209 }
210 
211 #ifndef __REACTOS__
212 
213 #define IDC_LISTVIEW        1000
214 #define IDC_STATUSBAR       1001
215 
216 #define NUM_COLUMNS            2
217 #define LISTVIEW_DEFSTYLE   (WS_CHILD | WS_VISIBLE | WS_TABSTOP |\
218                              LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL)
219 
220 static BOOL Control_CreateListView (CPanel *panel)
221 {
222     RECT ws, sb;
223     WCHAR empty_string[] = {0};
224     WCHAR buf[MAX_STRING_LEN];
225     LVCOLUMNW lvc;
226 
227     /* Create list view */
228     GetClientRect(panel->hWndStatusBar, &sb);
229     GetClientRect(panel->hWnd, &ws);
230 
231     panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
232                           empty_string, LISTVIEW_DEFSTYLE | LVS_ICON,
233                           0, 0, ws.right - ws.left, ws.bottom - ws.top -
234                           (sb.bottom - sb.top), panel->hWnd,
235                           (HMENU) IDC_LISTVIEW, panel->hInst, NULL);
236 
237     if (!panel->hWndListView)
238         return FALSE;
239 
240     /* Create image lists for list view */
241     panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
242         GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1);
243     panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
244         GetSystemMetrics(SM_CYICON), ILC_COLOR32 | ILC_MASK, 1, 1);
245 
246     SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall);
247     SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge);
248 
249     /* Create columns for list view */
250     lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
251     lvc.pszText = buf;
252     lvc.fmt = LVCFMT_LEFT;
253 
254     /* Name column */
255     lvc.iSubItem = 0;
256     lvc.cx = (ws.right - ws.left) / 3;
257     LoadStringW(shell32_hInstance, IDS_CPANEL_NAME, buf, sizeof(buf) / sizeof(buf[0]));
258 
259     if (ListView_InsertColumnW(panel->hWndListView, 0, &lvc) == -1)
260         return FALSE;
261 
262     /* Description column */
263     lvc.iSubItem = 1;
264     lvc.cx = ((ws.right - ws.left) / 3) * 2;
265     LoadStringW(shell32_hInstance, IDS_CPANEL_DESCRIPTION, buf, sizeof(buf) /
266         sizeof(buf[0]));
267 
268     if (ListView_InsertColumnW(panel->hWndListView, 1, &lvc) == -1)
269         return FALSE;
270 
271     return(TRUE);
272 }
273 
274 static void 	 Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
275 {
276    CPanel* panel = cs->lpCreateParams;
277    HMENU hMenu, hSubMenu;
278    CPlApplet* applet;
279    MENUITEMINFOW mii;
280    unsigned int i;
281    int menucount, index;
282    CPlItem *item;
283    LVITEMW lvItem;
284    INITCOMMONCONTROLSEX icex;
285    INT sb_parts;
286    int itemidx;
287 
288    SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
289    panel->hWnd = hWnd;
290 
291    /* Initialise common control DLL */
292    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
293    icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
294    InitCommonControlsEx(&icex);
295 
296    /* create the status bar */
297    if (!(panel->hWndStatusBar = CreateStatusWindowW(WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, NULL, hWnd, IDC_STATUSBAR)))
298        return;
299 
300    sb_parts = -1;
301    SendMessageW(panel->hWndStatusBar, SB_SETPARTS, 1, (LPARAM) &sb_parts);
302 
303    /* create the list view */
304    if (!Control_CreateListView(panel))
305        return;
306 
307    hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));
308 
309    /* insert menu items for applets */
310    hSubMenu = GetSubMenu(hMenu, 0);
311    menucount = 0;
312 
313    LIST_FOR_EACH_ENTRY( applet, &panel->applets, CPlApplet, entry )
314    {
315       for (i = 0; i < applet->count; i++) {
316          /* set up a CPlItem for this particular subprogram */
317          item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem));
318 
319          if (!item)
320             continue;
321 
322          item->applet = applet;
323          item->id = i;
324 
325          mii.cbSize = sizeof(MENUITEMINFOW);
326          mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
327          mii.dwTypeData = applet->info[i].name;
328          mii.cch = sizeof(applet->info[i].name) / sizeof(WCHAR);
329          mii.wID = IDM_CPANEL_APPLET_BASE + menucount;
330          mii.dwItemData = (ULONG_PTR)item;
331 
332          if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) {
333             /* add the list view item */
334             HICON icon = applet->info[i].icon;
335             if (!icon) icon = LoadImageW( 0, (LPCWSTR)IDI_WINLOGO, IMAGE_ICON, 0, 0, LR_SHARED );
336             index = ImageList_AddIcon(panel->hImageListLarge, icon);
337             ImageList_AddIcon(panel->hImageListSmall, icon);
338 
339             lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
340             lvItem.iItem = menucount;
341             lvItem.iSubItem = 0;
342             lvItem.pszText = applet->info[i].name;
343             lvItem.iImage = index;
344             lvItem.lParam = (LPARAM) item;
345 
346             itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem);
347 
348             /* add the description */
349             ListView_SetItemTextW(panel->hWndListView, itemidx, 1, applet->info[i].info);
350 
351             /* update menu bar, increment count */
352             DrawMenuBar(hWnd);
353             menucount++;
354          }
355       }
356    }
357 
358    panel->total_subprogs = menucount;
359 
360    /* check the "large items" icon in the View menu */
361    hSubMenu = GetSubMenu(hMenu, 1);
362    CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW,
363       FCIDM_SHVIEW_BIGICON, MF_BYCOMMAND);
364 
365    SetMenu(hWnd, hMenu);
366 }
367 
368 static void Control_FreeCPlItems(HWND hWnd, CPanel *panel)
369 {
370     HMENU hMenu, hSubMenu;
371     MENUITEMINFOW mii;
372     unsigned int i;
373 
374     /* get the File menu */
375     hMenu = GetMenu(hWnd);
376 
377     if (!hMenu)
378         return;
379 
380     hSubMenu = GetSubMenu(hMenu, 0);
381 
382     if (!hSubMenu)
383         return;
384 
385     /* loop and free the item data */
386     for (i = IDM_CPANEL_APPLET_BASE; i <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs; i++)
387     {
388         mii.cbSize = sizeof(MENUITEMINFOW);
389         mii.fMask = MIIM_DATA;
390 
391         if (!GetMenuItemInfoW(hSubMenu, i, FALSE, &mii))
392             continue;
393 
394         HeapFree(GetProcessHeap(), 0, (LPVOID) mii.dwItemData);
395     }
396 }
397 
398 static void Control_UpdateListViewStyle(CPanel *panel, UINT style, UINT id)
399 {
400     HMENU hMenu, hSubMenu;
401 
402     SetWindowLongW(panel->hWndListView, GWL_STYLE, LISTVIEW_DEFSTYLE | style);
403 
404     /* update the menu */
405     hMenu = GetMenu(panel->hWnd);
406     hSubMenu = GetSubMenu(hMenu, 1);
407 
408     CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW,
409         id, MF_BYCOMMAND);
410 }
411 
412 static CPlItem* Control_GetCPlItem_From_MenuID(HWND hWnd, UINT id)
413 {
414     HMENU hMenu, hSubMenu;
415     MENUITEMINFOW mii;
416 
417     /* retrieve the CPlItem structure from the menu item data */
418     hMenu = GetMenu(hWnd);
419 
420     if (!hMenu)
421         return NULL;
422 
423     hSubMenu = GetSubMenu(hMenu, 0);
424 
425     if (!hSubMenu)
426         return NULL;
427 
428     mii.cbSize = sizeof(MENUITEMINFOW);
429     mii.fMask = MIIM_DATA;
430 
431     if (!GetMenuItemInfoW(hSubMenu, id, FALSE, &mii))
432         return NULL;
433 
434     return (CPlItem *) mii.dwItemData;
435 }
436 
437 static CPlItem* Control_GetCPlItem_From_ListView(CPanel *panel)
438 {
439     LVITEMW lvItem;
440     int selitem;
441 
442     selitem = SendMessageW(panel->hWndListView, LVM_GETNEXTITEM, -1, LVNI_FOCUSED
443         | LVNI_SELECTED);
444 
445     if (selitem != -1)
446     {
447         lvItem.iItem = selitem;
448         lvItem.mask = LVIF_PARAM;
449 
450         if (SendMessageW(panel->hWndListView, LVM_GETITEMW, 0, (LPARAM) &lvItem))
451             return (CPlItem *) lvItem.lParam;
452     }
453 
454     return NULL;
455 }
456 
457 static void Control_StartApplet(HWND hWnd, CPlItem *item)
458 {
459     WCHAR verbOpen[] = {'c','p','l','o','p','e','n',0};
460     WCHAR format[] = {'@','%','d',0};
461     WCHAR param[MAX_PATH];
462 
463     /* execute the applet if item is valid */
464     if (item)
465     {
466         wsprintfW(param, format, item->id);
467         ShellExecuteW(hWnd, verbOpen, item->applet->cmd, param, NULL, SW_SHOW);
468     }
469 }
470 
471 static LRESULT WINAPI	Control_WndProc(HWND hWnd, UINT wMsg,
472 					WPARAM lParam1, LPARAM lParam2)
473 {
474    CPanel*	panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
475 
476    if (panel || wMsg == WM_CREATE) {
477       switch (wMsg) {
478       case WM_CREATE:
479 	 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
480 	 return 0;
481       case WM_DESTROY:
482          {
483              CPlApplet *applet, *next;
484              LIST_FOR_EACH_ENTRY_SAFE( applet, next, &panel->applets, CPlApplet, entry )
485                  Control_UnloadApplet(applet);
486          }
487          Control_FreeCPlItems(hWnd, panel);
488          PostQuitMessage(0);
489 	 break;
490       case WM_COMMAND:
491          switch (LOWORD(lParam1))
492          {
493              case IDM_CPANEL_EXIT:
494                  SendMessageW(hWnd, WM_CLOSE, 0, 0);
495                  return 0;
496 
497              case IDM_CPANEL_ABOUT:
498                  {
499                      WCHAR appName[MAX_STRING_LEN];
500                      HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL),
501                                              IMAGE_ICON, 48, 48, LR_SHARED);
502 
503                      LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName,
504                          sizeof(appName) / sizeof(appName[0]));
505                      ShellAboutW(hWnd, appName, NULL, icon);
506 
507                      return 0;
508                  }
509 
510              case FCIDM_SHVIEW_BIGICON:
511                  Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON);
512                  return 0;
513 
514              case FCIDM_SHVIEW_SMALLICON:
515                  Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON);
516                  return 0;
517 
518              case FCIDM_SHVIEW_LISTVIEW:
519                  Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW);
520                  return 0;
521 
522              case FCIDM_SHVIEW_REPORTVIEW:
523                  Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW);
524                  return 0;
525 
526              default:
527                  /* check if this is an applet */
528                  if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) &&
529                      (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs))
530                  {
531                      Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)));
532                      return 0;
533                  }
534 
535                  break;
536          }
537 
538          break;
539 
540       case WM_NOTIFY:
541       {
542           LPNMHDR nmh = (LPNMHDR) lParam2;
543 
544           switch (nmh->idFrom)
545           {
546               case IDC_LISTVIEW:
547                   switch (nmh->code)
548                   {
549                       case NM_RETURN:
550                       case NM_DBLCLK:
551                       {
552                           Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel));
553                           return 0;
554                       }
555                       case LVN_ITEMCHANGED:
556                       {
557                           CPlItem *item = Control_GetCPlItem_From_ListView(panel);
558 
559                           /* update the status bar if item is valid */
560                           if (item)
561                               SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
562                           else
563                               SetWindowTextW(panel->hWndStatusBar, NULL);
564 
565                           return 0;
566                       }
567                   }
568 
569                   break;
570           }
571 
572           break;
573       }
574 
575       case WM_MENUSELECT:
576           /* check if this is an applet */
577           if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) &&
578               (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs))
579           {
580               CPlItem *item = Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1));
581 
582               /* update the status bar if item is valid */
583               if (item)
584                   SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
585           }
586           else if ((HIWORD(lParam1) == 0xFFFF) && (lParam2 == 0))
587           {
588               /* reset status bar description to that of the selected icon */
589               CPlItem *item = Control_GetCPlItem_From_ListView(panel);
590 
591               if (item)
592                   SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info);
593               else
594                   SetWindowTextW(panel->hWndStatusBar, NULL);
595 
596               return 0;
597           }
598           else
599               SetWindowTextW(panel->hWndStatusBar, NULL);
600 
601           return 0;
602 
603       case WM_SIZE:
604       {
605           HDWP hdwp;
606           RECT sb;
607 
608           hdwp = BeginDeferWindowPos(2);
609 
610           if (hdwp == NULL)
611               break;
612 
613           GetClientRect(panel->hWndStatusBar, &sb);
614 
615           hdwp = DeferWindowPos(hdwp, panel->hWndListView, NULL, 0, 0,
616               LOWORD(lParam2), HIWORD(lParam2) - (sb.bottom - sb.top),
617               SWP_NOZORDER | SWP_NOMOVE);
618 
619           if (hdwp == NULL)
620               break;
621 
622           hdwp = DeferWindowPos(hdwp, panel->hWndStatusBar, NULL, 0, 0,
623               LOWORD(lParam2), LOWORD(lParam1), SWP_NOZORDER | SWP_NOMOVE);
624 
625           if (hdwp != NULL)
626               EndDeferWindowPos(hdwp);
627 
628           return 0;
629       }
630      }
631    }
632 
633    return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
634 }
635 
636 static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
637 {
638     WNDCLASSEXW wc;
639     MSG		msg;
640     WCHAR appName[MAX_STRING_LEN];
641     const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o',
642         'l','_','W','n','d','C','l','a','s','s',0};
643 
644     LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0]));
645 
646     wc.cbSize = sizeof(wc);
647     wc.style = CS_HREDRAW|CS_VREDRAW;
648     wc.lpfnWndProc = Control_WndProc;
649     wc.cbClsExtra = 0;
650     wc.cbWndExtra = sizeof(CPlApplet*);
651     wc.hInstance = panel->hInst = hInst;
652     wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) );
653     wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
654     wc.hbrBackground = GetStockObject(WHITE_BRUSH);
655     wc.lpszMenuName = NULL;
656     wc.lpszClassName = className;
657     wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON,
658                              GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
659 
660     if (!RegisterClassExW(&wc)) return;
661 
662     CreateWindowExW(0, wc.lpszClassName, appName,
663 		    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
664 		    CW_USEDEFAULT, CW_USEDEFAULT,
665 		    CW_USEDEFAULT, CW_USEDEFAULT,
666 		    hWnd, NULL, hInst, panel);
667     if (!panel->hWnd) return;
668 
669     while (GetMessageW(&msg, panel->hWnd, 0, 0)) {
670         TranslateMessage(&msg);
671         DispatchMessageW(&msg);
672     }
673 }
674 
675 static void Control_RegisterRegistryApplets(HWND hWnd, CPanel *panel, HKEY hkey_root, LPCWSTR szRepPath)
676 {
677     WCHAR name[MAX_PATH];
678     WCHAR value[MAX_PATH];
679     HKEY hkey;
680 
681     if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
682     {
683         int idx = 0;
684 
685         for(;; ++idx)
686         {
687             DWORD nameLen = MAX_PATH;
688             DWORD valueLen = MAX_PATH;
689 
690             if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
691                 break;
692 
693             Control_LoadApplet(hWnd, value, panel);
694         }
695         RegCloseKey(hkey);
696     }
697 }
698 
699 static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
700 {
701     static const WCHAR wszRegPath[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls";
702     HANDLE		h;
703     WIN32_FIND_DATAW	fd;
704     WCHAR		buffer[MAX_PATH];
705     WCHAR *p;
706 
707     /* first add .cpl files in the system directory */
708     GetSystemDirectoryW( buffer, MAX_PATH );
709     p = buffer + strlenW(buffer);
710     *p++ = '\\';
711     lstrcpyW(p, L"*.cpl");
712 
713     if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
714         do {
715 	   lstrcpyW(p, fd.cFileName);
716 	   Control_LoadApplet(hWnd, buffer, panel);
717 	} while (FindNextFileW(h, &fd));
718 	FindClose(h);
719     }
720 
721     /* now check for cpls in the registry */
722     Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath);
723     Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath);
724 
725     Control_DoInterface(panel, hWnd, hInst);
726 }
727 
728 #else
729 static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
730 {
731     ShellExecuteW(NULL,
732                   L"open",
733                   L"explorer.exe",
734                   L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
735                   NULL,
736                   SW_SHOWDEFAULT);
737 }
738 #endif
739 
740 #ifdef __REACTOS__
741 
742 /** Structure for in and out data when
743  *  search for the cpl dialog of first instance
744  */
745 typedef struct tagAppDlgFindData
746 {
747     PCWSTR    szAppFile;  /**< Full path to applet library as search parameter */
748     UINT_PTR  sAppletNo;  /**< Number of applet in a system control library as search parameter */
749     ATOM      aCPLName;   /**< to read window property 'CPLName' */
750     ATOM      aCPLFlags;  /**< to read window property 'CPLFlags'*/
751     HWND      hRunDLL;    /**< to skip self instance */
752     HWND      hDlgResult; /**< Returned dialog handle or NULL if not found */
753 } AppDlgFindData;
754 
755 /**
756  * Callback function to search applet dialog
757  * @param hwnd A handle to a top-level window.
758  * @param lParam Pointer of AppDlgFindData
759  * @return TRUE: continue enumeration, FALSE: stop enumeration
760  */
761 static BOOL CALLBACK
762 Control_EnumWinProc(
763     _In_ HWND   hwnd,
764     _In_ LPARAM lParam)
765 {
766     AppDlgFindData* pData = (AppDlgFindData*)lParam;
767     WCHAR szClassName[256] = L"";
768 
769     if (pData->hRunDLL == hwnd)
770     {
771         // Skip self instance
772         return TRUE;
773     }
774 
775     if (GetClassNameW(hwnd, szClassName, _countof(szClassName)))
776     {
777         // Note: A comparison on identical is not possible, the class names are different.
778         // ReactOS: 'rundll32_window'
779         // WinXP: 'RunDLL'
780         // other OS: not checked
781         if (StrStrIW(szClassName, L"rundll32") != NULL)
782         {
783             UINT_PTR sAppletNo;
784 
785             sAppletNo = (UINT_PTR)GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLFlags));
786             if (sAppletNo == pData->sAppletNo)
787             {
788                 HANDLE hRes;
789                 WCHAR szAppFile[MAX_PATH];
790 
791                 hRes = GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLName));
792                 GlobalGetAtomNameW((ATOM)HandleToUlong(hRes), szAppFile, _countof(szAppFile));
793                 if (wcscmp(szAppFile, pData->szAppFile) == 0)
794                 {
795                     HWND hDialog = GetLastActivePopup(hwnd);
796                     if (IsWindow(hDialog))
797                     {
798                         pData->hDlgResult = hDialog;
799                         return FALSE; // stop enumeration
800                     }
801                 }
802             }
803         }
804     }
805     return TRUE; // continue enumeration
806 }
807 #endif /* __REACTOS__ */
808 
809 static	void	Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
810    /* forms to parse:
811     *	foo.cpl,@sp,str
812     *	foo.cpl,@sp
813     *	foo.cpl,,str
814     *	foo.cpl @sp
815     *	foo.cpl str
816     *   "a path\foo.cpl"
817     */
818 {
819     LPWSTR	buffer;
820     LPWSTR	beg = NULL;
821     LPWSTR	end;
822     WCHAR	ch;
823     LPWSTR       ptr;
824     signed 	sp = -1;
825     LPWSTR	extraPmtsBuf = NULL;
826     LPWSTR	extraPmts = NULL;
827     BOOL        quoted = FALSE;
828     CPlApplet *applet;
829 
830     buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
831     if (!buffer) return;
832 
833     end = lstrcpyW(buffer, wszCmd);
834 
835     for (;;) {
836         ch = *end;
837         if (ch == '"') quoted = !quoted;
838         if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
839             *end = '\0';
840             if (beg) {
841                 if (*beg == '@') {
842                     sp = atoiW(beg + 1);
843                 } else if (*beg == '\0') {
844                     sp = -1;
845                 } else {
846                     extraPmtsBuf = beg;
847                 }
848             }
849             if (ch == '\0') break;
850             beg = end + 1;
851             if (ch == ' ') while (end[1] == ' ') end++;
852         }
853         end++;
854     }
855     while ((ptr = StrChrW(buffer, '"')))
856 	memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
857 
858     /* now check for any quotes in extraPmtsBuf and remove */
859     if (extraPmtsBuf != NULL)
860     {
861         beg = end = extraPmtsBuf;
862         quoted = FALSE;
863 
864         for (;;) {
865             ch = *end;
866             if (ch == '"') quoted = !quoted;
867             if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
868                 *end = '\0';
869                 if (beg) {
870                     if (*beg != '\0') {
871                         extraPmts = beg;
872                     }
873                 }
874                 if (ch == '\0') break;
875                 beg = end + 1;
876                 if (ch == ' ') while (end[1] == ' ') end++;
877             }
878             end++;
879         }
880 
881         while ((ptr = StrChrW(extraPmts, '"')))
882             memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
883 
884         if (extraPmts == NULL)
885             extraPmts = extraPmtsBuf;
886     }
887 
888     /* Now check if there had been a numerical value in the extra params */
889     if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) {
890         sp = atoiW(extraPmts + 1);
891     }
892 
893     TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp);
894 
895     applet = Control_LoadApplet(hWnd, buffer, panel);
896     if (applet)
897     {
898 #ifdef __REACTOS__
899     ULONG_PTR cookie;
900     BOOL bActivated;
901     ATOM aCPLName;
902     ATOM aCPLFlags;
903     ATOM aCPLPath;
904     AppDlgFindData findData;
905 #endif
906         /* we've been given a textual parameter (or none at all) */
907         if (sp == -1) {
908             while ((++sp) != applet->count) {
909                 TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name));
910 
911                 if (StrCmpIW(extraPmts, applet->info[sp].name) == 0)
912                     break;
913             }
914         }
915 
916         if (sp >= applet->count) {
917             WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
918             sp = 0;
919         }
920 
921 #ifdef __REACTOS__
922         bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE);
923 
924         aCPLPath = GlobalFindAtomW(applet->cmd);
925         if (!aCPLPath)
926         {
927             aCPLPath = GlobalAddAtomW(applet->cmd);
928         }
929 
930         aCPLName = GlobalFindAtomW(L"CPLName");
931         if (!aCPLName)
932         {
933             aCPLName = GlobalAddAtomW(L"CPLName");
934         }
935 
936         aCPLFlags = GlobalFindAtomW(L"CPLFlags");
937         if (!aCPLFlags)
938         {
939             aCPLFlags = GlobalAddAtomW(L"CPLFlags");
940         }
941 
942         findData.szAppFile = applet->cmd;
943         findData.sAppletNo = (UINT_PTR)(sp + 1);
944         findData.aCPLName = aCPLName;
945         findData.aCPLFlags = aCPLFlags;
946         findData.hRunDLL = applet->hWnd;
947         findData.hDlgResult = NULL;
948         // Find the dialog of this applet in the first instance.
949         // Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs.
950         EnumWindows(Control_EnumWinProc, (LPARAM)&findData);
951         if (findData.hDlgResult)
952         {
953             BringWindowToTop(findData.hDlgResult);
954         }
955         else
956         {
957             SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLName), (HANDLE)MAKEINTATOM(aCPLPath));
958             SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLFlags), UlongToHandle(sp + 1));
959 #endif
960 
961         if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
962             applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
963 #ifdef __REACTOS__
964             RemovePropW(applet->hWnd, applet->cmd);
965             GlobalDeleteAtom(aCPLPath);
966         }
967 #endif
968 
969         Control_UnloadApplet(applet);
970 
971 #ifdef __REACTOS__
972     if (bActivated)
973         DeactivateActCtx(0, cookie);
974 #endif
975 
976     }
977 
978     HeapFree(GetProcessHeap(), 0, buffer);
979 }
980 
981 /*************************************************************************
982  * Control_RunDLLW			[SHELL32.@]
983  *
984  */
985 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
986 {
987     CPanel	panel;
988 
989     TRACE("(%p, %p, %s, 0x%08x)\n",
990 	  hWnd, hInst, debugstr_w(cmd), nCmdShow);
991 
992 #ifndef __REACTOS__
993     memset(&panel, 0, sizeof(panel));
994     list_init( &panel.applets );
995 #endif
996 
997     if (!cmd || !*cmd) {
998         Control_DoWindow(&panel, hWnd, hInst);
999     } else {
1000         Control_DoLaunch(&panel, hWnd, cmd);
1001     }
1002 }
1003 
1004 /*************************************************************************
1005  * Control_RunDLLA			[SHELL32.@]
1006  *
1007  */
1008 void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
1009 {
1010     DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 );
1011     LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1012     if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len ))
1013     {
1014         Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow);
1015     }
1016     HeapFree(GetProcessHeap(), 0, wszCmd);
1017 }
1018 
1019 /*************************************************************************
1020  * Control_FillCache_RunDLLW			[SHELL32.@]
1021  *
1022  */
1023 HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
1024 {
1025     FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x);
1026     return S_OK;
1027 }
1028 
1029 /*************************************************************************
1030  * Control_FillCache_RunDLLA			[SHELL32.@]
1031  *
1032  */
1033 HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
1034 {
1035     return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
1036 }
1037 
1038 
1039 #ifdef __REACTOS__
1040 /*************************************************************************
1041  * RunDll_CallEntry16                [SHELL32.122]
1042  * the name is OK (when written with Dll, and not DLL as in Wine!)
1043  */
1044 void WINAPI RunDll_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst,
1045                                 LPCSTR cmdline, INT cmdshow )
1046 {
1047     FIXME( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n",
1048            proc, hwnd, inst, debugstr_a(cmdline), cmdshow );
1049 }
1050 #endif
1051 
1052 /*************************************************************************
1053  * CallCPLEntry16				[SHELL32.166]
1054  *
1055  * called by desk.cpl on "Advanced" with:
1056  * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
1057  *
1058  */
1059 #ifndef __REACTOS__
1060 DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6)
1061 #else
1062 DECLARE_HANDLE(FARPROC16);
1063 LRESULT WINAPI CallCPLEntry16(HINSTANCE hMod, FARPROC16 pFunc, HWND dw3, UINT dw4, LPARAM dw5, LPARAM dw6)
1064 #endif
1065 {
1066     FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
1067     return 0x0deadbee;
1068 }
1069