Add some API documentation stubs to make winapi_check happy.
[wine] / dlls / shell32 / control.c
1 /* Control Panel management
2  *
3  * Copyright 2001 Eric Pouech
4  * Copyright 2008 Owen Rudge
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winreg.h"
32 #include "wine/winbase16.h"
33 #include "wownt32.h"
34 #include "wine/debug.h"
35 #include "cpl.h"
36 #include "wine/unicode.h"
37 #include "commctrl.h"
38
39 #define NO_SHLWAPI_REG
40 #include "shlwapi.h"
41
42 #include "cpanel.h"
43 #include "shresdef.h"
44 #include "shell32_main.h"
45
46 #define MAX_STRING_LEN      1024
47
48 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
49
50 CPlApplet*      Control_UnloadApplet(CPlApplet* applet)
51 {
52     unsigned    i;
53     CPlApplet*  next;
54
55     for (i = 0; i < applet->count; i++) {
56         if (!applet->info[i].dwSize) continue;
57         applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData);
58     }
59     if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
60     FreeLibrary(applet->hModule);
61     next = applet->next;
62     HeapFree(GetProcessHeap(), 0, applet);
63     return next;
64 }
65
66 CPlApplet*      Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
67 {
68     CPlApplet*  applet;
69     unsigned    i;
70     CPLINFO     info;
71     NEWCPLINFOW newinfo;
72
73     if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
74        return applet;
75
76     applet->hWnd = hWnd;
77
78     if (!(applet->hModule = LoadLibraryW(cmd))) {
79         WARN("Cannot load control panel applet %s\n", debugstr_w(cmd));
80         goto theError;
81     }
82     if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
83         WARN("Not a valid control panel applet %s\n", debugstr_w(cmd));
84         goto theError;
85     }
86     if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) {
87         WARN("Init of applet has failed\n");
88         goto theError;
89     }
90     if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) {
91         WARN("No subprogram in applet\n");
92         goto theError;
93     }
94
95     applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
96                          sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOW));
97
98     for (i = 0; i < applet->count; i++) {
99        ZeroMemory(&newinfo, sizeof(newinfo));
100        newinfo.dwSize = sizeof(NEWCPLINFOA);
101        applet->info[i].dwSize = sizeof(NEWCPLINFOW);
102        applet->info[i].dwFlags = 0;
103        applet->info[i].dwHelpContext = 0;
104        applet->info[i].szHelpFile[0] = '\0';
105        /* proc is supposed to return a null value upon success for
106         * CPL_INQUIRE and CPL_NEWINQUIRE
107         * However, real drivers don't seem to behave like this
108         * So, use introspection rather than return value
109         */
110        applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
111        applet->info[i].lData = info.lData;
112        if (info.idIcon != CPL_DYNAMIC_RES)
113            applet->info[i].hIcon = LoadIconW(applet->hModule,
114                                              MAKEINTRESOURCEW(info.idIcon));
115        if (info.idName != CPL_DYNAMIC_RES)
116            LoadStringW(applet->hModule, info.idName,
117                        applet->info[i].szName, sizeof(applet->info[i].szName) / sizeof(WCHAR));
118        if (info.idInfo != CPL_DYNAMIC_RES)
119            LoadStringW(applet->hModule, info.idInfo,
120                        applet->info[i].szInfo, sizeof(applet->info[i].szInfo) / sizeof(WCHAR));
121
122        if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) ||
123            (info.idInfo == CPL_DYNAMIC_RES)) {
124            applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);
125
126            applet->info[i].dwFlags = newinfo.dwFlags;
127            applet->info[i].dwHelpContext = newinfo.dwHelpContext;
128            applet->info[i].lData = newinfo.lData;
129            if (info.idIcon == CPL_DYNAMIC_RES) {
130                if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i);
131                applet->info[i].hIcon = newinfo.hIcon;
132            }
133            if (newinfo.dwSize == sizeof(NEWCPLINFOW)) {
134                if (info.idName == CPL_DYNAMIC_RES)
135                    memcpy(applet->info[i].szName, newinfo.szName, sizeof(newinfo.szName));
136                if (info.idInfo == CPL_DYNAMIC_RES)
137                    memcpy(applet->info[i].szInfo, newinfo.szInfo, sizeof(newinfo.szInfo));
138                memcpy(applet->info[i].szHelpFile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile));
139            } else {
140                if (info.idName == CPL_DYNAMIC_RES)
141                    MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName,
142                                        sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR),
143                                        applet->info[i].szName,
144                                        sizeof(applet->info[i].szName) / sizeof(WCHAR));
145                if (info.idInfo == CPL_DYNAMIC_RES)
146                    MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo,
147                                        sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR),
148                                        applet->info[i].szInfo,
149                                        sizeof(applet->info[i].szInfo) / sizeof(WCHAR));
150                MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile,
151                                    sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR),
152                                    applet->info[i].szHelpFile,
153                                    sizeof(applet->info[i].szHelpFile) / sizeof(WCHAR));
154            }
155        }
156     }
157
158     applet->next = panel->first;
159     panel->first = applet;
160
161     return applet;
162
163  theError:
164     Control_UnloadApplet(applet);
165     return NULL;
166 }
167
168 #define IDC_LISTVIEW        1000
169 #define IDC_STATUSBAR       1001
170
171 #define NUM_COLUMNS            2
172 #define LISTVIEW_DEFSTYLE   (WS_CHILD | WS_VISIBLE | WS_TABSTOP |\
173                              LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL)
174
175 static BOOL Control_CreateListView (CPanel *panel)
176 {
177     RECT ws, sb;
178     WCHAR empty_string[] = {0};
179     WCHAR buf[MAX_STRING_LEN];
180     LVCOLUMNW lvc;
181
182     /* Create list view */
183     GetClientRect(panel->hWndStatusBar, &sb);
184     GetClientRect(panel->hWnd, &ws);
185
186     panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW,
187                           empty_string, LISTVIEW_DEFSTYLE | LVS_ICON,
188                           0, 0, ws.right - ws.left, ws.bottom - ws.top -
189                           (sb.bottom - sb.top), panel->hWnd,
190                           (HMENU) IDC_LISTVIEW, panel->hInst, NULL);
191
192     if (!panel->hWndListView)
193         return FALSE;
194
195     /* Create image lists for list view */
196     panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
197         GetSystemMetrics(SM_CYSMICON), ILC_MASK, 1, 1);
198     panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON),
199         GetSystemMetrics(SM_CYICON), ILC_MASK, 1, 1);
200
201     (void) ListView_SetImageList(panel->hWndListView, panel->hImageListSmall, LVSIL_SMALL);
202     (void) ListView_SetImageList(panel->hWndListView, panel->hImageListLarge, LVSIL_NORMAL);
203
204     /* Create columns for list view */
205     lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
206     lvc.pszText = buf;
207     lvc.fmt = LVCFMT_LEFT;
208
209     /* Name column */
210     lvc.iSubItem = 0;
211     lvc.cx = (ws.right - ws.left) / 3;
212     LoadStringW(shell32_hInstance, IDS_CPANEL_NAME, buf, sizeof(buf) / sizeof(buf[0]));
213
214     if (ListView_InsertColumnW(panel->hWndListView, 0, &lvc) == -1)
215         return FALSE;
216
217     /* Description column */
218     lvc.iSubItem = 1;
219     lvc.cx = ((ws.right - ws.left) / 3) * 2;
220     LoadStringW(shell32_hInstance, IDS_CPANEL_DESCRIPTION, buf, sizeof(buf) /
221         sizeof(buf[0]));
222
223     if (ListView_InsertColumnW(panel->hWndListView, 1, &lvc) == -1)
224         return FALSE;
225
226     return(TRUE);
227 }
228
229 static void      Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
230 {
231    CPanel*      panel = (CPanel*)cs->lpCreateParams;
232    HMENU hMenu, hSubMenu;
233    CPlApplet*   applet;
234    MENUITEMINFOW mii;
235    int menucount, i, index;
236    CPlItem *item;
237    LVITEMW lvItem;
238    INITCOMMONCONTROLSEX icex;
239    INT sb_parts;
240
241    SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
242    panel->hWnd = hWnd;
243
244    /* Initialise common control DLL */
245    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
246    icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
247    InitCommonControlsEx(&icex);
248
249    /* create the status bar */
250    if (!(panel->hWndStatusBar = CreateStatusWindowW(WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, NULL, hWnd, IDC_STATUSBAR)))
251        return;
252
253    sb_parts = -1;
254    SendMessageW(panel->hWndStatusBar, SB_SETPARTS, 1, (LPARAM) &sb_parts);
255
256    /* create the list view */
257    if (!Control_CreateListView(panel))
258        return;
259
260    hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));
261
262    /* insert menu items for applets */
263    hSubMenu = GetSubMenu(hMenu, 0);
264    menucount = 0;
265
266    for (applet = panel->first; applet; applet = applet->next) {
267       for (i = 0; i < applet->count; i++) {
268          if (!applet->info[i].dwSize)
269             continue;
270
271          /* set up a CPlItem for this particular subprogram */
272          item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem));
273
274          if (!item)
275             continue;
276
277          item->applet = (CPlApplet *) applet;
278          item->id = i;
279
280          mii.cbSize = sizeof(MENUITEMINFOW);
281          mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
282          mii.dwTypeData = applet->info[i].szName;
283          mii.cch = sizeof(applet->info[i].szName) / sizeof(applet->info[i].szName[0]);
284          mii.wID = IDM_CPANEL_APPLET_BASE + menucount;
285          mii.dwItemData = (DWORD) item;
286
287          if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) {
288             /* add the list view item */
289             index = ImageList_AddIcon(panel->hImageListLarge, applet->info[i].hIcon);
290             ImageList_AddIcon(panel->hImageListSmall, applet->info[i].hIcon);
291
292             lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
293             lvItem.iItem = menucount;
294             lvItem.iSubItem = 0;
295             lvItem.pszText = applet->info[i].szName;
296             lvItem.iImage = index;
297             lvItem.lParam = (LPARAM) item;
298
299             ListView_InsertItemW(panel->hWndListView, &lvItem);
300
301             /* add the description */
302             ListView_SetItemTextW(panel->hWndListView, menucount, 1,
303                 applet->info[i].szInfo);
304
305             /* update menu bar, increment count */
306             DrawMenuBar(hWnd);
307             menucount++;
308          }
309       }
310    }
311
312    panel->total_subprogs = menucount;
313
314    /* check the "large items" icon in the View menu */
315    hSubMenu = GetSubMenu(hMenu, 1);
316    CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW,
317       FCIDM_SHVIEW_BIGICON, MF_BYCOMMAND);
318
319    SetMenu(hWnd, hMenu);
320 }
321
322 static void Control_FreeCPlItems(HWND hWnd, CPanel *panel)
323 {
324     HMENU hMenu, hSubMenu;
325     MENUITEMINFOW mii;
326     int i;
327
328     /* get the File menu */
329     hMenu = GetMenu(hWnd);
330
331     if (!hMenu)
332         return;
333
334     hSubMenu = GetSubMenu(hMenu, 0);
335
336     if (!hSubMenu)
337         return;
338
339     /* loop and free the item data */
340     for (i = IDM_CPANEL_APPLET_BASE; i <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs; i++)
341     {
342         mii.cbSize = sizeof(MENUITEMINFOW);
343         mii.fMask = MIIM_DATA;
344
345         if (!GetMenuItemInfoW(hSubMenu, i, FALSE, &mii))
346             continue;
347
348         HeapFree(GetProcessHeap(), 0, (LPVOID) mii.dwItemData);
349     }
350 }
351
352 static void Control_UpdateListViewStyle(CPanel *panel, UINT style, UINT id)
353 {
354     HMENU hMenu, hSubMenu;
355
356     SetWindowLongW(panel->hWndListView, GWL_STYLE, LISTVIEW_DEFSTYLE | style);
357
358     /* update the menu */
359     hMenu = GetMenu(panel->hWnd);
360     hSubMenu = GetSubMenu(hMenu, 1);
361
362     CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW,
363         id, MF_BYCOMMAND);
364 }
365
366 static CPlItem* Control_GetCPlItem_From_MenuID(HWND hWnd, UINT id)
367 {
368     HMENU hMenu, hSubMenu;
369     MENUITEMINFOW mii;
370
371     /* retrieve the CPlItem structure from the menu item data */
372     hMenu = GetMenu(hWnd);
373
374     if (!hMenu)
375         return NULL;
376
377     hSubMenu = GetSubMenu(hMenu, 0);
378
379     if (!hSubMenu)
380         return NULL;
381
382     mii.cbSize = sizeof(MENUITEMINFOW);
383     mii.fMask = MIIM_DATA;
384
385     if (!GetMenuItemInfoW(hSubMenu, id, FALSE, &mii))
386         return NULL;
387
388     return (CPlItem *) mii.dwItemData;
389 }
390
391 static CPlItem* Control_GetCPlItem_From_ListView(CPanel *panel)
392 {
393     LVITEMW lvItem;
394     int selitem;
395
396     selitem = SendMessageW(panel->hWndListView, LVM_GETNEXTITEM, -1, LVNI_FOCUSED
397         | LVNI_SELECTED);
398
399     if (selitem != -1)
400     {
401         lvItem.iItem = selitem;
402         lvItem.mask = LVIF_PARAM;
403
404         if (SendMessageW(panel->hWndListView, LVM_GETITEMW, 0, (LPARAM) &lvItem))
405             return (CPlItem *) lvItem.lParam;
406     }
407
408     return NULL;
409 }
410
411 static LRESULT WINAPI   Control_WndProc(HWND hWnd, UINT wMsg,
412                                         WPARAM lParam1, LPARAM lParam2)
413 {
414    CPanel*      panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
415
416    if (panel || wMsg == WM_CREATE) {
417       switch (wMsg) {
418       case WM_CREATE:
419          Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
420          return 0;
421       case WM_DESTROY:
422          {
423             CPlApplet*  applet = panel->first;
424             while (applet)
425                applet = Control_UnloadApplet(applet);
426          }
427          Control_FreeCPlItems(hWnd, panel);
428          PostQuitMessage(0);
429          break;
430       case WM_COMMAND:
431          switch (LOWORD(lParam1))
432          {
433              case IDM_CPANEL_EXIT:
434                  SendMessageW(hWnd, WM_CLOSE, 0, 0);
435                  return 0;
436
437              case IDM_CPANEL_ABOUT:
438                  {
439                      WCHAR appName[MAX_STRING_LEN];
440
441                      LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName,
442                          sizeof(appName) / sizeof(appName[0]));
443                      ShellAboutW(hWnd, appName, NULL, NULL);
444
445                      return 0;
446                  }
447
448              case FCIDM_SHVIEW_BIGICON:
449                  Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON);
450                  return 0;
451
452              case FCIDM_SHVIEW_SMALLICON:
453                  Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON);
454                  return 0;
455
456              case FCIDM_SHVIEW_LISTVIEW:
457                  Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW);
458                  return 0;
459
460              case FCIDM_SHVIEW_REPORTVIEW:
461                  Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW);
462                  return 0;
463
464              default:
465                  /* check if this is an applet */
466                  if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) &&
467                      (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs))
468                  {
469                      CPlItem *item = Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1));
470
471                      /* execute the applet if item is valid */
472                      if (item)
473                          item->applet->proc(item->applet->hWnd, CPL_DBLCLK, item->id,
474                              item->applet->info[item->id].lData);
475
476                      return 0;
477                  }
478
479                  break;
480          }
481
482          break;
483
484       case WM_NOTIFY:
485       {
486           LPNMHDR nmh = (LPNMHDR) lParam2;
487
488           switch (nmh->idFrom)
489           {
490               case IDC_LISTVIEW:
491                   switch (nmh->code)
492                   {
493                       case NM_RETURN:
494                       case NM_DBLCLK:
495                       {
496                           CPlItem *item = Control_GetCPlItem_From_ListView(panel);
497
498                           /* execute the applet if item is valid */
499                           if (item)
500                               item->applet->proc(item->applet->hWnd, CPL_DBLCLK,
501                                   item->id, item->applet->info[item->id].lData);
502
503                           return 0;
504                       }
505                       case LVN_ITEMCHANGED:
506                       {
507                           CPlItem *item = Control_GetCPlItem_From_ListView(panel);
508
509                           /* update the status bar if item is valid */
510                           if (item)
511                               SetWindowTextW(panel->hWndStatusBar,
512                                   item->applet->info[item->id].szInfo);
513
514                           return 0;
515                       }
516                   }
517
518                   break;
519           }
520
521           break;
522       }
523
524       case WM_MENUSELECT:
525           /* check if this is an applet */
526           if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) &&
527               (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs))
528           {
529               CPlItem *item = Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1));
530
531               /* update the status bar if item is valid */
532               if (item)
533                   SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].szInfo);
534           }
535           else
536               SetWindowTextW(panel->hWndStatusBar, NULL);
537
538           return 0;
539
540       case WM_SIZE:
541       {
542           HDWP hdwp;
543           RECT sb;
544
545           hdwp = BeginDeferWindowPos(2);
546
547           if (hdwp == NULL)
548               break;
549
550           GetClientRect(panel->hWndStatusBar, &sb);
551
552           hdwp = DeferWindowPos(hdwp, panel->hWndListView, NULL, 0, 0,
553               LOWORD(lParam2), HIWORD(lParam2) - (sb.bottom - sb.top),
554               SWP_NOZORDER | SWP_NOMOVE);
555
556           if (hdwp == NULL)
557               break;
558
559           hdwp = DeferWindowPos(hdwp, panel->hWndStatusBar, NULL, 0, 0,
560               LOWORD(lParam2), LOWORD(lParam1), SWP_NOZORDER | SWP_NOMOVE);
561
562           if (hdwp != NULL)
563               EndDeferWindowPos(hdwp);
564
565           return 0;
566       }
567      }
568    }
569
570    return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
571 }
572
573 static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
574 {
575     WNDCLASSW   wc;
576     MSG         msg;
577     WCHAR appName[MAX_STRING_LEN];
578     const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o',
579         'l','_','W','n','d','C','l','a','s','s',0};
580
581     LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0]));
582
583     wc.style = CS_HREDRAW|CS_VREDRAW;
584     wc.lpfnWndProc = Control_WndProc;
585     wc.cbClsExtra = 0;
586     wc.cbWndExtra = sizeof(CPlApplet*);
587     wc.hInstance = panel->hInst = hInst;
588     wc.hIcon = 0;
589     wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
590     wc.hbrBackground = GetStockObject(WHITE_BRUSH);
591     wc.lpszMenuName = NULL;
592     wc.lpszClassName = className;
593
594     if (!RegisterClassW(&wc)) return;
595
596     CreateWindowExW(0, wc.lpszClassName, appName,
597                     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
598                     CW_USEDEFAULT, CW_USEDEFAULT,
599                     CW_USEDEFAULT, CW_USEDEFAULT,
600                     hWnd, NULL, hInst, panel);
601     if (!panel->hWnd) return;
602
603     while (GetMessageW(&msg, panel->hWnd, 0, 0)) {
604         TranslateMessage(&msg);
605         DispatchMessageW(&msg);
606     }
607 }
608
609 static void Control_RegisterRegistryApplets(HWND hWnd, CPanel *panel, HKEY hkey_root, LPCWSTR szRepPath)
610 {
611     WCHAR name[MAX_PATH];
612     WCHAR value[MAX_PATH];
613     HKEY hkey;
614
615     if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
616     {
617         int idx = 0;
618
619         for(;; ++idx)
620         {
621             DWORD nameLen = MAX_PATH;
622             DWORD valueLen = MAX_PATH;
623
624             if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
625                 break;
626
627             Control_LoadApplet(hWnd, value, panel);
628         }
629         RegCloseKey(hkey);
630     }
631 }
632
633 static  void    Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
634 {
635     HANDLE              h;
636     WIN32_FIND_DATAW    fd;
637     WCHAR               buffer[MAX_PATH];
638     static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0};
639     static const WCHAR wszRegPath[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t',
640             '\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
641             '\\','C','o','n','t','r','o','l',' ','P','a','n','e','l','\\','C','p','l','s',0};
642     WCHAR *p;
643
644     /* first add .cpl files in the system directory */
645     GetSystemDirectoryW( buffer, MAX_PATH );
646     p = buffer + strlenW(buffer);
647     *p++ = '\\';
648     lstrcpyW(p, wszAllCpl);
649
650     if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
651         do {
652            lstrcpyW(p, fd.cFileName);
653            Control_LoadApplet(hWnd, buffer, panel);
654         } while (FindNextFileW(h, &fd));
655         FindClose(h);
656     }
657
658     /* now check for cpls in the registry */
659     Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath);
660     Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath);
661
662     Control_DoInterface(panel, hWnd, hInst);
663 }
664
665 static  void    Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
666    /* forms to parse:
667     *   foo.cpl,@sp,str
668     *   foo.cpl,@sp
669     *   foo.cpl,,str
670     *   foo.cpl @sp
671     *   foo.cpl str
672     *   "a path\foo.cpl"
673     */
674 {
675     LPWSTR      buffer;
676     LPWSTR      beg = NULL;
677     LPWSTR      end;
678     WCHAR       ch;
679     LPWSTR       ptr;
680     signed      sp = -1;
681     LPWSTR      extraPmtsBuf = NULL;
682     LPWSTR      extraPmts = NULL;
683     int        quoted = 0;
684
685     buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
686     if (!buffer) return;
687
688     end = lstrcpyW(buffer, wszCmd);
689
690     for (;;) {
691         ch = *end;
692         if (ch == '"') quoted = !quoted;
693         if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
694             *end = '\0';
695             if (beg) {
696                 if (*beg == '@') {
697                     sp = atoiW(beg + 1);
698                 } else if (*beg == '\0') {
699                     sp = -1;
700                 } else {
701                     extraPmtsBuf = beg;
702                 }
703             }
704             if (ch == '\0') break;
705             beg = end + 1;
706             if (ch == ' ') while (end[1] == ' ') end++;
707         }
708         end++;
709     }
710     while ((ptr = StrChrW(buffer, '"')))
711         memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
712
713     /* now check for any quotes in extraPmtsBuf and remove */
714     if (extraPmtsBuf != NULL)
715     {
716         beg = end = extraPmtsBuf;
717         quoted = 0;
718
719         for (;;) {
720             ch = *end;
721             if (ch == '"') quoted = !quoted;
722             if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
723                 *end = '\0';
724                 if (beg) {
725                     if (*beg != '\0') {
726                         extraPmts = beg;
727                     }
728                 }
729                 if (ch == '\0') break;
730                     beg = end + 1;
731                 if (ch == ' ') while (end[1] == ' ') end++;
732             }
733             end++;
734         }
735
736         while ((ptr = StrChrW(extraPmts, '"')))
737             memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR));
738
739         if (extraPmts == NULL)
740             extraPmts = extraPmtsBuf;
741     }
742
743     TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp);
744
745     Control_LoadApplet(hWnd, buffer, panel);
746
747     if (panel->first) {
748         CPlApplet* applet = panel->first;
749
750         assert(applet && applet->next == NULL);
751
752         /* we've been given a textual parameter (or none at all) */
753         if (sp == -1) {
754             while ((++sp) != applet->count) {
755                 if (applet->info[sp].dwSize) {
756                     TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].szName));
757
758                     if (StrCmpIW(extraPmts, applet->info[sp].szName) == 0)
759                         break;
760                 }
761             }
762         }
763
764         if (sp >= applet->count) {
765             WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
766             sp = 0;
767         }
768
769         if (applet->info[sp].dwSize) {
770             if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts))
771                 applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData);
772         }
773
774         Control_UnloadApplet(applet);
775     }
776
777     HeapFree(GetProcessHeap(), 0, buffer);
778 }
779
780 /*************************************************************************
781  * Control_RunDLLW                      [SHELL32.@]
782  *
783  */
784 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
785 {
786     CPanel      panel;
787
788     TRACE("(%p, %p, %s, 0x%08x)\n",
789           hWnd, hInst, debugstr_w(cmd), nCmdShow);
790
791     memset(&panel, 0, sizeof(panel));
792
793     if (!cmd || !*cmd) {
794         Control_DoWindow(&panel, hWnd, hInst);
795     } else {
796         Control_DoLaunch(&panel, hWnd, cmd);
797     }
798 }
799
800 /*************************************************************************
801  * Control_RunDLLA                      [SHELL32.@]
802  *
803  */
804 void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
805 {
806     DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 );
807     LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
808     if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len ))
809     {
810         Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow);
811     }
812     HeapFree(GetProcessHeap(), 0, wszCmd);
813 }
814
815 /*************************************************************************
816  * Control_FillCache_RunDLLW                    [SHELL32.@]
817  *
818  */
819 HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
820 {
821     FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x);
822     return 0;
823 }
824
825 /*************************************************************************
826  * Control_FillCache_RunDLLA                    [SHELL32.@]
827  *
828  */
829 HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
830 {
831     return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
832 }
833
834
835 /*************************************************************************
836  * RunDLL_CallEntry16                           [SHELL32.122]
837  * the name is probably wrong
838  */
839 void WINAPI RunDLL_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst,
840                                 LPCSTR cmdline, INT cmdshow )
841 {
842     WORD args[5];
843     SEGPTR cmdline_seg;
844
845     TRACE( "proc %x hwnd %p inst %p cmdline %s cmdshow %d\n",
846            proc, hwnd, inst, debugstr_a(cmdline), cmdshow );
847
848     cmdline_seg = MapLS( cmdline );
849     args[4] = HWND_16(hwnd);
850     args[3] = MapHModuleLS(inst);
851     args[2] = SELECTOROF(cmdline_seg);
852     args[1] = OFFSETOF(cmdline_seg);
853     args[0] = cmdshow;
854     WOWCallback16Ex( proc, WCB16_PASCAL, sizeof(args), args, NULL );
855     UnMapLS( cmdline_seg );
856 }
857
858 /*************************************************************************
859  * CallCPLEntry16                               [SHELL32.166]
860  *
861  * called by desk.cpl on "Advanced" with:
862  * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
863  *
864  */
865 DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6)
866 {
867     FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
868     return 0x0deadbee;
869 }