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