xref: /reactos/dll/win32/shell32/wine/shellord.c (revision 541cb0d9)
1 /*
2  * The parameters of many functions changes between different OS versions
3  * (NT uses Unicode strings, 95 uses ASCII strings)
4  *
5  * Copyright 1997 Marcus Meissner
6  *           1998 Jürgen Schmied
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <wine/config.h>
24 
25 #define WIN32_NO_STATUS
26 #define _INC_WINDOWS
27 #define COBJMACROS
28 
29 #include <windef.h>
30 #include <winbase.h>
31 #include <wine/winternl.h>
32 #include <shlobj.h>
33 #include <undocshell.h>
34 #include <shlwapi.h>
35 #include <commdlg.h>
36 #include <commoncontrols.h>
37 #include "../shellrecyclebin/recyclebin.h"
38 
39 #include <wine/debug.h>
40 #include <wine/unicode.h>
41 
42 #include "pidl.h"
43 #include "shell32_main.h"
44 
45 WINE_DEFAULT_DEBUG_CHANNEL(shell);
46 WINE_DECLARE_DEBUG_CHANNEL(pidl);
47 
48 #ifdef __REACTOS__
49 #include <comctl32_undoc.h>
50 #include <shlwapi_undoc.h>
51 #else
52 /* FIXME: !!! move CREATEMRULIST and flags to header file !!! */
53 /*        !!! it is in both here and comctl32undoc.c      !!! */
54 typedef struct tagCREATEMRULIST
55 {
56     DWORD  cbSize;        /* size of struct */
57     DWORD  nMaxItems;     /* max no. of items in list */
58     DWORD  dwFlags;       /* see below */
59     HKEY   hKey;          /* root reg. key under which list is saved */
60     LPCSTR lpszSubKey;    /* reg. subkey */
61     int (CALLBACK *lpfnCompare)(LPCVOID, LPCVOID, DWORD); /* item compare proc */
62 } CREATEMRULISTA, *LPCREATEMRULISTA;
63 
64 /* dwFlags */
65 #define MRUF_STRING_LIST  0 /* list will contain strings */
66 #define MRUF_BINARY_LIST  1 /* list will contain binary data */
67 #define MRUF_DELAYED_SAVE 2 /* only save list order to reg. is FreeMRUList */
68 
69 extern HANDLE WINAPI CreateMRUListA(LPCREATEMRULISTA lpcml);
70 extern VOID WINAPI FreeMRUList(HANDLE hMRUList);
71 extern INT    WINAPI AddMRUData(HANDLE hList, LPCVOID lpData, DWORD cbData);
72 extern INT    WINAPI FindMRUData(HANDLE hList, LPCVOID lpData, DWORD cbData, LPINT lpRegNum);
73 extern INT    WINAPI EnumMRUListA(HANDLE hList, INT nItemPos, LPVOID lpBuffer, DWORD nBufferSize);
74 #endif
75 
76 /*************************************************************************
77  * ParseFieldA					[internal]
78  *
79  * copies a field from a ',' delimited string
80  *
81  * first field is nField = 1
82  */
83 DWORD WINAPI ParseFieldA(
84 	LPCSTR src,
85 	DWORD nField,
86 	LPSTR dst,
87 	DWORD len)
88 {
89 	WARN("(%s,0x%08x,%p,%d) semi-stub.\n",debugstr_a(src),nField,dst,len);
90 
91 	if (!src || !src[0] || !dst || !len)
92 	  return 0;
93 
94 	/* skip n fields delimited by ',' */
95 	while (nField > 1)
96 	{
97 	  if (*src=='\0') return FALSE;
98 	  if (*(src++)==',') nField--;
99 	}
100 
101 	/* copy part till the next ',' to dst */
102 	while ( *src!='\0' && *src!=',' && (len--)>0 ) *(dst++)=*(src++);
103 
104 	/* finalize the string */
105 	*dst=0x0;
106 
107 	return TRUE;
108 }
109 
110 /*************************************************************************
111  * ParseFieldW			[internal]
112  *
113  * copies a field from a ',' delimited string
114  *
115  * first field is nField = 1
116  */
117 DWORD WINAPI ParseFieldW(LPCWSTR src, DWORD nField, LPWSTR dst, DWORD len)
118 {
119 	WARN("(%s,0x%08x,%p,%d) semi-stub.\n", debugstr_w(src), nField, dst, len);
120 
121 	if (!src || !src[0] || !dst || !len)
122 	  return 0;
123 
124 	/* skip n fields delimited by ',' */
125 	while (nField > 1)
126 	{
127 	  if (*src == 0x0) return FALSE;
128 	  if (*src++ == ',') nField--;
129 	}
130 
131 	/* copy part till the next ',' to dst */
132 	while ( *src != 0x0 && *src != ',' && (len--)>0 ) *(dst++) = *(src++);
133 
134 	/* finalize the string */
135 	*dst = 0x0;
136 
137 	return TRUE;
138 }
139 
140 /*************************************************************************
141  * ParseField			[SHELL32.58]
142  */
143 DWORD WINAPI ParseFieldAW(LPCVOID src, DWORD nField, LPVOID dst, DWORD len)
144 {
145 	if (SHELL_OsIsUnicode())
146 	  return ParseFieldW(src, nField, dst, len);
147 	return ParseFieldA(src, nField, dst, len);
148 }
149 
150 /*************************************************************************
151  * GetFileNameFromBrowse            [SHELL32.63]
152  *
153  */
154 BOOL WINAPI GetFileNameFromBrowse(
155     HWND hwndOwner,
156     LPWSTR lpstrFile,
157     UINT nMaxFile,
158     LPCWSTR lpstrInitialDir,
159     LPCWSTR lpstrDefExt,
160     LPCWSTR lpstrFilter,
161     LPCWSTR lpstrTitle)
162 {
163 typedef BOOL (WINAPI *GetOpenFileNameProc)(OPENFILENAMEW *ofn);
164     HMODULE hmodule;
165     GetOpenFileNameProc pGetOpenFileNameW;
166     OPENFILENAMEW ofn;
167     BOOL ret;
168 
169     TRACE("%p, %s, %d, %s, %s, %s, %s)\n",
170       hwndOwner, debugstr_w(lpstrFile), nMaxFile, lpstrInitialDir, lpstrDefExt,
171       lpstrFilter, lpstrTitle);
172 
173     hmodule = LoadLibraryW(L"comdlg32.dll");
174     if(!hmodule) return FALSE;
175     pGetOpenFileNameW = (GetOpenFileNameProc)GetProcAddress(hmodule, "GetOpenFileNameW");
176     if(!pGetOpenFileNameW)
177     {
178     FreeLibrary(hmodule);
179     return FALSE;
180     }
181 
182     memset(&ofn, 0, sizeof(ofn));
183 
184     ofn.lStructSize = sizeof(ofn);
185     ofn.hwndOwner = hwndOwner;
186     ofn.lpstrFilter = lpstrFilter;
187     ofn.lpstrFile = lpstrFile;
188     ofn.nMaxFile = nMaxFile;
189     ofn.lpstrInitialDir = lpstrInitialDir;
190     ofn.lpstrTitle = lpstrTitle;
191     ofn.lpstrDefExt = lpstrDefExt;
192     ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
193     ret = pGetOpenFileNameW(&ofn);
194 
195     FreeLibrary(hmodule);
196     return ret;
197 }
198 
199 #ifdef __REACTOS__
200 BOOL SHELL_GlobalCounterChanged(LONG *pCounter, SHELL_GCOUNTER_DECLAREPARAMETERS(handle, id))
201 {
202     LONG count = SHELL_GlobalCounterGet(SHELL_GCOUNTER_PARAMETERS(handle, id));
203     if (*pCounter == count)
204         return FALSE;
205     *pCounter = count;
206     return TRUE;
207 }
208 
209 EXTERN_C void SHELL32_GetDefaultShellState(LPSHELLSTATE pss);
210 EXTERN_C BOOL SHELL32_ReadRegShellState(PREGSHELLSTATE prss);
211 EXTERN_C LSTATUS SHELL32_WriteRegShellState(PREGSHELLSTATE prss);
212 SHELL_GCOUNTER_DEFINE_GUID(SHGCGUID_ShellState, 0x7cb834f0, 0x527b, 0x11d2, 0x9d, 0x1f, 0x00, 0x00, 0xf8, 0x05, 0xca, 0x57);
213 SHELL_GCOUNTER_DEFINE_HANDLE(g_hShellState);
214 #define SHELL_GCOUNTER_SHELLSTATE SHELL_GCOUNTER_PARAMETERS(g_hShellState, GLOBALCOUNTER_SHELLSETTINGSCHANGED)
215 static LONG g_ShellStateCounter = 0;
216 static UINT g_CachedSSF = 0;
217 static REGSHELLSTATE g_ShellState;
218 enum { ssf_autocheckselect = 0x00800000, ssf_iconsonly = 0x01000000,
219        ssf_showtypeoverlay = 0x02000000, ssf_showstatusbar = 0x04000000 };
220 #endif //__REACTOS__
221 
222 /*************************************************************************
223  * SHGetSetSettings				[SHELL32.68]
224  */
225 VOID WINAPI SHGetSetSettings(LPSHELLSTATE lpss, DWORD dwMask, BOOL bSet)
226 {
227 #ifdef __REACTOS__
228     const DWORD inverted = SSF_SHOWEXTENSIONS;
229     LPSHELLSTATE gpss = &g_ShellState.ss;
230     HKEY hKeyAdv;
231 
232     if (!SHELL_GlobalCounterIsInitialized(g_hShellState))
233     {
234         SHELL_GlobalCounterCreate(&SHGCGUID_ShellState, g_hShellState);
235     }
236 
237     if (!lpss)
238     {
239         SHELL_GlobalCounterIncrement(SHELL_GCOUNTER_SHELLSTATE);
240         return;
241     }
242 
243     hKeyAdv = SHGetShellKey(SHKEY_Root_HKCU | SHKEY_Key_Explorer, L"Advanced", bSet);
244     if (!hKeyAdv && bSet)
245         return;
246 
247 #define SSF_STRUCTONLY (SSF_NOCONFIRMRECYCLE | SSF_DOUBLECLICKINWEBVIEW | SSF_DESKTOPHTML | \
248                         SSF_WIN95CLASSIC | SSF_SORTCOLUMNS | SSF_STARTPANELON)
249 #define SHGSS_GetSetStruct(getsetmacro) \
250     do { \
251         getsetmacro(fNoConfirmRecycle, SSF_NOCONFIRMRECYCLE); \
252         getsetmacro(fDoubleClickInWebView, SSF_DOUBLECLICKINWEBVIEW); \
253         getsetmacro(fDesktopHTML, SSF_DESKTOPHTML); \
254         getsetmacro(fWin95Classic, SSF_WIN95CLASSIC); \
255         getsetmacro(lParamSort, SSF_SORTCOLUMNS); \
256         getsetmacro(iSortDirection, SSF_SORTCOLUMNS); \
257         getsetmacro(fStartPanelOn, SSF_STARTPANELON); \
258     } while (0)
259 #define SHGSS_GetSetAdv(getsetmacro) \
260     do { \
261         getsetmacro(L"HideFileExt", fShowExtensions, SSF_SHOWEXTENSIONS); \
262         getsetmacro(L"ShowCompColor", fShowCompColor, SSF_SHOWCOMPCOLOR); \
263         getsetmacro(L"DontPrettyPath", fDontPrettyPath, SSF_DONTPRETTYPATH); \
264         getsetmacro(L"ShowAttribCol", fShowAttribCol, SSF_SHOWATTRIBCOL); \
265         getsetmacro(L"MapNetDrvBtn", fMapNetDrvBtn, SSF_MAPNETDRVBUTTON); \
266         getsetmacro(L"ShowInfoTip", fShowInfoTip, SSF_SHOWINFOTIP); \
267         getsetmacro(L"HideIcons", fHideIcons, SSF_HIDEICONS); \
268         getsetmacro(L"WebView", fWebView, SSF_WEBVIEW); \
269         getsetmacro(L"Filter", fFilter, SSF_FILTER); \
270         getsetmacro(L"ShowSuperHidden", fShowSuperHidden, SSF_SHOWSUPERHIDDEN); \
271         getsetmacro(L"NoNetCrawling", fNoNetCrawling, SSF_NONETCRAWLING); \
272         getsetmacro(L"SeparateProcess", fSepProcess, SSF_SEPPROCESS); \
273         getsetmacro(L"AutoCheckSelect", fAutoCheckSelect, ssf_autocheckselect); \
274         getsetmacro(L"IconsOnly", fIconsOnly, ssf_iconsonly); \
275         getsetmacro(L"ShowTypeOverlay", fShowTypeOverlay, ssf_showtypeoverlay); \
276         getsetmacro(L"ShowStatusBar", fShowStatusBar, ssf_showstatusbar); \
277     } while (0)
278 
279     if (bSet)
280     {
281         DWORD changed = 0;
282         if (dwMask & ~g_CachedSSF)
283         {
284             SHELLSTATE tempstate;
285             SHGetSetSettings(&tempstate, dwMask, FALSE); // Read entries that are not in g_CachedSSF
286         }
287 
288 #define SHGSS_WriteAdv(name, value, SSF) \
289     do { \
290         DWORD val = (value), cb = sizeof(DWORD); \
291         if (SHSetValueW(hKeyAdv, NULL, (name), REG_DWORD, &val, cb) == ERROR_SUCCESS) \
292         { \
293             ++changed; \
294         } \
295     } while (0)
296 #define SHGSS_SetAdv(name, field, SSF) \
297     do { \
298         if ((dwMask & (SSF)) && gpss->field != lpss->field) \
299         { \
300             const DWORD bitval = (gpss->field = lpss->field); \
301             SHGSS_WriteAdv((name), ((SSF) & inverted) ? !bitval : !!bitval, (SSF)); \
302         } \
303     } while (0)
304 #define SHGSS_SetStruct(field, SSF) \
305     do { \
306         if ((dwMask & (SSF)) && gpss->field != lpss->field) \
307         { \
308             gpss->field = lpss->field; \
309             ++changed; \
310         } \
311     } while (0)
312 
313         if ((dwMask & SSF_SHOWALLOBJECTS) && gpss->fShowAllObjects != lpss->fShowAllObjects)
314         {
315             gpss->fShowAllObjects = lpss->fShowAllObjects;
316             SHGSS_WriteAdv(L"Hidden", lpss->fShowAllObjects ? 1 : 2, SSF_SHOWALLOBJECTS);
317         }
318         SHGSS_SetStruct(fShowSysFiles, SSF_SHOWSYSFILES);
319         SHGSS_GetSetAdv(SHGSS_SetAdv);
320         SHGSS_GetSetStruct(SHGSS_SetStruct);
321         if (changed)
322         {
323             if ((dwMask & SSF_SHOWSUPERHIDDEN) && (DLL_EXPORT_VERSION) < _WIN32_WINNT_VISTA)
324             {
325                 // This is probably a Windows bug but write this alternative name just in case someone reads it
326                 DWORD val = gpss->fShowSuperHidden != FALSE;
327                 SHSetValueW(hKeyAdv, NULL, L"SuperHidden", REG_DWORD, &val, sizeof(val));
328             }
329             SHELL32_WriteRegShellState(&g_ShellState); // Write the new SHELLSTATE
330             SHGetSetSettings(NULL, 0, TRUE); // Invalidate counter
331             SHSendMessageBroadcastW(WM_SETTINGCHANGE, 0, (LPARAM)L"ShellState"); // Notify everyone
332         }
333     }
334     else
335     {
336         DWORD read = 0, data, cb, dummy = 0;
337         if (SHELL_GlobalCounterChanged(&g_ShellStateCounter, SHELL_GCOUNTER_SHELLSTATE))
338             g_CachedSSF = 0;
339 
340 #define SHGSS_ReadAdv(name, SSF) ( \
341     (g_CachedSSF & (SSF)) != (SSF) && (cb = sizeof(DWORD)) != 0 && \
342     SHQueryValueEx(hKeyAdv, (name), NULL, NULL, &data, &cb) == ERROR_SUCCESS && \
343     cb == sizeof(DWORD) && (read |= (SSF)) != 0 )
344 #define SHGSS_GetFieldHelper(field, SSF, src, dst, cachevar) \
345     do { \
346         if (dwMask & (SSF)) \
347         { \
348             (dst)->field = (src)->field; \
349             cachevar |= (SSF); \
350         } \
351     } while (0)
352 #define SHGSS_CacheField(field, SSF) SHGSS_GetFieldHelper(field, (SSF), &rss.ss, gpss, read)
353 #define SHGSS_GetField(field, SSF) SHGSS_GetFieldHelper(field, (SSF), gpss, lpss, dummy)
354 #define SHGSS_GetAdv(name, field, SSF) \
355     do { \
356         if (SHGSS_ReadAdv((name), (SSF))) \
357             gpss->field = ((SSF) & inverted) ? data == FALSE : data != FALSE; \
358         SHGSS_GetFieldHelper(field, (SSF), gpss, lpss, read); \
359     } while (0)
360 
361         if (SHGSS_ReadAdv(L"Hidden", SSF_SHOWALLOBJECTS | SSF_SHOWSYSFILES))
362         {
363             gpss->fShowAllObjects = data == 1;
364             gpss->fShowSysFiles = data > 1;
365         }
366         SHGSS_GetField(fShowAllObjects, SSF_SHOWALLOBJECTS);
367         SHGSS_GetField(fShowSysFiles, SSF_SHOWSYSFILES);
368         SHGSS_GetSetAdv(SHGSS_GetAdv);
369         if (dwMask & ~(read | g_CachedSSF))
370         {
371             REGSHELLSTATE rss;
372             if (SHELL32_ReadRegShellState(&rss))
373             {
374                 SHGSS_GetSetStruct(SHGSS_CacheField); // Copy the requested items to gpss
375             }
376             else
377             {
378                 SHELL32_GetDefaultShellState(gpss);
379                 read = 0; // The advanced items we read are no longer valid in gpss
380                 g_CachedSSF = SSF_STRUCTONLY;
381             }
382         }
383         SHGSS_GetSetStruct(SHGSS_GetField); // Copy requested items from gpss to output
384         g_CachedSSF |= read;
385     }
386     if (hKeyAdv)
387         RegCloseKey(hKeyAdv);
388 #else
389   if(bSet)
390   {
391     FIXME("%p 0x%08x TRUE\n", lpss, dwMask);
392   }
393   else
394   {
395     SHGetSettings((LPSHELLFLAGSTATE)lpss,dwMask);
396   }
397 #endif //__REACTOS__
398 }
399 
400 /*************************************************************************
401  * SHGetSettings				[SHELL32.@]
402  *
403  * NOTES
404  *  the registry path are for win98 (tested)
405  *  and possibly are the same in nt40
406  *
407  */
408 VOID WINAPI SHGetSettings(LPSHELLFLAGSTATE lpsfs, DWORD dwMask)
409 {
410 #ifdef __REACTOS__
411     SHELLSTATE ss;
412     SHGetSetSettings(&ss, dwMask & ~(SSF_SORTCOLUMNS | SSF_FILTER), FALSE);
413     *lpsfs = *(LPSHELLFLAGSTATE)&ss;
414     if (dwMask & SSF_HIDEICONS)
415         lpsfs->fHideIcons = ss.fHideIcons;
416     if (dwMask & ssf_autocheckselect)
417         lpsfs->fAutoCheckSelect = ss.fAutoCheckSelect;
418     if (dwMask & ssf_iconsonly)
419         lpsfs->fIconsOnly = ss.fIconsOnly;
420 #else
421 	HKEY	hKey;
422 	DWORD	dwData;
423 	DWORD	dwDataSize = sizeof (DWORD);
424 
425 	TRACE("(%p 0x%08x)\n",lpsfs,dwMask);
426 
427 	if (RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
428 				 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0))
429 	  return;
430 
431 	if ( (SSF_SHOWEXTENSIONS & dwMask) && !RegQueryValueExA(hKey, "HideFileExt", 0, 0, (LPBYTE)&dwData, &dwDataSize))
432 	  lpsfs->fShowExtensions  = ((dwData == 0) ?  0 : 1);
433 
434 	if ( (SSF_SHOWINFOTIP & dwMask) && !RegQueryValueExA(hKey, "ShowInfoTip", 0, 0, (LPBYTE)&dwData, &dwDataSize))
435 	  lpsfs->fShowInfoTip  = ((dwData == 0) ?  0 : 1);
436 
437 	if ( (SSF_DONTPRETTYPATH & dwMask) && !RegQueryValueExA(hKey, "DontPrettyPath", 0, 0, (LPBYTE)&dwData, &dwDataSize))
438 	  lpsfs->fDontPrettyPath  = ((dwData == 0) ?  0 : 1);
439 
440 	if ( (SSF_HIDEICONS & dwMask) && !RegQueryValueExA(hKey, "HideIcons", 0, 0, (LPBYTE)&dwData, &dwDataSize))
441 	  lpsfs->fHideIcons  = ((dwData == 0) ?  0 : 1);
442 
443 	if ( (SSF_MAPNETDRVBUTTON & dwMask) && !RegQueryValueExA(hKey, "MapNetDrvBtn", 0, 0, (LPBYTE)&dwData, &dwDataSize))
444 	  lpsfs->fMapNetDrvBtn  = ((dwData == 0) ?  0 : 1);
445 
446 	if ( (SSF_SHOWATTRIBCOL & dwMask) && !RegQueryValueExA(hKey, "ShowAttribCol", 0, 0, (LPBYTE)&dwData, &dwDataSize))
447 	  lpsfs->fShowAttribCol  = ((dwData == 0) ?  0 : 1);
448 
449 	if (((SSF_SHOWALLOBJECTS | SSF_SHOWSYSFILES) & dwMask) && !RegQueryValueExA(hKey, "Hidden", 0, 0, (LPBYTE)&dwData, &dwDataSize))
450 	{ if (dwData == 0)
451 	  { if (SSF_SHOWALLOBJECTS & dwMask)	lpsfs->fShowAllObjects  = 0;
452 	    if (SSF_SHOWSYSFILES & dwMask)	lpsfs->fShowSysFiles  = 0;
453 	  }
454 	  else if (dwData == 1)
455 	  { if (SSF_SHOWALLOBJECTS & dwMask)	lpsfs->fShowAllObjects  = 1;
456 	    if (SSF_SHOWSYSFILES & dwMask)	lpsfs->fShowSysFiles  = 0;
457 	  }
458 	  else if (dwData == 2)
459 	  { if (SSF_SHOWALLOBJECTS & dwMask)	lpsfs->fShowAllObjects  = 0;
460 	    if (SSF_SHOWSYSFILES & dwMask)	lpsfs->fShowSysFiles  = 1;
461 	  }
462 	}
463 	RegCloseKey (hKey);
464 #endif //__REACTOS__
465 	TRACE("-- 0x%04x\n", *(WORD*)lpsfs);
466 }
467 
468 /*************************************************************************
469  * SHShellFolderView_Message			[SHELL32.73]
470  *
471  * Send a message to an explorer cabinet window.
472  *
473  * PARAMS
474  *  hwndCabinet [I] The window containing the shellview to communicate with
475  *  dwMessage   [I] The SFVM message to send
476  *  dwParam     [I] Message parameter
477  *
478  * RETURNS
479  *  fixme.
480  *
481  * NOTES
482  *  Message SFVM_REARRANGE = 1
483  *
484  *    This message gets sent when a column gets clicked to instruct the
485  *    shell view to re-sort the item list. dwParam identifies the column
486  *    that was clicked.
487  */
488 LRESULT WINAPI SHShellFolderView_Message(
489 	HWND hwndCabinet,
490 	UINT uMessage,
491 	LPARAM lParam)
492 {
493 	FIXME("%p %08x %08lx stub\n",hwndCabinet, uMessage, lParam);
494 	return 0;
495 }
496 
497 /*************************************************************************
498  * RegisterShellHook				[SHELL32.181]
499  *
500  * Register a shell hook.
501  *
502  * PARAMS
503  *      hwnd   [I]  Window handle
504  *      dwType [I]  Type of hook.
505  *
506  * NOTES
507  *     Exported by ordinal
508  */
509 BOOL WINAPI RegisterShellHook(
510 	HWND hWnd,
511 	DWORD dwType)
512 {
513     if (dwType == 3)
514     {
515         SetTaskmanWindow(hWnd);
516         return RegisterShellHookWindow(hWnd);
517     }
518     else if (dwType == 0)
519     {
520         return DeregisterShellHookWindow(hWnd);
521     }
522 
523     ERR("Unsupported argument");
524     return FALSE;
525 }
526 
527 /*************************************************************************
528  * ShellMessageBoxW				[SHELL32.182]
529  *
530  * See ShellMessageBoxA.
531  *
532  */
533 #ifdef __REACTOS__
534 /*
535  * shell32.ShellMessageBoxW directly redirects to shlwapi.ShellMessageBoxWrapW,
536  * while shell32.ShellMessageBoxA is a copy-paste ANSI adaptation of the
537  * shlwapi.ShellMessageBoxWrapW function.
538  *
539  * From Vista+ onwards, all the implementation of ShellMessageBoxA/W that
540  * were existing in shell32 has been completely moved to shlwapi, so that
541  * shell32.ShellMessageBoxA and shell32.ShellMessageBoxW are redirections
542  * to the corresponding shlwapi functions.
543  *
544  */
545 #else // !__REACTOS__
546 /*
547  * NOTE:
548  * shlwapi.ShellMessageBoxWrapW is a duplicate of shell32.ShellMessageBoxW
549  * because we can't forward to it in the .spec file since it's exported by
550  * ordinal. If you change the implementation here please update the code in
551  * shlwapi as well.
552  */
553 // Wine version, broken.
554 int ShellMessageBoxW(
555 	HINSTANCE hInstance,
556 	HWND hWnd,
557 	LPCWSTR lpText,
558 	LPCWSTR lpCaption,
559 	UINT uType,
560 	...)
561 {
562 	WCHAR	szText[100],szTitle[100];
563 	LPCWSTR pszText = szText, pszTitle = szTitle;
564 	LPWSTR  pszTemp;
565 	__ms_va_list args;
566 	int	ret;
567 
568 	__ms_va_start(args, uType);
569 	/* wvsprintfA(buf,fmt, args); */
570 
571 	TRACE("(%p,%p,%p,%p,%08x)\n",
572 	    hInstance,hWnd,lpText,lpCaption,uType);
573 
574 	if (IS_INTRESOURCE(lpCaption))
575 	  LoadStringW(hInstance, LOWORD(lpCaption), szTitle, ARRAY_SIZE(szTitle));
576 	else
577 	  pszTitle = lpCaption;
578 
579 	if (IS_INTRESOURCE(lpText))
580 	  LoadStringW(hInstance, LOWORD(lpText), szText, ARRAY_SIZE(szText));
581 	else
582 	  pszText = lpText;
583 
584 	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
585 		       pszText, 0, 0, (LPWSTR)&pszTemp, 0, &args);
586 
587 	__ms_va_end(args);
588 
589 	ret = MessageBoxW(hWnd,pszTemp,pszTitle,uType);
590         LocalFree(pszTemp);
591 	return ret;
592 }
593 #endif
594 
595 /*************************************************************************
596  * ShellMessageBoxA				[SHELL32.183]
597  *
598  * Format and output an error message.
599  *
600  * PARAMS
601  *  hInstance [I] Instance handle of message creator
602  *  hWnd      [I] Window handle of message creator
603  *  lpText    [I] Resource Id of title or LPSTR
604  *  lpCaption [I] Resource Id of title or LPSTR
605  *  uType     [I] Type of error message
606  *
607  * RETURNS
608  *  A return value from MessageBoxA().
609  *
610  * NOTES
611  *     Exported by ordinal
612  */
613 #ifdef __REACTOS__
614 /*
615  * Note that we cannot straightforwardly implement ShellMessageBoxA around
616  * ShellMessageBoxW, by converting some parameters from ANSI to UNICODE,
617  * because there may be some variadic ANSI strings, associated with '%s'
618  * printf-like formatters inside the format string, that would also need
619  * to be converted; however there is no way for us to find these and perform
620  * the conversion ourselves.
621  * Therefore, we re-implement ShellMessageBoxA by doing a copy-paste ANSI
622  * adaptation of the shlwapi.ShellMessageBoxWrapW function.
623  */
624 #endif
625 int ShellMessageBoxA(
626 	HINSTANCE hInstance,
627 	HWND hWnd,
628 	LPCSTR lpText,
629 	LPCSTR lpCaption,
630 	UINT uType,
631 	...)
632 {
633 #ifdef __REACTOS__
634     CHAR *szText = NULL, szTitle[100];
635     LPCSTR pszText, pszTitle = szTitle;
636     LPSTR pszTemp;
637     __ms_va_list args;
638     int ret;
639 
640     __ms_va_start(args, uType);
641 
642     TRACE("(%p,%p,%p,%p,%08x)\n", hInstance, hWnd, lpText, lpCaption, uType);
643 
644     if (IS_INTRESOURCE(lpCaption))
645         LoadStringA(hInstance, LOWORD(lpCaption), szTitle, ARRAY_SIZE(szTitle));
646     else
647         pszTitle = lpCaption;
648 
649     if (IS_INTRESOURCE(lpText))
650     {
651         /* Retrieve the length of the Unicode string and obtain the maximum
652          * possible length for the corresponding ANSI string (not counting
653          * any possible NULL-terminator). */
654         const WCHAR *ptr;
655         UINT len = LoadStringW(hInstance, LOWORD(lpText), (LPWSTR)&ptr, 0);
656 
657         len = WideCharToMultiByte(CP_ACP, 0, ptr, len,
658                                   NULL, 0, NULL, NULL);
659 
660         if (len)
661         {
662             szText = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(CHAR));
663             if (szText) LoadStringA(hInstance, LOWORD(lpText), szText, len + 1);
664         }
665         pszText = szText;
666         if (!pszText) {
667             WARN("Failed to load id %d\n", LOWORD(lpText));
668             __ms_va_end(args);
669             return 0;
670         }
671     }
672     else
673         pszText = lpText;
674 
675     FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
676                    pszText, 0, 0, (LPSTR)&pszTemp, 0, &args);
677 
678     __ms_va_end(args);
679 
680     ret = MessageBoxA(hWnd, pszTemp, pszTitle, uType | MB_SETFOREGROUND);
681 
682     HeapFree(GetProcessHeap(), 0, szText);
683     LocalFree(pszTemp);
684     return ret;
685 
686 #else // __REACTOS__
687 
688 // Wine version, broken.
689 	char	szText[100],szTitle[100];
690 	LPCSTR  pszText = szText, pszTitle = szTitle;
691 	LPSTR   pszTemp;
692 	__ms_va_list args;
693 	int	ret;
694 
695 	__ms_va_start(args, uType);
696 	/* wvsprintfA(buf,fmt, args); */
697 
698 	TRACE("(%p,%p,%p,%p,%08x)\n",
699 	    hInstance,hWnd,lpText,lpCaption,uType);
700 
701 	if (IS_INTRESOURCE(lpCaption))
702 	  LoadStringA(hInstance, LOWORD(lpCaption), szTitle, sizeof(szTitle));
703 	else
704 	  pszTitle = lpCaption;
705 
706 	if (IS_INTRESOURCE(lpText))
707 	  LoadStringA(hInstance, LOWORD(lpText), szText, sizeof(szText));
708 	else
709 	  pszText = lpText;
710 
711 	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
712 		       pszText, 0, 0, (LPSTR)&pszTemp, 0, &args);
713 
714 	__ms_va_end(args);
715 
716 	ret = MessageBoxA(hWnd,pszTemp,pszTitle,uType);
717         LocalFree(pszTemp);
718 	return ret;
719 #endif
720 }
721 
722 /*************************************************************************
723  * SHRegisterDragDrop				[SHELL32.86]
724  *
725  * Probably equivalent to RegisterDragDrop but under Windows 95 it could use the
726  * shell32 built-in "mini-COM" without the need to load ole32.dll - see SHLoadOLE
727  * for details. Under Windows 98 this function initializes the true OLE when called
728  * the first time, on XP always returns E_OUTOFMEMORY and it got removed from Vista.
729  *
730  * We follow Windows 98 behaviour.
731  *
732  * NOTES
733  *     exported by ordinal
734  *
735  * SEE ALSO
736  *     RegisterDragDrop, SHLoadOLE
737  */
738 HRESULT WINAPI SHRegisterDragDrop(
739 	HWND hWnd,
740 	LPDROPTARGET pDropTarget)
741 {
742         static BOOL ole_initialized = FALSE;
743         HRESULT hr;
744 
745         TRACE("(%p,%p)\n", hWnd, pDropTarget);
746 
747         if (!ole_initialized)
748         {
749             hr = OleInitialize(NULL);
750             if (FAILED(hr))
751                 return hr;
752             ole_initialized = TRUE;
753         }
754 	return RegisterDragDrop(hWnd, pDropTarget);
755 }
756 
757 /*************************************************************************
758  * SHRevokeDragDrop				[SHELL32.87]
759  *
760  * Probably equivalent to RevokeDragDrop but under Windows 95 it could use the
761  * shell32 built-in "mini-COM" without the need to load ole32.dll - see SHLoadOLE
762  * for details. Function removed from Windows Vista.
763  *
764  * We call ole32 RevokeDragDrop which seems to work even if OleInitialize was
765  * not called.
766  *
767  * NOTES
768  *     exported by ordinal
769  *
770  * SEE ALSO
771  *     RevokeDragDrop, SHLoadOLE
772  */
773 HRESULT WINAPI SHRevokeDragDrop(HWND hWnd)
774 {
775     TRACE("(%p)\n", hWnd);
776     return RevokeDragDrop(hWnd);
777 }
778 
779 /*************************************************************************
780  * SHDoDragDrop					[SHELL32.88]
781  *
782  * Probably equivalent to DoDragDrop but under Windows 9x it could use the
783  * shell32 built-in "mini-COM" without the need to load ole32.dll - see SHLoadOLE
784  * for details
785  *
786  * NOTES
787  *     exported by ordinal
788  *
789  * SEE ALSO
790  *     DoDragDrop, SHLoadOLE
791  */
792 HRESULT WINAPI SHDoDragDrop(
793 	HWND hWnd,
794 	LPDATAOBJECT lpDataObject,
795 	LPDROPSOURCE lpDropSource,
796 	DWORD dwOKEffect,
797 	LPDWORD pdwEffect)
798 {
799     FIXME("(%p %p %p 0x%08x %p):stub.\n",
800     hWnd, lpDataObject, lpDropSource, dwOKEffect, pdwEffect);
801 	return DoDragDrop(lpDataObject, lpDropSource, dwOKEffect, pdwEffect);
802 }
803 
804 /*************************************************************************
805  * ArrangeWindows				[SHELL32.184]
806  *
807  */
808 WORD WINAPI ArrangeWindows(HWND hwndParent, DWORD dwReserved, const RECT *lpRect,
809         WORD cKids, const HWND *lpKids)
810 {
811     /* Unimplemented in WinXP SP3 */
812     TRACE("(%p 0x%08x %p 0x%04x %p):stub.\n",
813 	   hwndParent, dwReserved, lpRect, cKids, lpKids);
814     return 0;
815 }
816 
817 /*************************************************************************
818  * SignalFileOpen				[SHELL32.103]
819  *
820  * NOTES
821  *     exported by ordinal
822  */
823 BOOL WINAPI
824 SignalFileOpen (PCIDLIST_ABSOLUTE pidl)
825 {
826     FIXME("(%p):stub.\n", pidl);
827 
828     return FALSE;
829 }
830 
831 #ifndef __REACTOS__
832 
833 /*************************************************************************
834  * SHADD_get_policy - helper function for SHAddToRecentDocs
835  *
836  * PARAMETERS
837  *   policy    [IN]  policy name (null termed string) to find
838  *   type      [OUT] ptr to DWORD to receive type
839  *   buffer    [OUT] ptr to area to hold data retrieved
840  *   len       [IN/OUT] ptr to DWORD holding size of buffer and getting
841  *                      length filled
842  *
843  * RETURNS
844  *   result of the SHQueryValueEx call
845  */
846 static INT SHADD_get_policy(LPCSTR policy, LPDWORD type, LPVOID buffer, LPDWORD len)
847 {
848     HKEY Policy_basekey;
849     INT ret;
850 
851     /* Get the key for the policies location in the registry
852      */
853     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
854 		      "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
855 		      0, KEY_READ, &Policy_basekey)) {
856 
857 	if (RegOpenKeyExA(HKEY_CURRENT_USER,
858 			  "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
859 			  0, KEY_READ, &Policy_basekey)) {
860 	    TRACE("No Explorer Policies location exists. Policy wanted=%s\n",
861 		  policy);
862 	    *len = 0;
863 	    return ERROR_FILE_NOT_FOUND;
864 	}
865     }
866 
867     /* Retrieve the data if it exists
868      */
869     ret = SHQueryValueExA(Policy_basekey, policy, 0, type, buffer, len);
870     RegCloseKey(Policy_basekey);
871     return ret;
872 }
873 
874 #endif // __REACTOS__
875 
876 /*************************************************************************
877  * SHADD_compare_mru - helper function for SHAddToRecentDocs
878  *
879  * PARAMETERS
880  *   data1     [IN] data being looked for
881  *   data2     [IN] data in MRU
882  *   cbdata    [IN] length from FindMRUData call (not used)
883  *
884  * RETURNS
885  *   position within MRU list that data was added.
886  */
887 static INT CALLBACK SHADD_compare_mru(LPCVOID data1, LPCVOID data2, DWORD cbData)
888 {
889 #ifdef __REACTOS__
890     LPCWSTR psz1, psz2;
891     INT iCmp = lstrcmpiW(data1, data2);
892     if (iCmp != 0)
893         return iCmp;
894     psz1 = data1;
895     psz2 = data2;
896     psz1 += lstrlenW(psz1) + 1;
897     psz2 += lstrlenW(psz2) + 1;
898     return lstrcmpiW(psz1, psz2);
899 #else
900     return lstrcmpiA(data1, data2);
901 #endif
902 }
903 
904 #ifdef __REACTOS__
905 static BOOL
906 DoStoreMRUData(LPBYTE pbBuffer, LPDWORD pcbBuffer,
907                LPCWSTR pszTargetTitle, LPCWSTR pszTargetPath, LPCWSTR pszLinkTitle)
908 {
909     DWORD ib = 0, cb;
910     INT cchTargetTitle = lstrlenW(pszTargetTitle);
911     INT cchTargetPath = lstrlenW(pszTargetPath);
912     INT cchLinkTitle = lstrlenW(pszLinkTitle);
913 
914     cb = (cchTargetTitle + 1 + cchTargetPath + 1 + cchLinkTitle + 2) * sizeof(WCHAR);
915     if (cb > *pcbBuffer)
916         return FALSE;
917 
918     ZeroMemory(pbBuffer, *pcbBuffer);
919 
920     cb = (cchTargetTitle + 1) * sizeof(WCHAR);
921     if (ib + cb > *pcbBuffer)
922         return FALSE;
923     CopyMemory(&pbBuffer[ib], pszTargetTitle, cb);
924     ib += cb;
925 
926     cb = (cchTargetPath + 1) * sizeof(WCHAR);
927     if (ib + cb > *pcbBuffer)
928         return FALSE;
929     CopyMemory(&pbBuffer[ib], pszTargetPath, cb);
930     ib += cb;
931 
932     cb = (cchLinkTitle + 1) * sizeof(WCHAR);
933     if (ib + cb > *pcbBuffer)
934         return FALSE;
935     CopyMemory(&pbBuffer[ib], pszLinkTitle, cb);
936     ib += cb;
937 
938     *pcbBuffer = ib;
939     return TRUE;
940 }
941 #else
942 /*************************************************************************
943  * SHADD_create_add_mru_data - helper function for SHAddToRecentDocs
944  *
945  * PARAMETERS
946  *   mruhandle    [IN] handle for created MRU list
947  *   doc_name     [IN] null termed pure doc name
948  *   new_lnk_name [IN] null termed path and file name for .lnk file
949  *   buffer       [IN/OUT] 2048 byte area to construct MRU data
950  *   len          [OUT] ptr to int to receive space used in buffer
951  *
952  * RETURNS
953  *   position within MRU list that data was added.
954  */
955 static INT SHADD_create_add_mru_data(HANDLE mruhandle, LPCSTR doc_name, LPCSTR new_lnk_name,
956                                      LPSTR buffer, INT *len)
957 {
958     LPSTR ptr;
959     INT wlen;
960 
961     /*FIXME: Document:
962      *  RecentDocs MRU data structure seems to be:
963      *    +0h   document file name w/ terminating 0h
964      *    +nh   short int w/ size of remaining
965      *    +n+2h 02h 30h, or 01h 30h, or 00h 30h  -  unknown
966      *    +n+4h 10 bytes zeros  -   unknown
967      *    +n+eh shortcut file name w/ terminating 0h
968      *    +n+e+nh 3 zero bytes  -  unknown
969      */
970 
971     /* Create the MRU data structure for "RecentDocs"
972 	 */
973     ptr = buffer;
974     lstrcpyA(ptr, doc_name);
975     ptr += (lstrlenA(buffer) + 1);
976     wlen= lstrlenA(new_lnk_name) + 1 + 12;
977     *((short int*)ptr) = wlen;
978     ptr += 2;   /* step past the length */
979     *(ptr++) = 0x30;  /* unknown reason */
980     *(ptr++) = 0;     /* unknown, but can be 0x00, 0x01, 0x02 */
981     memset(ptr, 0, 10);
982     ptr += 10;
983     lstrcpyA(ptr, new_lnk_name);
984     ptr += (lstrlenA(new_lnk_name) + 1);
985     memset(ptr, 0, 3);
986     ptr += 3;
987     *len = ptr - buffer;
988 
989     /* Add the new entry into the MRU list
990      */
991     return AddMRUData(mruhandle, buffer, *len);
992 }
993 #endif
994 
995 /*************************************************************************
996  * SHAddToRecentDocs				[SHELL32.@]
997  *
998  * Modify (add/clear) Shell's list of recently used documents.
999  *
1000  * PARAMETERS
1001  *   uFlags  [IN] SHARD_PATHA, SHARD_PATHW or SHARD_PIDL
1002  *   pv      [IN] string or pidl, NULL clears the list
1003  *
1004  * NOTES
1005  *     exported by name
1006  *
1007  * FIXME
1008  *  convert to unicode
1009  */
1010 void WINAPI SHAddToRecentDocs (UINT uFlags,LPCVOID pv)
1011 {
1012 #ifdef __REACTOS__
1013     INT ret;
1014     WCHAR szTargetPath[MAX_PATH], szLinkDir[MAX_PATH], szLinkFile[MAX_PATH], szDescription[80];
1015     WCHAR szPath[MAX_PATH];
1016     DWORD cbBuffer;
1017     HANDLE hFind;
1018     WIN32_FIND_DATAW find;
1019     HKEY hExplorerKey;
1020     LONG error;
1021     LPWSTR pchDotExt, pchTargetTitle, pchLinkTitle;
1022     MRUINFOW mru;
1023     HANDLE hMRUList = NULL;
1024     IShellLinkW *psl = NULL;
1025     IPersistFile *pPf = NULL;
1026     HRESULT hr;
1027     BYTE Buffer[(MAX_PATH + 64) * sizeof(WCHAR)];
1028 
1029     TRACE("%04x %p\n", uFlags, pv);
1030 
1031     /* check policy */
1032     ret = SHRestricted(REST_NORECENTDOCSHISTORY);
1033     TRACE("policy value for NoRecentDocsHistory = %08x\n", ret);
1034     if (ret != 0)
1035         return;
1036 
1037     /* store to szTargetPath */
1038     szTargetPath[0] = 0;
1039     if (pv)
1040     {
1041         switch (uFlags)
1042         {
1043             case SHARD_PATHA:
1044                 MultiByteToWideChar(CP_ACP, 0, pv, -1, szLinkDir, ARRAYSIZE(szLinkDir));
1045                 GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
1046                 break;
1047 
1048             case SHARD_PATHW:
1049                 GetFullPathNameW(pv, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
1050                 break;
1051 
1052             case SHARD_PIDL:
1053                 SHGetPathFromIDListW(pv, szLinkDir);
1054                 GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
1055                 break;
1056 
1057             default:
1058                 FIXME("Unsupported flags: %u\n", uFlags);
1059                 return;
1060         }
1061     }
1062 
1063     /* get recent folder */
1064     if (!SHGetSpecialFolderPathW(NULL, szLinkDir, CSIDL_RECENT, FALSE))
1065     {
1066         ERR("serious issues 1\n");
1067         return;
1068     }
1069     TRACE("Users Recent dir %S\n", szLinkDir);
1070 
1071     /* open Explorer key */
1072     error = RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer",
1073                             0, NULL, 0,
1074                             KEY_READ | KEY_WRITE, NULL, &hExplorerKey, NULL);
1075     if (error)
1076     {
1077         ERR("Failed to RegCreateKeyExW: 0x%08X\n", error);
1078         return;
1079     }
1080 
1081     if (!pv)
1082     {
1083         TRACE("pv is NULL, so delete all shortcut files in %S\n", szLinkDir);
1084 
1085         lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
1086         PathAppendW(szLinkFile, L"*.lnk");
1087 
1088         hFind = FindFirstFileW(szLinkFile, &find);
1089         if (hFind != INVALID_HANDLE_VALUE)
1090         {
1091             do
1092             {
1093                 lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
1094                 PathAppendW(szLinkFile, find.cFileName);
1095                 DeleteFileW(szLinkFile);
1096             } while (FindNextFile(hFind, &find));
1097             FindClose(hFind);
1098         }
1099 
1100         SHDeleteKeyW(hExplorerKey, L"RecentDocs");
1101         RegCloseKey(hExplorerKey);
1102         return;
1103     }
1104 
1105     if (szTargetPath[0] == 0 || !PathFileExistsW(szTargetPath) ||
1106         PathIsDirectoryW(szTargetPath))
1107     {
1108         /* path is not normal file */
1109         RegCloseKey(hExplorerKey);
1110         return;
1111     }
1112 
1113     hr = CoInitialize(NULL);
1114     if (FAILED(hr))
1115     {
1116         ERR("CoInitialize: %08X\n", hr);
1117         RegCloseKey(hExplorerKey);
1118         return;
1119     }
1120 
1121     /* check if file is a shortcut */
1122     ret = 0;
1123     pchDotExt = PathFindExtensionW(szTargetPath);
1124     while (lstrcmpiW(pchDotExt, L".lnk") == 0)
1125     {
1126         hr = IShellLink_ConstructFromPath(szTargetPath, &IID_IShellLinkW, (LPVOID*)&psl);
1127         if (FAILED(hr))
1128         {
1129             ERR("IShellLink_ConstructFromPath: 0x%08X\n", hr);
1130             goto Quit;
1131         }
1132 
1133         IShellLinkW_GetPath(psl, szPath, ARRAYSIZE(szPath), NULL, 0);
1134         IShellLinkW_Release(psl);
1135         psl = NULL;
1136 
1137         lstrcpynW(szTargetPath, szPath, ARRAYSIZE(szTargetPath));
1138         pchDotExt = PathFindExtensionW(szTargetPath);
1139 
1140         if (++ret >= 8)
1141         {
1142             ERR("Link loop?\n");
1143             goto Quit;
1144         }
1145     }
1146     if (!lstrcmpiW(pchDotExt, L".exe"))
1147     {
1148         /* executables are not added */
1149         goto Quit;
1150     }
1151 
1152     /* ***  JOB 0: Build strings *** */
1153 
1154     pchTargetTitle = PathFindFileNameW(szTargetPath);
1155 
1156     lstrcpyW(szDescription, L"Shortcut to ");
1157     StrCatBuffW(szDescription, pchTargetTitle, ARRAYSIZE(szDescription));
1158 
1159     lstrcpynW(szLinkFile, szLinkDir, ARRAYSIZE(szLinkFile));
1160     PathAppendW(szLinkFile, pchTargetTitle);
1161     StrCatBuffW(szLinkFile, L".lnk", ARRAYSIZE(szLinkFile));
1162     pchLinkTitle = PathFindFileNameW(szLinkFile);
1163 
1164     /* ***  JOB 1: Update registry for ...\Explorer\RecentDocs list  *** */
1165 
1166     /* store MRU data */
1167     cbBuffer = sizeof(Buffer);
1168     ret = DoStoreMRUData(Buffer, &cbBuffer, pchTargetTitle, szTargetPath, pchLinkTitle);
1169     if (!ret)
1170     {
1171         ERR("DoStoreMRUData failed: %d\n", ret);
1172         goto Quit;
1173     }
1174 
1175     /* create MRU list */
1176     mru.cbSize = sizeof(mru);
1177     mru.uMax = 16;
1178     mru.fFlags = MRU_BINARY | MRU_CACHEWRITE;
1179     mru.hKey = hExplorerKey;
1180     mru.lpszSubKey = L"RecentDocs";
1181     mru.lpfnCompare = (MRUCMPPROCW)SHADD_compare_mru;
1182     hMRUList = CreateMRUListW(&mru);
1183     if (!hMRUList)
1184     {
1185         ERR("CreateMRUListW failed\n");
1186         goto Quit;
1187     }
1188 
1189     /* already exists? */
1190     ret = FindMRUData(hMRUList, Buffer, cbBuffer, NULL);
1191     if (ret >= 0)
1192     {
1193         /* Just touch for speed */
1194         HANDLE hFile;
1195         hFile = CreateFileW(szLinkFile, GENERIC_READ | GENERIC_WRITE,
1196                             FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1197         if (hFile != INVALID_HANDLE_VALUE)
1198         {
1199             TRACE("Just touch file '%S'.\n", szLinkFile);
1200             CloseHandle(hFile);
1201             goto Quit;
1202         }
1203     }
1204 
1205     /* add MRU data */
1206     ret = AddMRUData(hMRUList, Buffer, cbBuffer);
1207     if (ret < 0)
1208     {
1209         ERR("AddMRUData failed: %d\n", ret);
1210         goto Quit;
1211     }
1212 
1213     /* ***  JOB 2: Create shortcut in user's "Recent" directory  *** */
1214 
1215     hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
1216                           &IID_IShellLinkW, (LPVOID *)&psl);
1217     if (FAILED(hr))
1218     {
1219         ERR("CoInitialize for IID_IShellLinkW: %08X\n", hr);
1220         goto Quit;
1221     }
1222 
1223     hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID *)&pPf);
1224     if (FAILED(hr))
1225     {
1226         ERR("IShellLinkW_QueryInterface: %08X\n", hr);
1227         goto Quit;
1228     }
1229 
1230     if (uFlags == SHARD_PIDL)
1231         hr = IShellLinkW_SetIDList(psl, pv);
1232     else
1233         hr = IShellLinkW_SetPath(psl, pv);
1234 
1235     IShellLinkW_SetDescription(psl, szDescription);
1236 
1237     hr = IPersistFile_Save(pPf, szLinkFile, TRUE);
1238     if (FAILED(hr))
1239     {
1240         ERR("IPersistFile_Save: 0x%08X\n", hr);
1241     }
1242 
1243     hr = IPersistFile_SaveCompleted(pPf, szLinkFile);
1244     if (FAILED(hr))
1245     {
1246         ERR("IPersistFile_SaveCompleted: 0x%08X\n", hr);
1247     }
1248 
1249 Quit:
1250     if (hMRUList)
1251         FreeMRUList(hMRUList);
1252     if (pPf)
1253         IPersistFile_Release(pPf);
1254     if (psl)
1255         IShellLinkW_Release(psl);
1256     CoUninitialize();
1257     RegCloseKey(hExplorerKey);
1258 #else
1259 /* If list is a string list lpfnCompare has the following prototype
1260  * int CALLBACK MRUCompareString(LPCSTR s1, LPCSTR s2)
1261  * for binary lists the prototype is
1262  * int CALLBACK MRUCompareBinary(LPCVOID data1, LPCVOID data2, DWORD cbData)
1263  * where cbData is the no. of bytes to compare.
1264  * Need to check what return value means identical - 0?
1265  */
1266 
1267 
1268     UINT olderrormode;
1269     HKEY HCUbasekey;
1270     CHAR doc_name[MAX_PATH];
1271     CHAR link_dir[MAX_PATH];
1272     CHAR new_lnk_filepath[MAX_PATH];
1273     CHAR new_lnk_name[MAX_PATH];
1274     CHAR * ext;
1275     IMalloc *ppM;
1276     LPITEMIDLIST pidl;
1277     HWND hwnd = 0;       /* FIXME:  get real window handle */
1278     INT ret;
1279     DWORD data[64], datalen, type;
1280 
1281     TRACE("%04x %p\n", uFlags, pv);
1282 
1283     /*FIXME: Document:
1284      *  RecentDocs MRU data structure seems to be:
1285      *    +0h   document file name w/ terminating 0h
1286      *    +nh   short int w/ size of remaining
1287      *    +n+2h 02h 30h, or 01h 30h, or 00h 30h  -  unknown
1288      *    +n+4h 10 bytes zeros  -   unknown
1289      *    +n+eh shortcut file name w/ terminating 0h
1290      *    +n+e+nh 3 zero bytes  -  unknown
1291      */
1292 
1293     /* See if we need to do anything.
1294      */
1295     datalen = 64;
1296     ret=SHADD_get_policy( "NoRecentDocsHistory", &type, data, &datalen);
1297     if ((ret > 0) && (ret != ERROR_FILE_NOT_FOUND)) {
1298 	ERR("Error %d getting policy \"NoRecentDocsHistory\"\n", ret);
1299 	return;
1300     }
1301     if (ret == ERROR_SUCCESS) {
1302 	if (!( (type == REG_DWORD) ||
1303 	       ((type == REG_BINARY) && (datalen == 4)) )) {
1304 	    ERR("Error policy data for \"NoRecentDocsHistory\" not formatted correctly, type=%d, len=%d\n",
1305 		type, datalen);
1306 	    return;
1307 	}
1308 
1309 	TRACE("policy value for NoRecentDocsHistory = %08x\n", data[0]);
1310 	/* now test the actual policy value */
1311 	if ( data[0] != 0)
1312 	    return;
1313     }
1314 
1315     /* Open key to where the necessary info is
1316      */
1317     /* FIXME: This should be done during DLL PROCESS_ATTACH (or THREAD_ATTACH)
1318      *        and the close should be done during the _DETACH. The resulting
1319      *        key is stored in the DLL global data.
1320      */
1321     if (RegCreateKeyExA(HKEY_CURRENT_USER,
1322 			"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
1323 			0, 0, 0, KEY_READ, 0, &HCUbasekey, 0)) {
1324 	ERR("Failed to create 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer'\n");
1325 	return;
1326     }
1327 
1328     /* Get path to user's "Recent" directory
1329      */
1330     if(SUCCEEDED(SHGetMalloc(&ppM))) {
1331 	if (SUCCEEDED(SHGetSpecialFolderLocation(hwnd, CSIDL_RECENT,
1332 						 &pidl))) {
1333 	    SHGetPathFromIDListA(pidl, link_dir);
1334 	    IMalloc_Free(ppM, pidl);
1335 	}
1336 	else {
1337 	    /* serious issues */
1338 	    link_dir[0] = 0;
1339 	    ERR("serious issues 1\n");
1340 	}
1341 	IMalloc_Release(ppM);
1342     }
1343     else {
1344 	/* serious issues */
1345 	link_dir[0] = 0;
1346 	ERR("serious issues 2\n");
1347     }
1348     TRACE("Users Recent dir %s\n", link_dir);
1349 
1350     /* If no input, then go clear the lists */
1351     if (!pv) {
1352 	/* clear user's Recent dir
1353 	 */
1354 
1355 	/* FIXME: delete all files in "link_dir"
1356 	 *
1357 	 * while( more files ) {
1358 	 *    lstrcpyA(old_lnk_name, link_dir);
1359 	 *    PathAppendA(old_lnk_name, filenam);
1360 	 *    DeleteFileA(old_lnk_name);
1361 	 * }
1362 	 */
1363 	FIXME("should delete all files in %s\\\n", link_dir);
1364 
1365 	/* clear MRU list
1366 	 */
1367 	/* MS Bug ?? v4.72.3612.1700 of shell32 does the delete against
1368 	 *  HKEY_LOCAL_MACHINE version of ...CurrentVersion\Explorer
1369 	 *  and naturally it fails w/ rc=2. It should do it against
1370 	 *  HKEY_CURRENT_USER which is where it is stored, and where
1371 	 *  the MRU routines expect it!!!!
1372 	 */
1373 	RegDeleteKeyA(HCUbasekey, "RecentDocs");
1374 	RegCloseKey(HCUbasekey);
1375 	return;
1376     }
1377 
1378     /* Have data to add, the jobs to be done:
1379      *   1. Add document to MRU list in registry "HKCU\Software\
1380      *      Microsoft\Windows\CurrentVersion\Explorer\RecentDocs".
1381      *   2. Add shortcut to document in the user's Recent directory
1382      *      (CSIDL_RECENT).
1383      *   3. Add shortcut to Start menu's Documents submenu.
1384      */
1385 
1386     /* Get the pure document name from the input
1387      */
1388     switch (uFlags)
1389     {
1390     case SHARD_PIDL:
1391         if (!SHGetPathFromIDListA(pv, doc_name))
1392         {
1393             WARN("can't get path from PIDL\n");
1394             return;
1395         }
1396         break;
1397 
1398     case SHARD_PATHA:
1399         lstrcpynA(doc_name, pv, MAX_PATH);
1400         break;
1401 
1402     case SHARD_PATHW:
1403         WideCharToMultiByte(CP_ACP, 0, pv, -1, doc_name, MAX_PATH, NULL, NULL);
1404         break;
1405 
1406     default:
1407         FIXME("Unsupported flags: %u\n", uFlags);
1408         return;
1409     }
1410 
1411     TRACE("full document name %s\n", debugstr_a(doc_name));
1412 
1413     PathStripPathA(doc_name);
1414     TRACE("stripped document name %s\n", debugstr_a(doc_name));
1415 
1416 
1417     /* ***  JOB 1: Update registry for ...\Explorer\RecentDocs list  *** */
1418 
1419     {  /* on input needs:
1420 	*      doc_name    -  pure file-spec, no path
1421 	*      link_dir    -  path to the user's Recent directory
1422 	*      HCUbasekey  -  key of ...Windows\CurrentVersion\Explorer" node
1423 	* creates:
1424 	*      new_lnk_name-  pure file-spec, no path for new .lnk file
1425 	*      new_lnk_filepath
1426 	*                  -  path and file name of new .lnk file
1427 	*/
1428 	CREATEMRULISTA mymru;
1429 	HANDLE mruhandle;
1430 	INT len, pos, bufused, err;
1431 	INT i;
1432 	DWORD attr;
1433 	CHAR buffer[2048];
1434 	CHAR *ptr;
1435 	CHAR old_lnk_name[MAX_PATH];
1436 	short int slen;
1437 
1438 	mymru.cbSize = sizeof(CREATEMRULISTA);
1439 	mymru.nMaxItems = 15;
1440 	mymru.dwFlags = MRUF_BINARY_LIST | MRUF_DELAYED_SAVE;
1441 	mymru.hKey = HCUbasekey;
1442 	mymru.lpszSubKey = "RecentDocs";
1443         mymru.lpfnCompare = SHADD_compare_mru;
1444 	mruhandle = CreateMRUListA(&mymru);
1445 	if (!mruhandle) {
1446 	    /* MRU failed */
1447 	    ERR("MRU processing failed, handle zero\n");
1448 	    RegCloseKey(HCUbasekey);
1449 	    return;
1450 	}
1451 	len = lstrlenA(doc_name);
1452 	pos = FindMRUData(mruhandle, doc_name, len, 0);
1453 
1454 	/* Now get the MRU entry that will be replaced
1455 	 * and delete the .lnk file for it
1456 	 */
1457 	if ((bufused = EnumMRUListA(mruhandle, (pos == -1) ? 14 : pos,
1458                                     buffer, 2048)) != -1) {
1459 	    ptr = buffer;
1460 	    ptr += (lstrlenA(buffer) + 1);
1461 	    slen = *((short int*)ptr);
1462 	    ptr += 2;  /* skip the length area */
1463 	    if (bufused >= slen + (ptr-buffer)) {
1464 		/* buffer size looks good */
1465 		ptr += 12; /* get to string */
1466 		len = bufused - (ptr-buffer);  /* get length of buf remaining */
1467                 if (ptr[0] && (lstrlenA(ptr) <= len-1)) {
1468 		    /* appears to be good string */
1469 		    lstrcpyA(old_lnk_name, link_dir);
1470 		    PathAppendA(old_lnk_name, ptr);
1471 		    if (!DeleteFileA(old_lnk_name)) {
1472 			if ((attr = GetFileAttributesA(old_lnk_name)) == INVALID_FILE_ATTRIBUTES) {
1473 			    if ((err = GetLastError()) != ERROR_FILE_NOT_FOUND) {
1474 				ERR("Delete for %s failed, err=%d, attr=%08x\n",
1475 				    old_lnk_name, err, attr);
1476 			    }
1477 			    else {
1478 				TRACE("old .lnk file %s did not exist\n",
1479 				      old_lnk_name);
1480 			    }
1481 			}
1482 			else {
1483 			    ERR("Delete for %s failed, attr=%08x\n",
1484 				old_lnk_name, attr);
1485 			}
1486 		    }
1487 		    else {
1488 			TRACE("deleted old .lnk file %s\n", old_lnk_name);
1489 		    }
1490 		}
1491 	    }
1492 	}
1493 
1494 	/* Create usable .lnk file name for the "Recent" directory
1495 	 */
1496 	wsprintfA(new_lnk_name, "%s.lnk", doc_name);
1497 	lstrcpyA(new_lnk_filepath, link_dir);
1498 	PathAppendA(new_lnk_filepath, new_lnk_name);
1499 	i = 1;
1500 	olderrormode = SetErrorMode(SEM_FAILCRITICALERRORS);
1501 	while (GetFileAttributesA(new_lnk_filepath) != INVALID_FILE_ATTRIBUTES) {
1502 	    i++;
1503 	    wsprintfA(new_lnk_name, "%s (%u).lnk", doc_name, i);
1504 	    lstrcpyA(new_lnk_filepath, link_dir);
1505 	    PathAppendA(new_lnk_filepath, new_lnk_name);
1506 	}
1507 	SetErrorMode(olderrormode);
1508 	TRACE("new shortcut will be %s\n", new_lnk_filepath);
1509 
1510 	/* Now add the new MRU entry and data
1511 	 */
1512 	pos = SHADD_create_add_mru_data(mruhandle, doc_name, new_lnk_name,
1513 					buffer, &len);
1514 	FreeMRUList(mruhandle);
1515 	TRACE("Updated MRU list, new doc is position %d\n", pos);
1516     }
1517 
1518     /* ***  JOB 2: Create shortcut in user's "Recent" directory  *** */
1519 
1520     {  /* on input needs:
1521 	*      doc_name    -  pure file-spec, no path
1522 	*      new_lnk_filepath
1523 	*                  -  path and file name of new .lnk file
1524  	*      uFlags[in]  -  flags on call to SHAddToRecentDocs
1525 	*      pv[in]      -  document path/pidl on call to SHAddToRecentDocs
1526 	*/
1527 	IShellLinkA *psl = NULL;
1528 	IPersistFile *pPf = NULL;
1529 	HRESULT hres;
1530 	CHAR desc[MAX_PATH];
1531 	WCHAR widelink[MAX_PATH];
1532 
1533 	CoInitialize(0);
1534 
1535 	hres = CoCreateInstance( &CLSID_ShellLink,
1536 				 NULL,
1537 				 CLSCTX_INPROC_SERVER,
1538 				 &IID_IShellLinkA,
1539 				 (LPVOID )&psl);
1540 	if(SUCCEEDED(hres)) {
1541 
1542 	    hres = IShellLinkA_QueryInterface(psl, &IID_IPersistFile,
1543 					     (LPVOID *)&pPf);
1544 	    if(FAILED(hres)) {
1545 		/* bombed */
1546 		ERR("failed QueryInterface for IPersistFile %08x\n", hres);
1547 		goto fail;
1548 	    }
1549 
1550 	    /* Set the document path or pidl */
1551 	    if (uFlags == SHARD_PIDL) {
1552                 hres = IShellLinkA_SetIDList(psl, pv);
1553 	    } else {
1554                 hres = IShellLinkA_SetPath(psl, pv);
1555 	    }
1556 	    if(FAILED(hres)) {
1557 		/* bombed */
1558 		ERR("failed Set{IDList|Path} %08x\n", hres);
1559 		goto fail;
1560 	    }
1561 
1562 	    lstrcpyA(desc, "Shortcut to ");
1563 	    lstrcatA(desc, doc_name);
1564 	    hres = IShellLinkA_SetDescription(psl, desc);
1565 	    if(FAILED(hres)) {
1566 		/* bombed */
1567 		ERR("failed SetDescription %08x\n", hres);
1568 		goto fail;
1569 	    }
1570 
1571 	    MultiByteToWideChar(CP_ACP, 0, new_lnk_filepath, -1,
1572 				widelink, MAX_PATH);
1573 	    /* create the short cut */
1574 	    hres = IPersistFile_Save(pPf, widelink, TRUE);
1575 	    if(FAILED(hres)) {
1576 		/* bombed */
1577 		ERR("failed IPersistFile::Save %08x\n", hres);
1578 		IPersistFile_Release(pPf);
1579 		IShellLinkA_Release(psl);
1580 		goto fail;
1581 	    }
1582 	    hres = IPersistFile_SaveCompleted(pPf, widelink);
1583 	    IPersistFile_Release(pPf);
1584 	    IShellLinkA_Release(psl);
1585 	    TRACE("shortcut %s has been created, result=%08x\n",
1586 		  new_lnk_filepath, hres);
1587 	}
1588 	else {
1589 	    ERR("CoCreateInstance failed, hres=%08x\n", hres);
1590 	}
1591     }
1592 
1593  fail:
1594     CoUninitialize();
1595 
1596     /* all done */
1597     RegCloseKey(HCUbasekey);
1598     return;
1599 #endif
1600 }
1601 
1602 /*************************************************************************
1603  * SHCreateShellFolderViewEx			[SHELL32.174]
1604  *
1605  * Create a new instance of the default Shell folder view object.
1606  *
1607  * RETURNS
1608  *  Success: S_OK
1609  *  Failure: error value
1610  *
1611  * NOTES
1612  *  see IShellFolder::CreateViewObject
1613  */
1614  #ifndef __REACTOS__
1615 
1616 HRESULT WINAPI SHCreateShellFolderViewEx(
1617 	LPCSFV psvcbi,    /* [in] shelltemplate struct */
1618 	IShellView **ppv) /* [out] IShellView pointer */
1619 {
1620 	IShellView * psf;
1621 	HRESULT hRes;
1622 
1623 	TRACE("sf=%p pidl=%p cb=%p mode=0x%08x parm=%p\n",
1624 	  psvcbi->pshf, psvcbi->pidl, psvcbi->pfnCallback,
1625 	  psvcbi->fvm, psvcbi->psvOuter);
1626 
1627 	*ppv = NULL;
1628     hRes = IShellView_Constructor(psvcbi->pshf, &psf);
1629 
1630     if (FAILED(hRes))
1631         return hRes;
1632 
1633 	hRes = IShellView_QueryInterface(psf, &IID_IShellView, (LPVOID *)ppv);
1634 	IShellView_Release(psf);
1635 
1636 	return hRes;
1637 }
1638 #endif
1639 
1640 /*************************************************************************
1641  *  SHWinHelp					[SHELL32.127]
1642  *
1643  */
1644 HRESULT WINAPI SHWinHelp(HWND hwnd, LPCWSTR pszHelp, UINT uCommand, ULONG_PTR dwData)
1645 {
1646     TRACE("(%p, %s, 0x%08x, %p)\n", hwnd, debugstr_w(pszHelp), uCommand, dwData);
1647     if (!WinHelpW(hwnd, pszHelp, uCommand, dwData))
1648     {
1649 #if 0
1650         ShellMessageBoxW(shell32_hInstance, hwnd, MAKEINTRESOURCEW(9115),
1651                          MAKEINTRESOURCEW(9116), MB_ICONSTOP);
1652 #endif
1653         return FALSE;
1654     }
1655     return TRUE;
1656 }
1657 /*************************************************************************
1658  *  SHRunControlPanel [SHELL32.161]
1659  *
1660  */
1661 BOOL WINAPI SHRunControlPanel (_In_ LPCWSTR commandLine, _In_opt_ HWND parent)
1662 {
1663 #ifdef __REACTOS__
1664     /*
1665      * TODO: Run in-process when possible, using
1666      * HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel\InProcCPLs
1667      * and possibly some extra rules.
1668      * See also https://docs.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shruncontrolpanel
1669      * "If the specified Control Panel item is already running, SHRunControlPanel
1670      *  attempts to switch to that instance rather than opening a new instance."
1671      * This function is not supported as of Windows Vista, where it always returns FALSE.
1672      * However we need to keep it "alive" even when ReactOS is compliled as NT6+
1673      * in order to keep control panel elements launch commands.
1674      */
1675     WCHAR parameters[MAX_PATH] = L"shell32.dll,Control_RunDLL ";
1676     TRACE("(%s, %p)n", debugstr_w(commandLine), parent);
1677     wcscat(parameters, commandLine);
1678 
1679     return ((INT_PTR)ShellExecuteW(parent, L"open", L"rundll32.exe", parameters, NULL, SW_SHOWNORMAL) > 32);
1680 #else
1681 	FIXME("(%s, %p): stub\n", debugstr_w(commandLine), parent);
1682 	return FALSE;
1683 #endif
1684 }
1685 
1686 static LPUNKNOWN SHELL32_IExplorerInterface=0;
1687 /*************************************************************************
1688  * SHSetInstanceExplorer			[SHELL32.176]
1689  *
1690  * NOTES
1691  *  Sets the interface
1692  */
1693 VOID WINAPI SHSetInstanceExplorer (LPUNKNOWN lpUnknown)
1694 {	TRACE("%p\n", lpUnknown);
1695 	SHELL32_IExplorerInterface = lpUnknown;
1696 }
1697 /*************************************************************************
1698  * SHGetInstanceExplorer			[SHELL32.@]
1699  *
1700  * NOTES
1701  *  gets the interface pointer of the explorer and a reference
1702  */
1703 HRESULT WINAPI SHGetInstanceExplorer (IUnknown **lpUnknown)
1704 {	TRACE("%p\n", lpUnknown);
1705 
1706 	*lpUnknown = SHELL32_IExplorerInterface;
1707 
1708 	if (!SHELL32_IExplorerInterface)
1709 	  return E_FAIL;
1710 
1711 	IUnknown_AddRef(SHELL32_IExplorerInterface);
1712 	return S_OK;
1713 }
1714 /*************************************************************************
1715  * SHFreeUnusedLibraries			[SHELL32.123]
1716  *
1717  * Probably equivalent to CoFreeUnusedLibraries but under Windows 9x it could use
1718  * the shell32 built-in "mini-COM" without the need to load ole32.dll - see SHLoadOLE
1719  * for details
1720  *
1721  * NOTES
1722  *     exported by ordinal
1723  *
1724  * SEE ALSO
1725  *     CoFreeUnusedLibraries, SHLoadOLE
1726  */
1727 void WINAPI SHFreeUnusedLibraries (void)
1728 {
1729 	FIXME("stub\n");
1730 	CoFreeUnusedLibraries();
1731 }
1732 /*************************************************************************
1733  * DAD_AutoScroll				[SHELL32.129]
1734  *
1735  */
1736 BOOL WINAPI DAD_AutoScroll(HWND hwnd, AUTO_SCROLL_DATA *samples, const POINT * pt)
1737 {
1738     FIXME("hwnd = %p %p %p\n",hwnd,samples,pt);
1739     return FALSE;
1740 }
1741 /*************************************************************************
1742  * DAD_DragEnter				[SHELL32.130]
1743  *
1744  */
1745 BOOL WINAPI DAD_DragEnter(HWND hwnd)
1746 {
1747     FIXME("hwnd = %p\n",hwnd);
1748     return FALSE;
1749 }
1750 /*************************************************************************
1751  * DAD_DragEnterEx				[SHELL32.131]
1752  *
1753  */
1754 BOOL WINAPI DAD_DragEnterEx(HWND hwnd, POINT p)
1755 {
1756     FIXME("hwnd = %p (%d,%d)\n",hwnd,p.x,p.y);
1757     return FALSE;
1758 }
1759 /*************************************************************************
1760  * DAD_DragMove				[SHELL32.134]
1761  *
1762  */
1763 BOOL WINAPI DAD_DragMove(POINT p)
1764 {
1765     FIXME("(%d,%d)\n",p.x,p.y);
1766     return FALSE;
1767 }
1768 /*************************************************************************
1769  * DAD_DragLeave				[SHELL32.132]
1770  *
1771  */
1772 BOOL WINAPI DAD_DragLeave(VOID)
1773 {
1774     FIXME("\n");
1775     return FALSE;
1776 }
1777 /*************************************************************************
1778  * DAD_SetDragImage				[SHELL32.136]
1779  *
1780  * NOTES
1781  *  exported by name
1782  */
1783 BOOL WINAPI DAD_SetDragImage(
1784 	HIMAGELIST himlTrack,
1785 	LPPOINT lppt)
1786 {
1787     FIXME("%p %p stub\n",himlTrack, lppt);
1788     return FALSE;
1789 }
1790 /*************************************************************************
1791  * DAD_ShowDragImage				[SHELL32.137]
1792  *
1793  * NOTES
1794  *  exported by name
1795  */
1796 BOOL WINAPI DAD_ShowDragImage(BOOL bShow)
1797 {
1798     FIXME("0x%08x stub\n",bShow);
1799     return FALSE;
1800 }
1801 
1802 /*************************************************************************
1803  * ReadCabinetState				[SHELL32.651] NT 4.0
1804  *
1805  */
1806 BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length)
1807 {
1808 	HKEY hkey = 0;
1809 	DWORD type, r;
1810 	C_ASSERT(sizeof(*cs) == FIELD_OFFSET(CABINETSTATE, fMenuEnumFilter) + sizeof(UINT));
1811 
1812 	TRACE("%p %d\n", cs, length);
1813 
1814 	if( (cs == NULL) || (length < (int)sizeof(*cs))  )
1815 		return FALSE;
1816 
1817 	r = RegOpenKeyW( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CabinetState", &hkey );
1818 	if( r == ERROR_SUCCESS )
1819 	{
1820 		type = REG_BINARY;
1821 		r = RegQueryValueExW( hkey, L"Settings",
1822 			NULL, &type, (LPBYTE)cs, (LPDWORD)&length );
1823 		RegCloseKey( hkey );
1824 
1825 	}
1826 
1827 	/* if we can't read from the registry, create default values */
1828 	if ( (r != ERROR_SUCCESS) || (cs->cLength < sizeof(*cs)) ||
1829 		(cs->cLength != length) )
1830 	{
1831 		SHELLSTATE shellstate;
1832 		shellstate.fWin95Classic = FALSE;
1833 		SHGetSetSettings(&shellstate, SSF_WIN95CLASSIC, FALSE);
1834 
1835 		TRACE("Initializing shell cabinet settings\n");
1836 		memset(cs, 0, sizeof(*cs));
1837 		cs->cLength          = sizeof(*cs);
1838 		cs->nVersion         = 2;
1839 		cs->fFullPathTitle   = FALSE;
1840 		cs->fSaveLocalView   = TRUE;
1841 		cs->fNotShell        = FALSE;
1842 		cs->fSimpleDefault   = TRUE;
1843 		cs->fDontShowDescBar = FALSE;
1844 		cs->fNewWindowMode   = shellstate.fWin95Classic;
1845 		cs->fShowCompColor   = FALSE;
1846 		cs->fDontPrettyNames = FALSE;
1847 		cs->fAdminsCreateCommonGroups = TRUE;
1848 		cs->fMenuEnumFilter  = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
1849 	}
1850 
1851 	return TRUE;
1852 }
1853 
1854 /*************************************************************************
1855  * WriteCabinetState				[SHELL32.652] NT 4.0
1856  *
1857  */
1858 BOOL WINAPI WriteCabinetState(CABINETSTATE *cs)
1859 {
1860 	DWORD r;
1861 	HKEY hkey = 0;
1862 
1863 	TRACE("%p\n",cs);
1864 
1865 	if( cs == NULL )
1866 		return FALSE;
1867 
1868 	r = RegCreateKeyExW( HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CabinetState", 0,
1869 		 NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1870 	if( r == ERROR_SUCCESS )
1871 	{
1872 		r = RegSetValueExW( hkey, L"Settings", 0,
1873 			REG_BINARY, (LPBYTE) cs, cs->cLength);
1874 
1875 		RegCloseKey( hkey );
1876 	}
1877 
1878 #ifdef __REACTOS__
1879 	/* TODO: if (r==ERROR_SUCCESS) Increment GLOBALCOUNTER_FOLDERSETTINGSCHANGE */
1880 #endif
1881 	return (r==ERROR_SUCCESS);
1882 }
1883 
1884 /*************************************************************************
1885  * FileIconInit 				[SHELL32.660]
1886  *
1887  */
1888 BOOL WINAPI FileIconInit(BOOL bFullInit)
1889 {
1890     return SIC_Initialize();
1891 }
1892 
1893 /*************************************************************************
1894  * SetAppStartingCursor				[SHELL32.99]
1895  */
1896 HRESULT WINAPI SetAppStartingCursor(HWND u, DWORD v)
1897 {	FIXME("hwnd=%p 0x%04x stub\n",u,v );
1898 	return 0;
1899 }
1900 
1901 /*************************************************************************
1902  * SHLoadOLE					[SHELL32.151]
1903  *
1904  * To reduce the memory usage of Windows 95, its shell32 contained an
1905  * internal implementation of a part of COM (see e.g. SHGetMalloc, SHCoCreateInstance,
1906  * SHRegisterDragDrop etc.) that allowed to use in-process STA objects without
1907  * the need to load OLE32.DLL. If OLE32.DLL was already loaded, the SH* function
1908  * would just call the Co* functions.
1909  *
1910  * The SHLoadOLE was called when OLE32.DLL was being loaded to transfer all the
1911  * information from the shell32 "mini-COM" to ole32.dll.
1912  *
1913  * See http://blogs.msdn.com/oldnewthing/archive/2004/07/05/173226.aspx for a
1914  * detailed description.
1915  *
1916  * Under wine ole32.dll is always loaded as it is imported by shlwapi.dll which is
1917  * imported by shell32 and no "mini-COM" is used (except for the "LoadWithoutCOM"
1918  * hack in SHCoCreateInstance)
1919  */
1920 HRESULT WINAPI SHLoadOLE(LPARAM lParam)
1921 {	FIXME("0x%08lx stub\n",lParam);
1922 	return S_OK;
1923 }
1924 /*************************************************************************
1925  * DriveType					[SHELL32.64]
1926  *
1927  */
1928 int WINAPI DriveType(int DriveType)
1929 {
1930     WCHAR root[] = L"A:\\";
1931     root[0] = L'A' + DriveType;
1932     return GetDriveTypeW(root);
1933 }
1934 /*************************************************************************
1935  * InvalidateDriveType            [SHELL32.65]
1936  * Unimplemented in XP SP3
1937  */
1938 int WINAPI InvalidateDriveType(int u)
1939 {
1940     TRACE("0x%08x stub\n",u);
1941     return 0;
1942 }
1943 /*************************************************************************
1944  * SHAbortInvokeCommand				[SHELL32.198]
1945  *
1946  */
1947 HRESULT WINAPI SHAbortInvokeCommand(void)
1948 {	FIXME("stub\n");
1949 	return 1;
1950 }
1951 /*************************************************************************
1952  * SHOutOfMemoryMessageBox			[SHELL32.126]
1953  *
1954  */
1955 int WINAPI SHOutOfMemoryMessageBox(
1956 	HWND hwndOwner,
1957 	LPCSTR lpCaption,
1958 	UINT uType)
1959 {
1960 	FIXME("%p %s 0x%08x stub\n",hwndOwner, lpCaption, uType);
1961 	return 0;
1962 }
1963 /*************************************************************************
1964  * SHFlushClipboard				[SHELL32.121]
1965  *
1966  */
1967 HRESULT WINAPI SHFlushClipboard(void)
1968 {
1969     return OleFlushClipboard();
1970 }
1971 
1972 /*************************************************************************
1973  * SHWaitForFileToOpen				[SHELL32.97]
1974  *
1975  */
1976 BOOL WINAPI SHWaitForFileToOpen(
1977 	LPCITEMIDLIST pidl,
1978 	DWORD dwFlags,
1979 	DWORD dwTimeout)
1980 {
1981 	FIXME("%p 0x%08x 0x%08x stub\n", pidl, dwFlags, dwTimeout);
1982 	return FALSE;
1983 }
1984 
1985 /************************************************************************
1986  *	RLBuildListOfPaths			[SHELL32.146]
1987  *
1988  * NOTES
1989  *   builds a DPA
1990  */
1991 DWORD WINAPI RLBuildListOfPaths (void)
1992 {	FIXME("stub\n");
1993 	return 0;
1994 }
1995 /************************************************************************
1996  *	SHValidateUNC				[SHELL32.173]
1997  *
1998  */
1999 BOOL WINAPI SHValidateUNC (HWND hwndOwner, PWSTR pszFile, UINT fConnect)
2000 {
2001 	FIXME("(%p, %s, 0x%08x): stub\n", hwndOwner, debugstr_w(pszFile), fConnect);
2002 	return FALSE;
2003 }
2004 
2005 /************************************************************************
2006  * DoEnvironmentSubstA [SHELL32.@]
2007  *
2008  * See DoEnvironmentSubstW.
2009  */
2010 DWORD WINAPI DoEnvironmentSubstA(LPSTR pszString, UINT cchString)
2011 {
2012     LPSTR dst;
2013     BOOL res = FALSE;
2014     DWORD len = cchString;
2015 
2016     TRACE("(%s, %d)\n", debugstr_a(pszString), cchString);
2017     if (pszString == NULL) /* Really return 0? */
2018         return 0;
2019     if ((dst = (LPSTR)HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(CHAR))))
2020     {
2021         len = ExpandEnvironmentStringsA(pszString, dst, cchString);
2022         /* len includes the terminating 0 */
2023         if (len && len < cchString)
2024         {
2025             res = TRUE;
2026             memcpy(pszString, dst, len);
2027         }
2028         else
2029             len = cchString;
2030 
2031         HeapFree(GetProcessHeap(), 0, dst);
2032     }
2033     return MAKELONG(len, res);
2034 }
2035 
2036 /************************************************************************
2037  * DoEnvironmentSubstW [SHELL32.@]
2038  *
2039  * Replace all %KEYWORD% in the string with the value of the named
2040  * environment variable. If the buffer is too small, the string is not modified.
2041  *
2042  * PARAMS
2043  *  pszString  [I] '\0' terminated string with %keyword%.
2044  *             [O] '\0' terminated string with %keyword% substituted.
2045  *  cchString  [I] size of str.
2046  *
2047  * RETURNS
2048  *  Success:  The string in the buffer is updated
2049  *            HIWORD: TRUE
2050  *            LOWORD: characters used in the buffer, including space for the terminating 0
2051  *  Failure:  buffer too small. The string is not modified.
2052  *            HIWORD: FALSE
2053  *            LOWORD: provided size of the buffer in characters
2054  */
2055 DWORD WINAPI DoEnvironmentSubstW(LPWSTR pszString, UINT cchString)
2056 {
2057     LPWSTR dst;
2058     BOOL res = FALSE;
2059     DWORD len = cchString;
2060 
2061     TRACE("(%s, %d)\n", debugstr_w(pszString), cchString);
2062 
2063     if ((cchString < MAXLONG) && (dst = HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(WCHAR))))
2064     {
2065         len = ExpandEnvironmentStringsW(pszString, dst, cchString);
2066         /* len includes the terminating 0 */
2067         if (len && len <= cchString)
2068         {
2069             res = TRUE;
2070             memcpy(pszString, dst, len * sizeof(WCHAR));
2071         }
2072         else
2073             len = cchString;
2074 
2075         HeapFree(GetProcessHeap(), 0, dst);
2076     }
2077     return MAKELONG(len, res);
2078 }
2079 
2080 /************************************************************************
2081  *	DoEnvironmentSubst			[SHELL32.53]
2082  *
2083  * See DoEnvironmentSubstA.
2084  */
2085 DWORD WINAPI DoEnvironmentSubstAW(LPVOID x, UINT y)
2086 {
2087     if (SHELL_OsIsUnicode())
2088         return DoEnvironmentSubstW(x, y);
2089     return DoEnvironmentSubstA(x, y);
2090 }
2091 
2092 /*************************************************************************
2093  *      GUIDFromStringA   [SHELL32.703]
2094  */
2095 BOOL WINAPI GUIDFromStringA(LPCSTR str, LPGUID guid)
2096 {
2097     ANSI_STRING ansi_str;
2098     WCHAR szWide[40];
2099     UNICODE_STRING guid_str = { 0, sizeof(szWide), szWide };
2100     if (*str != '{')
2101         return FALSE;
2102     RtlInitAnsiString(&ansi_str, str);
2103     return !RtlAnsiStringToUnicodeString(&guid_str, &ansi_str, FALSE) &&
2104            !RtlGUIDFromString(&guid_str, guid);
2105 }
2106 
2107 /*************************************************************************
2108  *      GUIDFromStringW   [SHELL32.704]
2109  */
2110 BOOL WINAPI GUIDFromStringW(LPCWSTR str, LPGUID guid)
2111 {
2112     UNICODE_STRING guid_str;
2113     if (!str || *str != L'{')
2114         return FALSE;
2115     RtlInitUnicodeString(&guid_str, str);
2116     return !RtlGUIDFromString(&guid_str, guid);
2117 }
2118 
2119 /*************************************************************************
2120  *      PathIsTemporaryA    [SHELL32.713]
2121  */
2122 #ifdef __REACTOS__
2123 /** @see https://undoc.airesoft.co.uk/shell32.dll/PathIsTemporaryA.php */
2124 BOOL WINAPI PathIsTemporaryA(_In_ LPCSTR Str)
2125 #else
2126 BOOL WINAPI PathIsTemporaryA(LPSTR Str)
2127 #endif
2128 {
2129 #ifdef __REACTOS__
2130     WCHAR szWide[MAX_PATH];
2131 
2132     TRACE("(%s)\n", debugstr_a(Str));
2133 
2134     SHAnsiToUnicode(Str, szWide, _countof(szWide));
2135     return PathIsTemporaryW(szWide);
2136 #else
2137     FIXME("(%s)stub\n", debugstr_a(Str));
2138     return FALSE;
2139 #endif
2140 }
2141 
2142 /*************************************************************************
2143  *      PathIsTemporaryW    [SHELL32.714]
2144  */
2145 #ifdef __REACTOS__
2146 /** @see https://undoc.airesoft.co.uk/shell32.dll/PathIsTemporaryW.php */
2147 BOOL WINAPI PathIsTemporaryW(_In_ LPCWSTR Str)
2148 #else
2149 BOOL WINAPI PathIsTemporaryW(LPWSTR Str)
2150 #endif
2151 {
2152 #ifdef __REACTOS__
2153     WCHAR szLongPath[MAX_PATH], szTempPath[MAX_PATH];
2154     DWORD attrs;
2155     LPCWSTR pszTarget = Str;
2156 
2157     TRACE("(%s)\n", debugstr_w(Str));
2158 
2159     attrs = GetFileAttributesW(Str);
2160     if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_TEMPORARY))
2161         return TRUE;
2162 
2163     if (!GetTempPathW(_countof(szTempPath), szTempPath) ||
2164         !GetLongPathNameW(szTempPath, szTempPath, _countof(szTempPath)))
2165     {
2166         return FALSE;
2167     }
2168 
2169     if (GetLongPathNameW(Str, szLongPath, _countof(szLongPath)))
2170         pszTarget = szLongPath;
2171 
2172     return (PathIsEqualOrSubFolder(szTempPath, pszTarget) ||
2173             PathIsEqualOrSubFolder(UlongToPtr(CSIDL_INTERNET_CACHE), pszTarget) ||
2174             PathIsEqualOrSubFolder(UlongToPtr(CSIDL_CDBURN_AREA), pszTarget));
2175 #else
2176     FIXME("(%s)stub\n", debugstr_w(Str));
2177     return FALSE;
2178 #endif
2179 }
2180 
2181 typedef struct _PSXA
2182 {
2183     UINT uiCount;
2184     UINT uiAllocated;
2185     IShellPropSheetExt *pspsx[1];
2186 } PSXA, *PPSXA;
2187 
2188 typedef struct _PSXA_CALL
2189 {
2190     LPFNADDPROPSHEETPAGE lpfnAddReplaceWith;
2191     LPARAM lParam;
2192     BOOL bCalled;
2193     BOOL bMultiple;
2194     UINT uiCount;
2195 } PSXA_CALL, *PPSXA_CALL;
2196 
2197 static BOOL CALLBACK PsxaCall(HPROPSHEETPAGE hpage, LPARAM lParam)
2198 {
2199     PPSXA_CALL Call = (PPSXA_CALL)lParam;
2200 
2201     if (Call != NULL)
2202     {
2203         if ((Call->bMultiple || !Call->bCalled) &&
2204             Call->lpfnAddReplaceWith(hpage, Call->lParam))
2205         {
2206             Call->bCalled = TRUE;
2207             Call->uiCount++;
2208             return TRUE;
2209         }
2210     }
2211 
2212     return FALSE;
2213 }
2214 
2215 /*************************************************************************
2216  *      SHAddFromPropSheetExtArray	[SHELL32.167]
2217  */
2218 UINT WINAPI SHAddFromPropSheetExtArray(HPSXA hpsxa, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
2219 {
2220     PSXA_CALL Call;
2221     UINT i;
2222     PPSXA psxa = (PPSXA)hpsxa;
2223 
2224     TRACE("(%p,%p,%08lx)\n", hpsxa, lpfnAddPage, lParam);
2225 
2226     if (psxa)
2227     {
2228         ZeroMemory(&Call, sizeof(Call));
2229         Call.lpfnAddReplaceWith = lpfnAddPage;
2230         Call.lParam = lParam;
2231         Call.bMultiple = TRUE;
2232 
2233         /* Call the AddPage method of all registered IShellPropSheetExt interfaces */
2234         for (i = 0; i != psxa->uiCount; i++)
2235         {
2236             psxa->pspsx[i]->lpVtbl->AddPages(psxa->pspsx[i], PsxaCall, (LPARAM)&Call);
2237         }
2238 
2239         return Call.uiCount;
2240     }
2241 
2242     return 0;
2243 }
2244 
2245 /*************************************************************************
2246  *      SHCreatePropSheetExtArray	[SHELL32.168]
2247  */
2248 HPSXA WINAPI SHCreatePropSheetExtArray(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface)
2249 {
2250     return SHCreatePropSheetExtArrayEx(hKey, pszSubKey, max_iface, NULL);
2251 }
2252 
2253 /*************************************************************************
2254  *      SHCreatePropSheetExtArrayEx	[SHELL32.194]
2255  */
2256 HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, LPDATAOBJECT pDataObj)
2257 {
2258     WCHAR szHandler[64];
2259     DWORD dwHandlerLen;
2260     WCHAR szClsidHandler[39];
2261     DWORD dwClsidSize;
2262     CLSID clsid;
2263     LONG lRet;
2264     DWORD dwIndex;
2265     IShellExtInit *psxi;
2266     IShellPropSheetExt *pspsx;
2267     HKEY hkBase, hkPropSheetHandlers;
2268     PPSXA psxa = NULL;
2269 
2270     TRACE("(%p,%s,%u)\n", hKey, debugstr_w(pszSubKey), max_iface);
2271 
2272     if (max_iface == 0)
2273         return NULL;
2274 
2275     /* Open the registry key */
2276     lRet = RegOpenKeyW(hKey, pszSubKey, &hkBase);
2277     if (lRet != ERROR_SUCCESS)
2278         return NULL;
2279 
2280     lRet = RegOpenKeyExW(hkBase, L"shellex\\PropertySheetHandlers", 0, KEY_ENUMERATE_SUB_KEYS, &hkPropSheetHandlers);
2281     RegCloseKey(hkBase);
2282     if (lRet == ERROR_SUCCESS)
2283     {
2284         /* Create and initialize the Property Sheet Extensions Array */
2285         psxa = LocalAlloc(LMEM_FIXED, FIELD_OFFSET(PSXA, pspsx[max_iface]));
2286         if (psxa)
2287         {
2288             ZeroMemory(psxa, FIELD_OFFSET(PSXA, pspsx[max_iface]));
2289             psxa->uiAllocated = max_iface;
2290 
2291             /* Enumerate all subkeys and attempt to load the shell extensions */
2292             dwIndex = 0;
2293             do
2294             {
2295                 dwHandlerLen = sizeof(szHandler) / sizeof(szHandler[0]);
2296                 lRet = RegEnumKeyExW(hkPropSheetHandlers, dwIndex++, szHandler, &dwHandlerLen, NULL, NULL, NULL, NULL);
2297                 if (lRet != ERROR_SUCCESS)
2298                 {
2299                     if (lRet == ERROR_MORE_DATA)
2300                         continue;
2301 
2302                     if (lRet == ERROR_NO_MORE_ITEMS)
2303                         lRet = ERROR_SUCCESS;
2304                     break;
2305                 }
2306 
2307                 /* The CLSID is stored either in the key itself or in its default value. */
2308                 if (FAILED(lRet = SHCLSIDFromStringW(szHandler, &clsid)))
2309                 {
2310                     dwClsidSize = sizeof(szClsidHandler);
2311                     if (SHGetValueW(hkPropSheetHandlers, szHandler, NULL, NULL, szClsidHandler, &dwClsidSize) == ERROR_SUCCESS)
2312                     {
2313                         /* Force a NULL-termination and convert the string */
2314                         szClsidHandler[(sizeof(szClsidHandler) / sizeof(szClsidHandler[0])) - 1] = 0;
2315                         lRet = SHCLSIDFromStringW(szClsidHandler, &clsid);
2316                     }
2317                 }
2318 
2319                 if (SUCCEEDED(lRet))
2320                 {
2321                     /* Attempt to get an IShellPropSheetExt and an IShellExtInit instance.
2322                        Only if both interfaces are supported it's a real shell extension.
2323                        Then call IShellExtInit's Initialize method. */
2324                     if (SUCCEEDED(CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER/* | CLSCTX_NO_CODE_DOWNLOAD */, &IID_IShellPropSheetExt, (LPVOID *)&pspsx)))
2325                     {
2326                         if (SUCCEEDED(pspsx->lpVtbl->QueryInterface(pspsx, &IID_IShellExtInit, (PVOID *)&psxi)))
2327                         {
2328                             if (SUCCEEDED(psxi->lpVtbl->Initialize(psxi, NULL, pDataObj, hKey)))
2329                             {
2330                                 /* Add the IShellPropSheetExt instance to the array */
2331                                 psxa->pspsx[psxa->uiCount++] = pspsx;
2332                             }
2333                             else
2334                             {
2335                                 psxi->lpVtbl->Release(psxi);
2336                                 pspsx->lpVtbl->Release(pspsx);
2337                             }
2338                         }
2339                         else
2340                             pspsx->lpVtbl->Release(pspsx);
2341                     }
2342                 }
2343 
2344             } while (psxa->uiCount != psxa->uiAllocated);
2345         }
2346         else
2347             lRet = ERROR_NOT_ENOUGH_MEMORY;
2348 
2349         RegCloseKey(hkPropSheetHandlers);
2350     }
2351 
2352     if (lRet != ERROR_SUCCESS && psxa)
2353     {
2354         SHDestroyPropSheetExtArray((HPSXA)psxa);
2355         psxa = NULL;
2356     }
2357 
2358     return (HPSXA)psxa;
2359 }
2360 
2361 /*************************************************************************
2362  *      SHReplaceFromPropSheetExtArray	[SHELL32.170]
2363  */
2364 UINT WINAPI SHReplaceFromPropSheetExtArray(HPSXA hpsxa, UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
2365 {
2366     PSXA_CALL Call;
2367     UINT i;
2368     PPSXA psxa = (PPSXA)hpsxa;
2369 
2370     TRACE("(%p,%u,%p,%08lx)\n", hpsxa, uPageID, lpfnReplaceWith, lParam);
2371 
2372     if (psxa)
2373     {
2374         ZeroMemory(&Call, sizeof(Call));
2375         Call.lpfnAddReplaceWith = lpfnReplaceWith;
2376         Call.lParam = lParam;
2377 
2378         /* Call the ReplacePage method of all registered IShellPropSheetExt interfaces.
2379            Each shell extension is only allowed to call the callback once during the callback. */
2380         for (i = 0; i != psxa->uiCount; i++)
2381         {
2382             Call.bCalled = FALSE;
2383             psxa->pspsx[i]->lpVtbl->ReplacePage(psxa->pspsx[i], uPageID, PsxaCall, (LPARAM)&Call);
2384         }
2385 
2386         return Call.uiCount;
2387     }
2388 
2389     return 0;
2390 }
2391 
2392 /*************************************************************************
2393  *      SHDestroyPropSheetExtArray	[SHELL32.169]
2394  */
2395 void WINAPI SHDestroyPropSheetExtArray(HPSXA hpsxa)
2396 {
2397     UINT i;
2398     PPSXA psxa = (PPSXA)hpsxa;
2399 
2400     TRACE("(%p)\n", hpsxa);
2401 
2402     if (psxa)
2403     {
2404         for (i = 0; i != psxa->uiCount; i++)
2405         {
2406             psxa->pspsx[i]->lpVtbl->Release(psxa->pspsx[i]);
2407         }
2408 
2409         LocalFree(psxa);
2410     }
2411 }
2412 
2413 /*************************************************************************
2414  *      CIDLData_CreateFromIDArray	[SHELL32.83]
2415  *
2416  *  Create IDataObject from PIDLs??
2417  */
2418 HRESULT WINAPI CIDLData_CreateFromIDArray(
2419 	PCIDLIST_ABSOLUTE pidlFolder,
2420     UINT cpidlFiles,
2421 	PCUIDLIST_RELATIVE_ARRAY lppidlFiles,
2422 	LPDATAOBJECT *ppdataObject)
2423 {
2424     UINT i;
2425     HWND hwnd = 0;   /*FIXME: who should be hwnd of owner? set to desktop */
2426     HRESULT hResult;
2427 
2428     TRACE("(%p, %d, %p, %p)\n", pidlFolder, cpidlFiles, lppidlFiles, ppdataObject);
2429     if (TRACE_ON(pidl))
2430     {
2431 	pdump (pidlFolder);
2432 	for (i=0; i<cpidlFiles; i++) pdump (lppidlFiles[i]);
2433     }
2434     hResult = IDataObject_Constructor(hwnd, pidlFolder, lppidlFiles, cpidlFiles, FALSE, ppdataObject);
2435     return hResult;
2436 }
2437 
2438 /*************************************************************************
2439  * SHCreateStdEnumFmtEtc			[SHELL32.74]
2440  *
2441  * NOTES
2442  *
2443  */
2444 HRESULT WINAPI SHCreateStdEnumFmtEtc(
2445     UINT cFormats,
2446 	const FORMATETC *lpFormats,
2447 	LPENUMFORMATETC *ppenumFormatetc)
2448 {
2449 	IEnumFORMATETC *pef;
2450 	HRESULT hRes;
2451 	TRACE("cf=%d fe=%p pef=%p\n", cFormats, lpFormats, ppenumFormatetc);
2452 
2453     hRes = IEnumFORMATETC_Constructor(cFormats, lpFormats, &pef);
2454     if (FAILED(hRes))
2455         return hRes;
2456 
2457 	IEnumFORMATETC_AddRef(pef);
2458 	hRes = IEnumFORMATETC_QueryInterface(pef, &IID_IEnumFORMATETC, (LPVOID*)ppenumFormatetc);
2459 	IEnumFORMATETC_Release(pef);
2460 
2461 	return hRes;
2462 }
2463 
2464 /*************************************************************************
2465  *		SHFindFiles (SHELL32.90)
2466  */
2467 BOOL WINAPI SHFindFiles( PCIDLIST_ABSOLUTE pidlFolder, PCIDLIST_ABSOLUTE pidlSaveFile )
2468 {
2469     FIXME("params ignored: %p %p\n", pidlFolder, pidlSaveFile);
2470     if (SHRestricted(REST_NOFIND))
2471     {
2472         return FALSE;
2473     }
2474     /* Open the search results folder */
2475     /* FIXME: CSearchBar should be opened as well */
2476     return ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{E17D4FC0-5564-11D1-83F2-00A0C90DC849}", NULL, SW_SHOWNORMAL) > (HINSTANCE)32;
2477 }
2478 
2479 /*************************************************************************
2480  *		SHUpdateImageW (SHELL32.192)
2481  *
2482  * Notifies the shell that an icon in the system image list has been changed.
2483  *
2484  * PARAMS
2485  *  pszHashItem [I] Path to file that contains the icon.
2486  *  iIndex      [I] Zero-based index of the icon in the file.
2487  *  uFlags      [I] Flags determining the icon attributes. See notes.
2488  *  iImageIndex [I] Index of the icon in the system image list.
2489  *
2490  * RETURNS
2491  *  Nothing
2492  *
2493  * NOTES
2494  *  uFlags can be one or more of the following flags:
2495  *  GIL_NOTFILENAME - pszHashItem is not a file name.
2496  *  GIL_SIMULATEDOC - Create a document icon using the specified icon.
2497  */
2498 void WINAPI SHUpdateImageW(LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex)
2499 {
2500     FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_w(pszHashItem), iIndex, uFlags, iImageIndex);
2501 }
2502 
2503 /*************************************************************************
2504  *		SHUpdateImageA (SHELL32.191)
2505  *
2506  * See SHUpdateImageW.
2507  */
2508 VOID WINAPI SHUpdateImageA(LPCSTR pszHashItem, INT iIndex, UINT uFlags, INT iImageIndex)
2509 {
2510     FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_a(pszHashItem), iIndex, uFlags, iImageIndex);
2511 }
2512 
2513 INT WINAPI SHHandleUpdateImage(PCIDLIST_ABSOLUTE pidlExtra)
2514 {
2515     FIXME("%p - stub\n", pidlExtra);
2516 
2517     return -1;
2518 }
2519 
2520 BOOL WINAPI SHObjectProperties(HWND hwnd, DWORD dwType, LPCWSTR szObject, LPCWSTR szPage)
2521 {
2522     LPITEMIDLIST pidl = NULL;
2523     switch (dwType)
2524     {
2525         case SHOP_FILEPATH:
2526             pidl = ILCreateFromPathW(szObject);
2527             break;
2528     }
2529     if (pidl)
2530     {
2531         SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_INVOKEIDLIST, hwnd, L"properties",
2532                                   NULL, szPage, NULL, SW_SHOWNORMAL, NULL, pidl };
2533         BOOL result = ShellExecuteExW(&sei);
2534         ILFree(pidl);
2535         return result;
2536     }
2537 
2538     FIXME("%p, 0x%08x, %s, %s - stub\n", hwnd, dwType, debugstr_w(szObject), debugstr_w(szPage));
2539 
2540     return TRUE;
2541 }
2542 
2543 BOOL WINAPI SHGetNewLinkInfoA(LPCSTR pszLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL *pfMustCopy,
2544                               UINT uFlags)
2545 {
2546     WCHAR wszLinkTo[MAX_PATH];
2547     WCHAR wszDir[MAX_PATH];
2548     WCHAR wszName[MAX_PATH];
2549     BOOL res;
2550 
2551     MultiByteToWideChar(CP_ACP, 0, pszLinkTo, -1, wszLinkTo, MAX_PATH);
2552     MultiByteToWideChar(CP_ACP, 0, pszDir, -1, wszDir, MAX_PATH);
2553 
2554     res = SHGetNewLinkInfoW(wszLinkTo, wszDir, wszName, pfMustCopy, uFlags);
2555 
2556     if (res)
2557         WideCharToMultiByte(CP_ACP, 0, wszName, -1, pszName, MAX_PATH, NULL, NULL);
2558 
2559     return res;
2560 }
2561 
2562 BOOL WINAPI SHGetNewLinkInfoW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL *pfMustCopy,
2563                               UINT uFlags)
2564 {
2565     const WCHAR *basename;
2566     WCHAR *dst_basename;
2567     int i=2;
2568 
2569     TRACE("(%s, %s, %p, %p, 0x%08x)\n", debugstr_w(pszLinkTo), debugstr_w(pszDir),
2570           pszName, pfMustCopy, uFlags);
2571 
2572     *pfMustCopy = FALSE;
2573 
2574     if (uFlags & SHGNLI_PIDL)
2575     {
2576         FIXME("SHGNLI_PIDL flag unsupported\n");
2577         return FALSE;
2578     }
2579 
2580     if (uFlags)
2581         FIXME("ignoring flags: 0x%08x\n", uFlags);
2582 
2583     /* FIXME: should test if the file is a shortcut or DOS program */
2584     if (GetFileAttributesW(pszLinkTo) == INVALID_FILE_ATTRIBUTES)
2585         return FALSE;
2586 
2587     basename = strrchrW(pszLinkTo, '\\');
2588     if (basename)
2589         basename = basename+1;
2590     else
2591         basename = pszLinkTo;
2592 
2593     lstrcpynW(pszName, pszDir, MAX_PATH);
2594     if (!PathAddBackslashW(pszName))
2595         return FALSE;
2596 
2597     dst_basename = pszName + strlenW(pszName);
2598 
2599     snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, L"%s.lnk", basename);
2600 
2601     while (GetFileAttributesW(pszName) != INVALID_FILE_ATTRIBUTES)
2602     {
2603         snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, L"%s (%d).lnk", basename, i);
2604         i++;
2605     }
2606 
2607     return TRUE;
2608 }
2609 
2610 HRESULT WINAPI SHStartNetConnectionDialog(HWND hwnd, LPCSTR pszRemoteName, DWORD dwType)
2611 {
2612 #ifdef __REACTOS__
2613     if (SHELL_OsIsUnicode())
2614         return SHStartNetConnectionDialogW(hwnd, (LPCWSTR)pszRemoteName, dwType);
2615     return SHStartNetConnectionDialogA(hwnd, pszRemoteName, dwType);
2616 #else
2617     FIXME("%p, %s, 0x%08x - stub\n", hwnd, debugstr_a(pszRemoteName), dwType);
2618 
2619     return S_OK;
2620 #endif
2621 }
2622 /*************************************************************************
2623  *              SHSetLocalizedName (SHELL32.@)
2624  */
2625 HRESULT WINAPI SHSetLocalizedName(LPCWSTR pszPath, LPCWSTR pszResModule, int idsRes)
2626 {
2627     FIXME("%p, %s, %d - stub\n", pszPath, debugstr_w(pszResModule), idsRes);
2628 
2629     return S_OK;
2630 }
2631 
2632 /*************************************************************************
2633  *              LinkWindow_RegisterClass (SHELL32.258)
2634  */
2635 BOOL WINAPI LinkWindow_RegisterClass(void)
2636 {
2637     FIXME("()\n");
2638     return TRUE;
2639 }
2640 
2641 /*************************************************************************
2642  *              LinkWindow_UnregisterClass (SHELL32.259)
2643  */
2644 BOOL WINAPI LinkWindow_UnregisterClass(DWORD dwUnused)
2645 {
2646     FIXME("()\n");
2647     return TRUE;
2648 }
2649 
2650 /*************************************************************************
2651  *              SHFlushSFCache (SHELL32.526)
2652  *
2653  * Notifies the shell that a user-specified special folder location has changed.
2654  *
2655  * NOTES
2656  *   In Wine, the shell folder registry values are not cached, so this function
2657  *   has no effect.
2658  */
2659 void WINAPI SHFlushSFCache(void)
2660 {
2661 }
2662 
2663 /*************************************************************************
2664  *              SHGetImageList (SHELL32.727)
2665  *
2666  * Returns a copy of a shell image list.
2667  *
2668  * NOTES
2669  *   Windows XP features 4 sizes of image list, and Vista 5. Wine currently
2670  *   only supports the traditional small and large image lists, so requests
2671  *   for the others will currently fail.
2672  */
2673 HRESULT WINAPI SHGetImageList(int iImageList, REFIID riid, void **ppv)
2674 {
2675     HIMAGELIST hLarge, hSmall;
2676     HIMAGELIST hNew;
2677     HRESULT ret = E_FAIL;
2678 
2679     /* Wine currently only maintains large and small image lists */
2680     if ((iImageList != SHIL_LARGE) && (iImageList != SHIL_SMALL) && (iImageList != SHIL_SYSSMALL))
2681     {
2682         FIXME("Unsupported image list %i requested\n", iImageList);
2683         return E_FAIL;
2684     }
2685 
2686     Shell_GetImageLists(&hLarge, &hSmall);
2687 #ifndef __REACTOS__
2688     hNew = ImageList_Duplicate(iImageList == SHIL_LARGE ? hLarge : hSmall);
2689 
2690     /* Get the interface for the new image list */
2691     if (hNew)
2692     {
2693         ret = HIMAGELIST_QueryInterface(hNew, riid, ppv);
2694         ImageList_Destroy(hNew);
2695     }
2696 #else
2697     /* Duplicating the imagelist causes the start menu items not to draw on
2698      * the first show. Was the Duplicate necessary for some reason? I believe
2699      * Windows returns the raw pointer here. */
2700     hNew = (iImageList == SHIL_LARGE ? hLarge : hSmall);
2701     ret = IImageList2_QueryInterface((IImageList2 *) hNew, riid, ppv);
2702 #endif
2703 
2704     return ret;
2705 }
2706 
2707 #ifndef __REACTOS__
2708 
2709 /*************************************************************************
2710  * SHCreateShellFolderView			[SHELL32.256]
2711  *
2712  * Create a new instance of the default Shell folder view object.
2713  *
2714  * RETURNS
2715  *  Success: S_OK
2716  *  Failure: error value
2717  *
2718  * NOTES
2719  *  see IShellFolder::CreateViewObject
2720  */
2721 HRESULT WINAPI SHCreateShellFolderView(const SFV_CREATE *pcsfv,
2722                         IShellView **ppsv)
2723 {
2724 	IShellView * psf;
2725 	HRESULT hRes;
2726 
2727 	*ppsv = NULL;
2728 	if (!pcsfv || pcsfv->cbSize != sizeof(*pcsfv))
2729 	  return E_INVALIDARG;
2730 
2731 	TRACE("sf=%p outer=%p callback=%p\n",
2732 	  pcsfv->pshf, pcsfv->psvOuter, pcsfv->psfvcb);
2733 
2734     hRes = IShellView_Constructor(pcsfv->pshf, &psf);
2735     if (FAILED(hRes))
2736         return hRes;
2737 
2738 	hRes = IShellView_QueryInterface(psf, &IID_IShellView, (LPVOID *)ppsv);
2739 	IShellView_Release(psf);
2740 
2741 	return hRes;
2742 }
2743 #endif
2744 
2745 
2746 /*************************************************************************
2747  * SHTestTokenMembership    [SHELL32.245]
2748  *
2749  * Checks whether a given token is a mamber of a local group with the
2750  * specified RID.
2751  *
2752  */
2753 EXTERN_C BOOL
2754 WINAPI
2755 SHTestTokenMembership(HANDLE TokenHandle, ULONG ulRID)
2756 {
2757     SID_IDENTIFIER_AUTHORITY ntAuth = {SECURITY_NT_AUTHORITY};
2758     DWORD nSubAuthority0, nSubAuthority1;
2759     DWORD nSubAuthorityCount;
2760     PSID SidToCheck;
2761     BOOL IsMember = FALSE;
2762 
2763     if ((ulRID == SECURITY_SERVICE_RID) || ulRID == SECURITY_LOCAL_SYSTEM_RID)
2764     {
2765         nSubAuthority0 = ulRID;
2766         nSubAuthority1 = 0;
2767         nSubAuthorityCount= 1;
2768     }
2769     else
2770     {
2771         nSubAuthority0 = SECURITY_BUILTIN_DOMAIN_RID;
2772         nSubAuthority1 = ulRID;
2773         nSubAuthorityCount= 2;
2774     }
2775 
2776     if (!AllocateAndInitializeSid(&ntAuth,
2777                                   nSubAuthorityCount,
2778                                   nSubAuthority0,
2779                                   nSubAuthority1,
2780                                   0, 0, 0, 0, 0, 0,
2781                                   &SidToCheck))
2782     {
2783         return FALSE;
2784     }
2785 
2786     if (!CheckTokenMembership(TokenHandle, SidToCheck, &IsMember))
2787     {
2788         IsMember = FALSE;
2789     }
2790 
2791     FreeSid(SidToCheck);
2792     return IsMember;
2793 }
2794 
2795 /*************************************************************************
2796  * IsUserAnAdmin    [SHELL32.680] NT 4.0
2797  *
2798  * Checks whether the current user is a member of the Administrators group.
2799  *
2800  * PARAMS
2801  *     None
2802  *
2803  * RETURNS
2804  *     Success: TRUE
2805  *     Failure: FALSE
2806  */
2807 BOOL WINAPI IsUserAnAdmin(VOID)
2808 {
2809     return SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS);
2810 }
2811 
2812 /*************************************************************************
2813  *              SHLimitInputEdit(SHELL32.@)
2814  */
2815 
2816 /* TODO: Show baloon popup window with TTS_BALLOON */
2817 
2818 typedef struct UxSubclassInfo
2819 {
2820     HWND hwnd;
2821     WNDPROC fnWndProc;
2822     LPWSTR pwszValidChars;
2823     LPWSTR pwszInvalidChars;
2824 } UxSubclassInfo;
2825 
2826 static void
2827 UxSubclassInfo_Destroy(UxSubclassInfo *pInfo)
2828 {
2829     if (!pInfo)
2830         return;
2831 
2832     RemovePropW(pInfo->hwnd, L"UxSubclassInfo");
2833 
2834     CoTaskMemFree(pInfo->pwszValidChars);
2835     CoTaskMemFree(pInfo->pwszInvalidChars);
2836 
2837     SetWindowLongPtrW(pInfo->hwnd, GWLP_WNDPROC, (LONG_PTR)pInfo->fnWndProc);
2838 
2839     HeapFree(GetProcessHeap(), 0, pInfo);
2840 }
2841 
2842 static BOOL
2843 DoSanitizeText(LPWSTR pszSanitized, LPCWSTR pszInvalidChars, LPCWSTR pszValidChars)
2844 {
2845     LPWSTR pch1, pch2;
2846     BOOL bFound = FALSE;
2847 
2848     for (pch1 = pch2 = pszSanitized; *pch1; ++pch1)
2849     {
2850         if (pszInvalidChars)
2851         {
2852             if (wcschr(pszInvalidChars, *pch1) != NULL)
2853             {
2854                 bFound = TRUE;
2855                 continue;
2856             }
2857         }
2858         else if (pszValidChars)
2859         {
2860             if (wcschr(pszValidChars, *pch1) == NULL)
2861             {
2862                 bFound = TRUE;
2863                 continue;
2864             }
2865         }
2866 
2867         *pch2 = *pch1;
2868         ++pch2;
2869     }
2870     *pch2 = 0;
2871 
2872     return bFound;
2873 }
2874 
2875 static void
2876 DoSanitizeClipboard(HWND hwnd, UxSubclassInfo *pInfo)
2877 {
2878     HGLOBAL hData;
2879     LPWSTR pszText, pszSanitized;
2880     DWORD cbData;
2881 
2882     if (GetWindowLongPtrW(hwnd, GWL_STYLE) & ES_READONLY)
2883         return;
2884     if (!OpenClipboard(hwnd))
2885         return;
2886 
2887     hData = GetClipboardData(CF_UNICODETEXT);
2888     pszText = GlobalLock(hData);
2889     if (!pszText)
2890     {
2891         CloseClipboard();
2892         return;
2893     }
2894     SHStrDupW(pszText, &pszSanitized);
2895     GlobalUnlock(hData);
2896 
2897     if (pszSanitized &&
2898         DoSanitizeText(pszSanitized, pInfo->pwszInvalidChars, pInfo->pwszValidChars))
2899     {
2900         MessageBeep(0xFFFFFFFF);
2901 
2902         /* Update clipboard text */
2903         cbData = (lstrlenW(pszSanitized) + 1) * sizeof(WCHAR);
2904         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbData);
2905         pszText = GlobalLock(hData);
2906         if (pszText)
2907         {
2908             CopyMemory(pszText, pszSanitized, cbData);
2909             GlobalUnlock(hData);
2910 
2911             SetClipboardData(CF_UNICODETEXT, hData);
2912         }
2913     }
2914 
2915     CoTaskMemFree(pszSanitized);
2916     CloseClipboard();
2917 }
2918 
2919 static LRESULT CALLBACK
2920 LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2921 {
2922     WNDPROC fnWndProc;
2923     WCHAR wch;
2924     UxSubclassInfo *pInfo = GetPropW(hwnd, L"UxSubclassInfo");
2925     if (!pInfo)
2926         return DefWindowProcW(hwnd, uMsg, wParam, lParam);
2927 
2928     fnWndProc = pInfo->fnWndProc;
2929 
2930     switch (uMsg)
2931     {
2932         case WM_KEYDOWN:
2933             if (GetKeyState(VK_SHIFT) < 0 && wParam == VK_INSERT)
2934                 DoSanitizeClipboard(hwnd, pInfo);
2935             else if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V')
2936                 DoSanitizeClipboard(hwnd, pInfo);
2937 
2938             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2939 
2940         case WM_PASTE:
2941             DoSanitizeClipboard(hwnd, pInfo);
2942             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2943 
2944         case WM_CHAR:
2945             if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V')
2946                 break;
2947 
2948             if (pInfo->pwszInvalidChars)
2949             {
2950                 if (wcschr(pInfo->pwszInvalidChars, (WCHAR)wParam) != NULL)
2951                 {
2952                     MessageBeep(0xFFFFFFFF);
2953                     break;
2954                 }
2955             }
2956             else if (pInfo->pwszValidChars)
2957             {
2958                 if (wcschr(pInfo->pwszValidChars, (WCHAR)wParam) == NULL)
2959                 {
2960                     MessageBeep(0xFFFFFFFF);
2961                     break;
2962                 }
2963             }
2964             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2965 
2966         case WM_UNICHAR:
2967             if (wParam == UNICODE_NOCHAR)
2968                 return TRUE;
2969 
2970             /* FALL THROUGH */
2971 
2972         case WM_IME_CHAR:
2973             wch = (WCHAR)wParam;
2974             if (GetKeyState(VK_CONTROL) < 0 && wch == L'V')
2975                 break;
2976 
2977             if (!IsWindowUnicode(hwnd) && HIBYTE(wch) != 0)
2978             {
2979                 CHAR data[] = {HIBYTE(wch), LOBYTE(wch)};
2980                 MultiByteToWideChar(CP_ACP, 0, data, 2, &wch, 1);
2981             }
2982 
2983             if (pInfo->pwszInvalidChars)
2984             {
2985                 if (wcschr(pInfo->pwszInvalidChars, wch) != NULL)
2986                 {
2987                     MessageBeep(0xFFFFFFFF);
2988                     break;
2989                 }
2990             }
2991             else if (pInfo->pwszValidChars)
2992             {
2993                 if (wcschr(pInfo->pwszValidChars, wch) == NULL)
2994                 {
2995                     MessageBeep(0xFFFFFFFF);
2996                     break;
2997                 }
2998             }
2999             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
3000 
3001         case WM_NCDESTROY:
3002             UxSubclassInfo_Destroy(pInfo);
3003             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
3004 
3005         default:
3006             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
3007     }
3008 
3009     return 0;
3010 }
3011 
3012 static UxSubclassInfo *
3013 UxSubclassInfo_Create(HWND hwnd, LPWSTR valid, LPWSTR invalid)
3014 {
3015     UxSubclassInfo *pInfo;
3016     pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UxSubclassInfo));
3017     if (!pInfo)
3018     {
3019         ERR("HeapAlloc failed.\n");
3020         CoTaskMemFree(valid);
3021         CoTaskMemFree(invalid);
3022         return NULL;
3023     }
3024 
3025     pInfo->fnWndProc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)LimitEditWindowProc);
3026     if (!pInfo->fnWndProc)
3027     {
3028         ERR("SetWindowLongPtrW failed\n");
3029         CoTaskMemFree(valid);
3030         CoTaskMemFree(invalid);
3031         HeapFree(GetProcessHeap(), 0, pInfo);
3032         return NULL;
3033     }
3034 
3035     pInfo->hwnd = hwnd;
3036     pInfo->pwszValidChars = valid;
3037     pInfo->pwszInvalidChars = invalid;
3038     if (!SetPropW(hwnd, L"UxSubclassInfo", pInfo))
3039     {
3040         UxSubclassInfo_Destroy(pInfo);
3041         pInfo = NULL;
3042     }
3043     return pInfo;
3044 }
3045 
3046 HRESULT WINAPI
3047 SHLimitInputEdit(HWND hWnd, IShellFolder *psf)
3048 {
3049     IItemNameLimits *pLimits;
3050     HRESULT hr;
3051     LPWSTR pwszValidChars, pwszInvalidChars;
3052     UxSubclassInfo *pInfo;
3053 
3054     pInfo = GetPropW(hWnd, L"UxSubclassInfo");
3055     if (pInfo)
3056     {
3057         UxSubclassInfo_Destroy(pInfo);
3058         pInfo = NULL;
3059     }
3060 
3061     hr = psf->lpVtbl->QueryInterface(psf, &IID_IItemNameLimits, (LPVOID *)&pLimits);
3062     if (FAILED(hr))
3063     {
3064         ERR("hr: %x\n", hr);
3065         return hr;
3066     }
3067 
3068     pwszValidChars = pwszInvalidChars = NULL;
3069     hr = pLimits->lpVtbl->GetValidCharacters(pLimits, &pwszValidChars, &pwszInvalidChars);
3070     if (FAILED(hr))
3071     {
3072         ERR("hr: %x\n", hr);
3073         pLimits->lpVtbl->Release(pLimits);
3074         return hr;
3075     }
3076 
3077     pInfo = UxSubclassInfo_Create(hWnd, pwszValidChars, pwszInvalidChars);
3078     if (!pInfo)
3079         hr = E_FAIL;
3080 
3081     pLimits->lpVtbl->Release(pLimits);
3082 
3083     return hr;
3084 }
3085 
3086 #ifdef __REACTOS__
3087 /*************************************************************************
3088  *  SHLimitInputCombo [SHELL32.748]
3089  *
3090  * Sets limits on valid characters for a combobox control.
3091  * This function works like SHLimitInputEdit, but the target is a combobox
3092  * instead of a textbox.
3093  */
3094 HRESULT WINAPI
3095 SHLimitInputCombo(HWND hWnd, IShellFolder *psf)
3096 {
3097     HWND hwndEdit;
3098 
3099     TRACE("%p %p\n", hWnd, psf);
3100 
3101     hwndEdit = GetTopWindow(hWnd);
3102     if (!hwndEdit)
3103         return E_FAIL;
3104 
3105     return SHLimitInputEdit(hwndEdit, psf);
3106 }
3107 #endif
3108