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