xref: /reactos/dll/win32/shell32/wine/shellord.c (revision 8a978a17)
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     static const WCHAR szExplorerKey[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer";
813     INT ret;
814     WCHAR szTargetPath[MAX_PATH], szLinkDir[MAX_PATH], szLinkFile[MAX_PATH], szDescription[80];
815     WCHAR szPath[MAX_PATH];
816     DWORD cbBuffer, data[64], datalen, type;
817     HANDLE hFind;
818     WIN32_FIND_DATAW find;
819     HKEY hExplorerKey;
820     LONG error;
821     LPWSTR pchDotExt, pchTargetTitle, pchLinkTitle;
822     MRUINFOW mru;
823     HANDLE hMRUList = NULL;
824     IShellLinkW *psl = NULL;
825     IPersistFile *pPf = NULL;
826     HRESULT hr;
827     BYTE Buffer[(MAX_PATH + 64) * sizeof(WCHAR)];
828 
829     TRACE("%04x %p\n", uFlags, pv);
830 
831     /* check policy */
832     ret = SHADD_get_policy("NoRecentDocsHistory", &type, data, &datalen);
833     if (ret > 0 && ret != ERROR_FILE_NOT_FOUND)
834     {
835         ERR("Error %d getting policy \"NoRecentDocsHistory\"\n", ret);
836     }
837     else if (ret == ERROR_SUCCESS)
838     {
839         if (!(type == REG_DWORD || (type == REG_BINARY && datalen == 4)))
840         {
841             ERR("Error policy data for \"NoRecentDocsHistory\" not formatted correctly, type=%d, len=%d\n",
842                 type, datalen);
843             return;
844         }
845 
846         TRACE("policy value for NoRecentDocsHistory = %08x\n", data[0]);
847         /* now test the actual policy value */
848         if (data[0] != 0)
849             return;
850     }
851 
852     /* store to szTargetPath */
853     szTargetPath[0] = 0;
854     if (pv)
855     {
856         switch (uFlags)
857         {
858             case SHARD_PATHA:
859                 MultiByteToWideChar(CP_ACP, 0, pv, -1, szLinkDir, ARRAYSIZE(szLinkDir));
860                 GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
861                 break;
862 
863             case SHARD_PATHW:
864                 GetFullPathNameW(pv, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
865                 break;
866 
867             case SHARD_PIDL:
868                 SHGetPathFromIDListW(pv, szLinkDir);
869                 GetFullPathNameW(szLinkDir, ARRAYSIZE(szTargetPath), szTargetPath, NULL);
870                 break;
871 
872             default:
873                 FIXME("Unsupported flags: %u\n", uFlags);
874                 return;
875         }
876     }
877 
878     /* get recent folder */
879     if (!SHGetSpecialFolderPathW(NULL, szLinkDir, CSIDL_RECENT, FALSE))
880     {
881         ERR("serious issues 1\n");
882         return;
883     }
884     TRACE("Users Recent dir %S\n", szLinkDir);
885 
886     /* open Explorer key */
887     error = RegCreateKeyExW(HKEY_CURRENT_USER, szExplorerKey, 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     TRACE("(%s, %p)n", debugstr_w(commandLine), parent);
1490     WCHAR parameters[MAX_PATH] = L"shell32.dll,Control_RunDLL ";
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 static const WCHAR szwCabLocation[] = {
1617   'S','o','f','t','w','a','r','e','\\',
1618   'M','i','c','r','o','s','o','f','t','\\',
1619   'W','i','n','d','o','w','s','\\',
1620   'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1621   'E','x','p','l','o','r','e','r','\\',
1622   'C','a','b','i','n','e','t','S','t','a','t','e',0
1623 };
1624 
1625 static const WCHAR szwSettings[] = { 'S','e','t','t','i','n','g','s',0 };
1626 
1627 /*************************************************************************
1628  * ReadCabinetState				[SHELL32.651] NT 4.0
1629  *
1630  */
1631 BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length)
1632 {
1633 	HKEY hkey = 0;
1634 	DWORD type, r;
1635 
1636 	TRACE("%p %d\n", cs, length);
1637 
1638 	if( (cs == NULL) || (length < (int)sizeof(*cs))  )
1639 		return FALSE;
1640 
1641 	r = RegOpenKeyW( HKEY_CURRENT_USER, szwCabLocation, &hkey );
1642 	if( r == ERROR_SUCCESS )
1643 	{
1644 		type = REG_BINARY;
1645 		r = RegQueryValueExW( hkey, szwSettings,
1646 			NULL, &type, (LPBYTE)cs, (LPDWORD)&length );
1647 		RegCloseKey( hkey );
1648 
1649 	}
1650 
1651 	/* if we can't read from the registry, create default values */
1652 	if ( (r != ERROR_SUCCESS) || (cs->cLength < sizeof(*cs)) ||
1653 		(cs->cLength != length) )
1654 	{
1655 		TRACE("Initializing shell cabinet settings\n");
1656 		memset(cs, 0, sizeof(*cs));
1657 		cs->cLength          = sizeof(*cs);
1658 		cs->nVersion         = 2;
1659 		cs->fFullPathTitle   = FALSE;
1660 		cs->fSaveLocalView   = TRUE;
1661 		cs->fNotShell        = FALSE;
1662 		cs->fSimpleDefault   = TRUE;
1663 		cs->fDontShowDescBar = FALSE;
1664 		cs->fNewWindowMode   = FALSE;
1665 		cs->fShowCompColor   = FALSE;
1666 		cs->fDontPrettyNames = FALSE;
1667 		cs->fAdminsCreateCommonGroups = TRUE;
1668 		cs->fMenuEnumFilter  = 96;
1669 	}
1670 
1671 	return TRUE;
1672 }
1673 
1674 /*************************************************************************
1675  * WriteCabinetState				[SHELL32.652] NT 4.0
1676  *
1677  */
1678 BOOL WINAPI WriteCabinetState(CABINETSTATE *cs)
1679 {
1680 	DWORD r;
1681 	HKEY hkey = 0;
1682 
1683 	TRACE("%p\n",cs);
1684 
1685 	if( cs == NULL )
1686 		return FALSE;
1687 
1688 	r = RegCreateKeyExW( HKEY_CURRENT_USER, szwCabLocation, 0,
1689 		 NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1690 	if( r == ERROR_SUCCESS )
1691 	{
1692 		r = RegSetValueExW( hkey, szwSettings, 0,
1693 			REG_BINARY, (LPBYTE) cs, cs->cLength);
1694 
1695 		RegCloseKey( hkey );
1696 	}
1697 
1698 	return (r==ERROR_SUCCESS);
1699 }
1700 
1701 /*************************************************************************
1702  * FileIconInit 				[SHELL32.660]
1703  *
1704  */
1705 BOOL WINAPI FileIconInit(BOOL bFullInit)
1706 {
1707     return SIC_Initialize();
1708 }
1709 
1710 /*************************************************************************
1711  * SetAppStartingCursor				[SHELL32.99]
1712  */
1713 HRESULT WINAPI SetAppStartingCursor(HWND u, DWORD v)
1714 {	FIXME("hwnd=%p 0x%04x stub\n",u,v );
1715 	return 0;
1716 }
1717 
1718 /*************************************************************************
1719  * SHLoadOLE					[SHELL32.151]
1720  *
1721  * To reduce the memory usage of Windows 95, its shell32 contained an
1722  * internal implementation of a part of COM (see e.g. SHGetMalloc, SHCoCreateInstance,
1723  * SHRegisterDragDrop etc.) that allowed to use in-process STA objects without
1724  * the need to load OLE32.DLL. If OLE32.DLL was already loaded, the SH* function
1725  * would just call the Co* functions.
1726  *
1727  * The SHLoadOLE was called when OLE32.DLL was being loaded to transfer all the
1728  * information from the shell32 "mini-COM" to ole32.dll.
1729  *
1730  * See http://blogs.msdn.com/oldnewthing/archive/2004/07/05/173226.aspx for a
1731  * detailed description.
1732  *
1733  * Under wine ole32.dll is always loaded as it is imported by shlwapi.dll which is
1734  * imported by shell32 and no "mini-COM" is used (except for the "LoadWithoutCOM"
1735  * hack in SHCoCreateInstance)
1736  */
1737 HRESULT WINAPI SHLoadOLE(LPARAM lParam)
1738 {	FIXME("0x%08lx stub\n",lParam);
1739 	return S_OK;
1740 }
1741 /*************************************************************************
1742  * DriveType					[SHELL32.64]
1743  *
1744  */
1745 int WINAPI DriveType(int DriveType)
1746 {
1747     WCHAR root[] = L"A:\\";
1748     root[0] = L'A' + DriveType;
1749     return GetDriveTypeW(root);
1750 }
1751 /*************************************************************************
1752  * InvalidateDriveType            [SHELL32.65]
1753  * Unimplemented in XP SP3
1754  */
1755 int WINAPI InvalidateDriveType(int u)
1756 {
1757     TRACE("0x%08x stub\n",u);
1758     return 0;
1759 }
1760 /*************************************************************************
1761  * SHAbortInvokeCommand				[SHELL32.198]
1762  *
1763  */
1764 HRESULT WINAPI SHAbortInvokeCommand(void)
1765 {	FIXME("stub\n");
1766 	return 1;
1767 }
1768 /*************************************************************************
1769  * SHOutOfMemoryMessageBox			[SHELL32.126]
1770  *
1771  */
1772 int WINAPI SHOutOfMemoryMessageBox(
1773 	HWND hwndOwner,
1774 	LPCSTR lpCaption,
1775 	UINT uType)
1776 {
1777 	FIXME("%p %s 0x%08x stub\n",hwndOwner, lpCaption, uType);
1778 	return 0;
1779 }
1780 /*************************************************************************
1781  * SHFlushClipboard				[SHELL32.121]
1782  *
1783  */
1784 HRESULT WINAPI SHFlushClipboard(void)
1785 {
1786     return OleFlushClipboard();
1787 }
1788 
1789 /*************************************************************************
1790  * SHWaitForFileToOpen				[SHELL32.97]
1791  *
1792  */
1793 BOOL WINAPI SHWaitForFileToOpen(
1794 	LPCITEMIDLIST pidl,
1795 	DWORD dwFlags,
1796 	DWORD dwTimeout)
1797 {
1798 	FIXME("%p 0x%08x 0x%08x stub\n", pidl, dwFlags, dwTimeout);
1799 	return FALSE;
1800 }
1801 
1802 /************************************************************************
1803  *	RLBuildListOfPaths			[SHELL32.146]
1804  *
1805  * NOTES
1806  *   builds a DPA
1807  */
1808 DWORD WINAPI RLBuildListOfPaths (void)
1809 {	FIXME("stub\n");
1810 	return 0;
1811 }
1812 /************************************************************************
1813  *	SHValidateUNC				[SHELL32.173]
1814  *
1815  */
1816 BOOL WINAPI SHValidateUNC (HWND hwndOwner, PWSTR pszFile, UINT fConnect)
1817 {
1818 	FIXME("(%p, %s, 0x%08x): stub\n", hwndOwner, debugstr_w(pszFile), fConnect);
1819 	return FALSE;
1820 }
1821 
1822 /************************************************************************
1823  * DoEnvironmentSubstA [SHELL32.@]
1824  *
1825  * See DoEnvironmentSubstW.
1826  */
1827 DWORD WINAPI DoEnvironmentSubstA(LPSTR pszString, UINT cchString)
1828 {
1829     LPSTR dst;
1830     BOOL res = FALSE;
1831     DWORD len = cchString;
1832 
1833     TRACE("(%s, %d)\n", debugstr_a(pszString), cchString);
1834     if (pszString == NULL) /* Really return 0? */
1835         return 0;
1836     if ((dst = (LPSTR)HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(CHAR))))
1837     {
1838         len = ExpandEnvironmentStringsA(pszString, dst, cchString);
1839         /* len includes the terminating 0 */
1840         if (len && len < cchString)
1841         {
1842             res = TRUE;
1843             memcpy(pszString, dst, len);
1844         }
1845         else
1846             len = cchString;
1847 
1848         HeapFree(GetProcessHeap(), 0, dst);
1849     }
1850     return MAKELONG(len, res);
1851 }
1852 
1853 /************************************************************************
1854  * DoEnvironmentSubstW [SHELL32.@]
1855  *
1856  * Replace all %KEYWORD% in the string with the value of the named
1857  * environment variable. If the buffer is too small, the string is not modified.
1858  *
1859  * PARAMS
1860  *  pszString  [I] '\0' terminated string with %keyword%.
1861  *             [O] '\0' terminated string with %keyword% substituted.
1862  *  cchString  [I] size of str.
1863  *
1864  * RETURNS
1865  *  Success:  The string in the buffer is updated
1866  *            HIWORD: TRUE
1867  *            LOWORD: characters used in the buffer, including space for the terminating 0
1868  *  Failure:  buffer too small. The string is not modified.
1869  *            HIWORD: FALSE
1870  *            LOWORD: provided size of the buffer in characters
1871  */
1872 DWORD WINAPI DoEnvironmentSubstW(LPWSTR pszString, UINT cchString)
1873 {
1874     LPWSTR dst;
1875     BOOL res = FALSE;
1876     DWORD len = cchString;
1877 
1878     TRACE("(%s, %d)\n", debugstr_w(pszString), cchString);
1879 
1880     if ((cchString < MAXLONG) && (dst = HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(WCHAR))))
1881     {
1882         len = ExpandEnvironmentStringsW(pszString, dst, cchString);
1883         /* len includes the terminating 0 */
1884         if (len && len <= cchString)
1885         {
1886             res = TRUE;
1887             memcpy(pszString, dst, len * sizeof(WCHAR));
1888         }
1889         else
1890             len = cchString;
1891 
1892         HeapFree(GetProcessHeap(), 0, dst);
1893     }
1894     return MAKELONG(len, res);
1895 }
1896 
1897 /************************************************************************
1898  *	DoEnvironmentSubst			[SHELL32.53]
1899  *
1900  * See DoEnvironmentSubstA.
1901  */
1902 DWORD WINAPI DoEnvironmentSubstAW(LPVOID x, UINT y)
1903 {
1904     if (SHELL_OsIsUnicode())
1905         return DoEnvironmentSubstW(x, y);
1906     return DoEnvironmentSubstA(x, y);
1907 }
1908 
1909 /*************************************************************************
1910  *      GUIDFromStringA   [SHELL32.703]
1911  */
1912 BOOL WINAPI GUIDFromStringA(LPCSTR str, LPGUID guid)
1913 {
1914     TRACE("GUIDFromStringA() stub\n");
1915     return FALSE;
1916 }
1917 
1918 /*************************************************************************
1919  *      GUIDFromStringW   [SHELL32.704]
1920  */
1921 BOOL WINAPI GUIDFromStringW(LPCWSTR str, LPGUID guid)
1922 {
1923     UNICODE_STRING guid_str;
1924 
1925     RtlInitUnicodeString(&guid_str, str);
1926     return !RtlGUIDFromString(&guid_str, guid);
1927 }
1928 
1929 /*************************************************************************
1930  *      PathIsTemporaryA    [SHELL32.713]
1931  */
1932 BOOL WINAPI PathIsTemporaryA(LPSTR Str)
1933 {
1934     FIXME("(%s)stub\n", debugstr_a(Str));
1935     return FALSE;
1936 }
1937 
1938 /*************************************************************************
1939  *      PathIsTemporaryW    [SHELL32.714]
1940  */
1941 BOOL WINAPI PathIsTemporaryW(LPWSTR Str)
1942 {
1943     FIXME("(%s)stub\n", debugstr_w(Str));
1944     return FALSE;
1945 }
1946 
1947 typedef struct _PSXA
1948 {
1949     UINT uiCount;
1950     UINT uiAllocated;
1951     IShellPropSheetExt *pspsx[1];
1952 } PSXA, *PPSXA;
1953 
1954 typedef struct _PSXA_CALL
1955 {
1956     LPFNADDPROPSHEETPAGE lpfnAddReplaceWith;
1957     LPARAM lParam;
1958     BOOL bCalled;
1959     BOOL bMultiple;
1960     UINT uiCount;
1961 } PSXA_CALL, *PPSXA_CALL;
1962 
1963 static BOOL CALLBACK PsxaCall(HPROPSHEETPAGE hpage, LPARAM lParam)
1964 {
1965     PPSXA_CALL Call = (PPSXA_CALL)lParam;
1966 
1967     if (Call != NULL)
1968     {
1969         if ((Call->bMultiple || !Call->bCalled) &&
1970             Call->lpfnAddReplaceWith(hpage, Call->lParam))
1971         {
1972             Call->bCalled = TRUE;
1973             Call->uiCount++;
1974             return TRUE;
1975         }
1976     }
1977 
1978     return FALSE;
1979 }
1980 
1981 /*************************************************************************
1982  *      SHAddFromPropSheetExtArray	[SHELL32.167]
1983  */
1984 UINT WINAPI SHAddFromPropSheetExtArray(HPSXA hpsxa, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
1985 {
1986     PSXA_CALL Call;
1987     UINT i;
1988     PPSXA psxa = (PPSXA)hpsxa;
1989 
1990     TRACE("(%p,%p,%08lx)\n", hpsxa, lpfnAddPage, lParam);
1991 
1992     if (psxa)
1993     {
1994         ZeroMemory(&Call, sizeof(Call));
1995         Call.lpfnAddReplaceWith = lpfnAddPage;
1996         Call.lParam = lParam;
1997         Call.bMultiple = TRUE;
1998 
1999         /* Call the AddPage method of all registered IShellPropSheetExt interfaces */
2000         for (i = 0; i != psxa->uiCount; i++)
2001         {
2002             psxa->pspsx[i]->lpVtbl->AddPages(psxa->pspsx[i], PsxaCall, (LPARAM)&Call);
2003         }
2004 
2005         return Call.uiCount;
2006     }
2007 
2008     return 0;
2009 }
2010 
2011 /*************************************************************************
2012  *      SHCreatePropSheetExtArray	[SHELL32.168]
2013  */
2014 HPSXA WINAPI SHCreatePropSheetExtArray(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface)
2015 {
2016     return SHCreatePropSheetExtArrayEx(hKey, pszSubKey, max_iface, NULL);
2017 }
2018 
2019 /*************************************************************************
2020  *      SHCreatePropSheetExtArrayEx	[SHELL32.194]
2021  */
2022 HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, LPDATAOBJECT pDataObj)
2023 {
2024     static const WCHAR szPropSheetSubKey[] = {'s','h','e','l','l','e','x','\\','P','r','o','p','e','r','t','y','S','h','e','e','t','H','a','n','d','l','e','r','s',0};
2025     WCHAR szHandler[64];
2026     DWORD dwHandlerLen;
2027     WCHAR szClsidHandler[39];
2028     DWORD dwClsidSize;
2029     CLSID clsid;
2030     LONG lRet;
2031     DWORD dwIndex;
2032     IShellExtInit *psxi;
2033     IShellPropSheetExt *pspsx;
2034     HKEY hkBase, hkPropSheetHandlers;
2035     PPSXA psxa = NULL;
2036 
2037     TRACE("(%p,%s,%u)\n", hKey, debugstr_w(pszSubKey), max_iface);
2038 
2039     if (max_iface == 0)
2040         return NULL;
2041 
2042     /* Open the registry key */
2043     lRet = RegOpenKeyW(hKey, pszSubKey, &hkBase);
2044     if (lRet != ERROR_SUCCESS)
2045         return NULL;
2046 
2047     lRet = RegOpenKeyExW(hkBase, szPropSheetSubKey, 0, KEY_ENUMERATE_SUB_KEYS, &hkPropSheetHandlers);
2048     RegCloseKey(hkBase);
2049     if (lRet == ERROR_SUCCESS)
2050     {
2051         /* Create and initialize the Property Sheet Extensions Array */
2052         psxa = LocalAlloc(LMEM_FIXED, FIELD_OFFSET(PSXA, pspsx[max_iface]));
2053         if (psxa)
2054         {
2055             ZeroMemory(psxa, FIELD_OFFSET(PSXA, pspsx[max_iface]));
2056             psxa->uiAllocated = max_iface;
2057 
2058             /* Enumerate all subkeys and attempt to load the shell extensions */
2059             dwIndex = 0;
2060             do
2061             {
2062                 dwHandlerLen = sizeof(szHandler) / sizeof(szHandler[0]);
2063                 lRet = RegEnumKeyExW(hkPropSheetHandlers, dwIndex++, szHandler, &dwHandlerLen, NULL, NULL, NULL, NULL);
2064                 if (lRet != ERROR_SUCCESS)
2065                 {
2066                     if (lRet == ERROR_MORE_DATA)
2067                         continue;
2068 
2069                     if (lRet == ERROR_NO_MORE_ITEMS)
2070                         lRet = ERROR_SUCCESS;
2071                     break;
2072                 }
2073 
2074                 /* The CLSID is stored either in the key itself or in its default value. */
2075                 if (FAILED(lRet = SHCLSIDFromStringW(szHandler, &clsid)))
2076                 {
2077                     dwClsidSize = sizeof(szClsidHandler);
2078                     if (SHGetValueW(hkPropSheetHandlers, szHandler, NULL, NULL, szClsidHandler, &dwClsidSize) == ERROR_SUCCESS)
2079                     {
2080                         /* Force a NULL-termination and convert the string */
2081                         szClsidHandler[(sizeof(szClsidHandler) / sizeof(szClsidHandler[0])) - 1] = 0;
2082                         lRet = SHCLSIDFromStringW(szClsidHandler, &clsid);
2083                     }
2084                 }
2085 
2086                 if (SUCCEEDED(lRet))
2087                 {
2088                     /* Attempt to get an IShellPropSheetExt and an IShellExtInit instance.
2089                        Only if both interfaces are supported it's a real shell extension.
2090                        Then call IShellExtInit's Initialize method. */
2091                     if (SUCCEEDED(CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER/* | CLSCTX_NO_CODE_DOWNLOAD */, &IID_IShellPropSheetExt, (LPVOID *)&pspsx)))
2092                     {
2093                         if (SUCCEEDED(pspsx->lpVtbl->QueryInterface(pspsx, &IID_IShellExtInit, (PVOID *)&psxi)))
2094                         {
2095                             if (SUCCEEDED(psxi->lpVtbl->Initialize(psxi, NULL, pDataObj, hKey)))
2096                             {
2097                                 /* Add the IShellPropSheetExt instance to the array */
2098                                 psxa->pspsx[psxa->uiCount++] = pspsx;
2099                             }
2100                             else
2101                             {
2102                                 psxi->lpVtbl->Release(psxi);
2103                                 pspsx->lpVtbl->Release(pspsx);
2104                             }
2105                         }
2106                         else
2107                             pspsx->lpVtbl->Release(pspsx);
2108                     }
2109                 }
2110 
2111             } while (psxa->uiCount != psxa->uiAllocated);
2112         }
2113         else
2114             lRet = ERROR_NOT_ENOUGH_MEMORY;
2115 
2116         RegCloseKey(hkPropSheetHandlers);
2117     }
2118 
2119     if (lRet != ERROR_SUCCESS && psxa)
2120     {
2121         SHDestroyPropSheetExtArray((HPSXA)psxa);
2122         psxa = NULL;
2123     }
2124 
2125     return (HPSXA)psxa;
2126 }
2127 
2128 /*************************************************************************
2129  *      SHReplaceFromPropSheetExtArray	[SHELL32.170]
2130  */
2131 UINT WINAPI SHReplaceFromPropSheetExtArray(HPSXA hpsxa, UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
2132 {
2133     PSXA_CALL Call;
2134     UINT i;
2135     PPSXA psxa = (PPSXA)hpsxa;
2136 
2137     TRACE("(%p,%u,%p,%08lx)\n", hpsxa, uPageID, lpfnReplaceWith, lParam);
2138 
2139     if (psxa)
2140     {
2141         ZeroMemory(&Call, sizeof(Call));
2142         Call.lpfnAddReplaceWith = lpfnReplaceWith;
2143         Call.lParam = lParam;
2144 
2145         /* Call the ReplacePage method of all registered IShellPropSheetExt interfaces.
2146            Each shell extension is only allowed to call the callback once during the callback. */
2147         for (i = 0; i != psxa->uiCount; i++)
2148         {
2149             Call.bCalled = FALSE;
2150             psxa->pspsx[i]->lpVtbl->ReplacePage(psxa->pspsx[i], uPageID, PsxaCall, (LPARAM)&Call);
2151         }
2152 
2153         return Call.uiCount;
2154     }
2155 
2156     return 0;
2157 }
2158 
2159 /*************************************************************************
2160  *      SHDestroyPropSheetExtArray	[SHELL32.169]
2161  */
2162 void WINAPI SHDestroyPropSheetExtArray(HPSXA hpsxa)
2163 {
2164     UINT i;
2165     PPSXA psxa = (PPSXA)hpsxa;
2166 
2167     TRACE("(%p)\n", hpsxa);
2168 
2169     if (psxa)
2170     {
2171         for (i = 0; i != psxa->uiCount; i++)
2172         {
2173             psxa->pspsx[i]->lpVtbl->Release(psxa->pspsx[i]);
2174         }
2175 
2176         LocalFree(psxa);
2177     }
2178 }
2179 
2180 /*************************************************************************
2181  *      CIDLData_CreateFromIDArray	[SHELL32.83]
2182  *
2183  *  Create IDataObject from PIDLs??
2184  */
2185 HRESULT WINAPI CIDLData_CreateFromIDArray(
2186 	PCIDLIST_ABSOLUTE pidlFolder,
2187     UINT cpidlFiles,
2188 	PCUIDLIST_RELATIVE_ARRAY lppidlFiles,
2189 	LPDATAOBJECT *ppdataObject)
2190 {
2191     UINT i;
2192     HWND hwnd = 0;   /*FIXME: who should be hwnd of owner? set to desktop */
2193     HRESULT hResult;
2194 
2195     TRACE("(%p, %d, %p, %p)\n", pidlFolder, cpidlFiles, lppidlFiles, ppdataObject);
2196     if (TRACE_ON(pidl))
2197     {
2198 	pdump (pidlFolder);
2199 	for (i=0; i<cpidlFiles; i++) pdump (lppidlFiles[i]);
2200     }
2201     hResult = IDataObject_Constructor(hwnd, pidlFolder, lppidlFiles, cpidlFiles, FALSE, ppdataObject);
2202     return hResult;
2203 }
2204 
2205 /*************************************************************************
2206  * SHCreateStdEnumFmtEtc			[SHELL32.74]
2207  *
2208  * NOTES
2209  *
2210  */
2211 HRESULT WINAPI SHCreateStdEnumFmtEtc(
2212     UINT cFormats,
2213 	const FORMATETC *lpFormats,
2214 	LPENUMFORMATETC *ppenumFormatetc)
2215 {
2216 	IEnumFORMATETC *pef;
2217 	HRESULT hRes;
2218 	TRACE("cf=%d fe=%p pef=%p\n", cFormats, lpFormats, ppenumFormatetc);
2219 
2220     hRes = IEnumFORMATETC_Constructor(cFormats, lpFormats, &pef);
2221     if (FAILED(hRes))
2222         return hRes;
2223 
2224 	IEnumFORMATETC_AddRef(pef);
2225 	hRes = IEnumFORMATETC_QueryInterface(pef, &IID_IEnumFORMATETC, (LPVOID*)ppenumFormatetc);
2226 	IEnumFORMATETC_Release(pef);
2227 
2228 	return hRes;
2229 }
2230 
2231 /*************************************************************************
2232  *		SHFindFiles (SHELL32.90)
2233  */
2234 BOOL WINAPI SHFindFiles( PCIDLIST_ABSOLUTE pidlFolder, PCIDLIST_ABSOLUTE pidlSaveFile )
2235 {
2236     FIXME("params ignored: %p %p\n", pidlFolder, pidlSaveFile);
2237     if (SHRestricted(REST_NOFIND))
2238     {
2239         return FALSE;
2240     }
2241     /* Open the search results folder */
2242     /* FIXME: CSearchBar should be opened as well */
2243     return ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{E17D4FC0-5564-11D1-83F2-00A0C90DC849}", NULL, SW_SHOWNORMAL) > (HINSTANCE)32;
2244 }
2245 
2246 /*************************************************************************
2247  *		SHUpdateImageW (SHELL32.192)
2248  *
2249  * Notifies the shell that an icon in the system image list has been changed.
2250  *
2251  * PARAMS
2252  *  pszHashItem [I] Path to file that contains the icon.
2253  *  iIndex      [I] Zero-based index of the icon in the file.
2254  *  uFlags      [I] Flags determining the icon attributes. See notes.
2255  *  iImageIndex [I] Index of the icon in the system image list.
2256  *
2257  * RETURNS
2258  *  Nothing
2259  *
2260  * NOTES
2261  *  uFlags can be one or more of the following flags:
2262  *  GIL_NOTFILENAME - pszHashItem is not a file name.
2263  *  GIL_SIMULATEDOC - Create a document icon using the specified icon.
2264  */
2265 void WINAPI SHUpdateImageW(LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex)
2266 {
2267     FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_w(pszHashItem), iIndex, uFlags, iImageIndex);
2268 }
2269 
2270 /*************************************************************************
2271  *		SHUpdateImageA (SHELL32.191)
2272  *
2273  * See SHUpdateImageW.
2274  */
2275 VOID WINAPI SHUpdateImageA(LPCSTR pszHashItem, INT iIndex, UINT uFlags, INT iImageIndex)
2276 {
2277     FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_a(pszHashItem), iIndex, uFlags, iImageIndex);
2278 }
2279 
2280 INT WINAPI SHHandleUpdateImage(PCIDLIST_ABSOLUTE pidlExtra)
2281 {
2282     FIXME("%p - stub\n", pidlExtra);
2283 
2284     return -1;
2285 }
2286 
2287 BOOL WINAPI SHObjectProperties(HWND hwnd, DWORD dwType, LPCWSTR szObject, LPCWSTR szPage)
2288 {
2289     FIXME("%p, 0x%08x, %s, %s - stub\n", hwnd, dwType, debugstr_w(szObject), debugstr_w(szPage));
2290 
2291     return TRUE;
2292 }
2293 
2294 BOOL WINAPI SHGetNewLinkInfoA(LPCSTR pszLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL *pfMustCopy,
2295                               UINT uFlags)
2296 {
2297     WCHAR wszLinkTo[MAX_PATH];
2298     WCHAR wszDir[MAX_PATH];
2299     WCHAR wszName[MAX_PATH];
2300     BOOL res;
2301 
2302     MultiByteToWideChar(CP_ACP, 0, pszLinkTo, -1, wszLinkTo, MAX_PATH);
2303     MultiByteToWideChar(CP_ACP, 0, pszDir, -1, wszDir, MAX_PATH);
2304 
2305     res = SHGetNewLinkInfoW(wszLinkTo, wszDir, wszName, pfMustCopy, uFlags);
2306 
2307     if (res)
2308         WideCharToMultiByte(CP_ACP, 0, wszName, -1, pszName, MAX_PATH, NULL, NULL);
2309 
2310     return res;
2311 }
2312 
2313 BOOL WINAPI SHGetNewLinkInfoW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL *pfMustCopy,
2314                               UINT uFlags)
2315 {
2316     const WCHAR *basename;
2317     WCHAR *dst_basename;
2318     int i=2;
2319     static const WCHAR lnkformat[] = {'%','s','.','l','n','k',0};
2320     static const WCHAR lnkformatnum[] = {'%','s',' ','(','%','d',')','.','l','n','k',0};
2321 
2322     TRACE("(%s, %s, %p, %p, 0x%08x)\n", debugstr_w(pszLinkTo), debugstr_w(pszDir),
2323           pszName, pfMustCopy, uFlags);
2324 
2325     *pfMustCopy = FALSE;
2326 
2327     if (uFlags & SHGNLI_PIDL)
2328     {
2329         FIXME("SHGNLI_PIDL flag unsupported\n");
2330         return FALSE;
2331     }
2332 
2333     if (uFlags)
2334         FIXME("ignoring flags: 0x%08x\n", uFlags);
2335 
2336     /* FIXME: should test if the file is a shortcut or DOS program */
2337     if (GetFileAttributesW(pszLinkTo) == INVALID_FILE_ATTRIBUTES)
2338         return FALSE;
2339 
2340     basename = strrchrW(pszLinkTo, '\\');
2341     if (basename)
2342         basename = basename+1;
2343     else
2344         basename = pszLinkTo;
2345 
2346     lstrcpynW(pszName, pszDir, MAX_PATH);
2347     if (!PathAddBackslashW(pszName))
2348         return FALSE;
2349 
2350     dst_basename = pszName + strlenW(pszName);
2351 
2352     snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, lnkformat, basename);
2353 
2354     while (GetFileAttributesW(pszName) != INVALID_FILE_ATTRIBUTES)
2355     {
2356         snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, lnkformatnum, basename, i);
2357         i++;
2358     }
2359 
2360     return TRUE;
2361 }
2362 
2363 HRESULT WINAPI SHStartNetConnectionDialog(HWND hwnd, LPCSTR pszRemoteName, DWORD dwType)
2364 {
2365     FIXME("%p, %s, 0x%08x - stub\n", hwnd, debugstr_a(pszRemoteName), dwType);
2366 
2367     return S_OK;
2368 }
2369 /*************************************************************************
2370  *              SHSetLocalizedName (SHELL32.@)
2371  */
2372 HRESULT WINAPI SHSetLocalizedName(LPCWSTR pszPath, LPCWSTR pszResModule, int idsRes)
2373 {
2374     FIXME("%p, %s, %d - stub\n", pszPath, debugstr_w(pszResModule), idsRes);
2375 
2376     return S_OK;
2377 }
2378 
2379 /*************************************************************************
2380  *              LinkWindow_RegisterClass (SHELL32.258)
2381  */
2382 BOOL WINAPI LinkWindow_RegisterClass(void)
2383 {
2384     FIXME("()\n");
2385     return TRUE;
2386 }
2387 
2388 /*************************************************************************
2389  *              LinkWindow_UnregisterClass (SHELL32.259)
2390  */
2391 BOOL WINAPI LinkWindow_UnregisterClass(DWORD dwUnused)
2392 {
2393     FIXME("()\n");
2394     return TRUE;
2395 }
2396 
2397 /*************************************************************************
2398  *              SHFlushSFCache (SHELL32.526)
2399  *
2400  * Notifies the shell that a user-specified special folder location has changed.
2401  *
2402  * NOTES
2403  *   In Wine, the shell folder registry values are not cached, so this function
2404  *   has no effect.
2405  */
2406 void WINAPI SHFlushSFCache(void)
2407 {
2408 }
2409 
2410 /*************************************************************************
2411  *              SHGetImageList (SHELL32.727)
2412  *
2413  * Returns a copy of a shell image list.
2414  *
2415  * NOTES
2416  *   Windows XP features 4 sizes of image list, and Vista 5. Wine currently
2417  *   only supports the traditional small and large image lists, so requests
2418  *   for the others will currently fail.
2419  */
2420 HRESULT WINAPI SHGetImageList(int iImageList, REFIID riid, void **ppv)
2421 {
2422     HIMAGELIST hLarge, hSmall;
2423     HIMAGELIST hNew;
2424     HRESULT ret = E_FAIL;
2425 
2426     /* Wine currently only maintains large and small image lists */
2427     if ((iImageList != SHIL_LARGE) && (iImageList != SHIL_SMALL) && (iImageList != SHIL_SYSSMALL))
2428     {
2429         FIXME("Unsupported image list %i requested\n", iImageList);
2430         return E_FAIL;
2431     }
2432 
2433     Shell_GetImageLists(&hLarge, &hSmall);
2434 #ifndef __REACTOS__
2435     hNew = ImageList_Duplicate(iImageList == SHIL_LARGE ? hLarge : hSmall);
2436 
2437     /* Get the interface for the new image list */
2438     if (hNew)
2439     {
2440         ret = HIMAGELIST_QueryInterface(hNew, riid, ppv);
2441         ImageList_Destroy(hNew);
2442     }
2443 #else
2444     /* Duplicating the imagelist causes the start menu items not to draw on
2445      * the first show. Was the Duplicate necessary for some reason? I believe
2446      * Windows returns the raw pointer here. */
2447     hNew = (iImageList == SHIL_LARGE ? hLarge : hSmall);
2448     ret = IImageList2_QueryInterface((IImageList2 *) hNew, riid, ppv);
2449 #endif
2450 
2451     return ret;
2452 }
2453 
2454 #ifndef __REACTOS__
2455 
2456 /*************************************************************************
2457  * SHCreateShellFolderView			[SHELL32.256]
2458  *
2459  * Create a new instance of the default Shell folder view object.
2460  *
2461  * RETURNS
2462  *  Success: S_OK
2463  *  Failure: error value
2464  *
2465  * NOTES
2466  *  see IShellFolder::CreateViewObject
2467  */
2468 HRESULT WINAPI SHCreateShellFolderView(const SFV_CREATE *pcsfv,
2469                         IShellView **ppsv)
2470 {
2471 	IShellView * psf;
2472 	HRESULT hRes;
2473 
2474 	*ppsv = NULL;
2475 	if (!pcsfv || pcsfv->cbSize != sizeof(*pcsfv))
2476 	  return E_INVALIDARG;
2477 
2478 	TRACE("sf=%p outer=%p callback=%p\n",
2479 	  pcsfv->pshf, pcsfv->psvOuter, pcsfv->psfvcb);
2480 
2481     hRes = IShellView_Constructor(pcsfv->pshf, &psf);
2482     if (FAILED(hRes))
2483         return hRes;
2484 
2485 	hRes = IShellView_QueryInterface(psf, &IID_IShellView, (LPVOID *)ppsv);
2486 	IShellView_Release(psf);
2487 
2488 	return hRes;
2489 }
2490 #endif
2491 
2492 
2493 /*************************************************************************
2494  * SHTestTokenMembership    [SHELL32.245]
2495  *
2496  * Checks whether a given token is a mamber of a local group with the
2497  * specified RID.
2498  *
2499  */
2500 EXTERN_C BOOL
2501 WINAPI
2502 SHTestTokenMembership(HANDLE TokenHandle, ULONG ulRID)
2503 {
2504     SID_IDENTIFIER_AUTHORITY ntAuth = {SECURITY_NT_AUTHORITY};
2505     DWORD nSubAuthority0, nSubAuthority1;
2506     DWORD nSubAuthorityCount;
2507     PSID SidToCheck;
2508     BOOL IsMember = FALSE;
2509 
2510     if ((ulRID == SECURITY_SERVICE_RID) || ulRID == SECURITY_LOCAL_SYSTEM_RID)
2511     {
2512         nSubAuthority0 = ulRID;
2513         nSubAuthority1 = 0;
2514         nSubAuthorityCount= 1;
2515     }
2516     else
2517     {
2518         nSubAuthority0 = SECURITY_BUILTIN_DOMAIN_RID;
2519         nSubAuthority1 = ulRID;
2520         nSubAuthorityCount= 2;
2521     }
2522 
2523     if (!AllocateAndInitializeSid(&ntAuth,
2524                                   nSubAuthorityCount,
2525                                   nSubAuthority0,
2526                                   nSubAuthority1,
2527                                   0, 0, 0, 0, 0, 0,
2528                                   &SidToCheck))
2529     {
2530         return FALSE;
2531     }
2532 
2533     if (!CheckTokenMembership(TokenHandle, SidToCheck, &IsMember))
2534     {
2535         IsMember = FALSE;
2536     }
2537 
2538     FreeSid(SidToCheck);
2539     return IsMember;
2540 }
2541 
2542 /*************************************************************************
2543  * IsUserAnAdmin    [SHELL32.680] NT 4.0
2544  *
2545  * Checks whether the current user is a member of the Administrators group.
2546  *
2547  * PARAMS
2548  *     None
2549  *
2550  * RETURNS
2551  *     Success: TRUE
2552  *     Failure: FALSE
2553  */
2554 BOOL WINAPI IsUserAnAdmin(VOID)
2555 {
2556     return SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS);
2557 }
2558 
2559 /*************************************************************************
2560  *              SHLimitInputEdit(SHELL32.@)
2561  */
2562 
2563 /* TODO: Show baloon popup window with TTS_BALLOON */
2564 
2565 typedef struct UxSubclassInfo
2566 {
2567     HWND hwnd;
2568     WNDPROC fnWndProc;
2569     LPWSTR pwszValidChars;
2570     LPWSTR pwszInvalidChars;
2571 } UxSubclassInfo;
2572 
2573 static void
2574 UxSubclassInfo_Destroy(UxSubclassInfo *pInfo)
2575 {
2576     if (!pInfo)
2577         return;
2578 
2579     RemovePropW(pInfo->hwnd, L"UxSubclassInfo");
2580 
2581     CoTaskMemFree(pInfo->pwszValidChars);
2582     CoTaskMemFree(pInfo->pwszInvalidChars);
2583 
2584     SetWindowLongPtrW(pInfo->hwnd, GWLP_WNDPROC, (LONG_PTR)pInfo->fnWndProc);
2585 
2586     HeapFree(GetProcessHeap(), 0, pInfo);
2587 }
2588 
2589 static BOOL
2590 DoSanitizeText(LPWSTR pszSanitized, LPCWSTR pszInvalidChars, LPCWSTR pszValidChars)
2591 {
2592     LPWSTR pch1, pch2;
2593     BOOL bFound = FALSE;
2594 
2595     for (pch1 = pch2 = pszSanitized; *pch1; ++pch1)
2596     {
2597         if (pszInvalidChars)
2598         {
2599             if (wcschr(pszInvalidChars, *pch1) != NULL)
2600             {
2601                 bFound = TRUE;
2602                 continue;
2603             }
2604         }
2605         else if (pszValidChars)
2606         {
2607             if (wcschr(pszValidChars, *pch1) == NULL)
2608             {
2609                 bFound = TRUE;
2610                 continue;
2611             }
2612         }
2613 
2614         *pch2 = *pch1;
2615         ++pch2;
2616     }
2617     *pch2 = 0;
2618 
2619     return bFound;
2620 }
2621 
2622 static void
2623 DoSanitizeClipboard(HWND hwnd, UxSubclassInfo *pInfo)
2624 {
2625     HGLOBAL hData;
2626     LPWSTR pszText, pszSanitized;
2627     DWORD cbData;
2628 
2629     if (GetWindowLongPtrW(hwnd, GWL_STYLE) & ES_READONLY)
2630         return;
2631     if (!OpenClipboard(hwnd))
2632         return;
2633 
2634     hData = GetClipboardData(CF_UNICODETEXT);
2635     pszText = GlobalLock(hData);
2636     if (!pszText)
2637     {
2638         CloseClipboard();
2639         return;
2640     }
2641     SHStrDupW(pszText, &pszSanitized);
2642     GlobalUnlock(hData);
2643 
2644     if (pszSanitized &&
2645         DoSanitizeText(pszSanitized, pInfo->pwszInvalidChars, pInfo->pwszValidChars))
2646     {
2647         MessageBeep(0xFFFFFFFF);
2648 
2649         /* Update clipboard text */
2650         cbData = (lstrlenW(pszSanitized) + 1) * sizeof(WCHAR);
2651         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbData);
2652         pszText = GlobalLock(hData);
2653         if (pszText)
2654         {
2655             CopyMemory(pszText, pszSanitized, cbData);
2656             GlobalUnlock(hData);
2657 
2658             SetClipboardData(CF_UNICODETEXT, hData);
2659         }
2660     }
2661 
2662     CoTaskMemFree(pszSanitized);
2663     CloseClipboard();
2664 }
2665 
2666 static LRESULT CALLBACK
2667 LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2668 {
2669     WNDPROC fnWndProc;
2670     WCHAR wch;
2671     UxSubclassInfo *pInfo = GetPropW(hwnd, L"UxSubclassInfo");
2672     if (!pInfo)
2673         return DefWindowProcW(hwnd, uMsg, wParam, lParam);
2674 
2675     fnWndProc = pInfo->fnWndProc;
2676 
2677     switch (uMsg)
2678     {
2679         case WM_KEYDOWN:
2680             if (GetKeyState(VK_SHIFT) < 0 && wParam == VK_INSERT)
2681                 DoSanitizeClipboard(hwnd, pInfo);
2682             else if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V')
2683                 DoSanitizeClipboard(hwnd, pInfo);
2684 
2685             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2686 
2687         case WM_PASTE:
2688             DoSanitizeClipboard(hwnd, pInfo);
2689             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2690 
2691         case WM_CHAR:
2692             if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V')
2693                 break;
2694 
2695             if (pInfo->pwszInvalidChars)
2696             {
2697                 if (wcschr(pInfo->pwszInvalidChars, (WCHAR)wParam) != NULL)
2698                 {
2699                     MessageBeep(0xFFFFFFFF);
2700                     break;
2701                 }
2702             }
2703             else if (pInfo->pwszValidChars)
2704             {
2705                 if (wcschr(pInfo->pwszValidChars, (WCHAR)wParam) == NULL)
2706                 {
2707                     MessageBeep(0xFFFFFFFF);
2708                     break;
2709                 }
2710             }
2711             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2712 
2713         case WM_UNICHAR:
2714             if (wParam == UNICODE_NOCHAR)
2715                 return TRUE;
2716 
2717             /* FALL THROUGH */
2718 
2719         case WM_IME_CHAR:
2720             wch = (WCHAR)wParam;
2721             if (GetKeyState(VK_CONTROL) < 0 && wch == L'V')
2722                 break;
2723 
2724             if (!IsWindowUnicode(hwnd) && HIBYTE(wch) != 0)
2725             {
2726                 CHAR data[] = {HIBYTE(wch), LOBYTE(wch)};
2727                 MultiByteToWideChar(CP_ACP, 0, data, 2, &wch, 1);
2728             }
2729 
2730             if (pInfo->pwszInvalidChars)
2731             {
2732                 if (wcschr(pInfo->pwszInvalidChars, wch) != NULL)
2733                 {
2734                     MessageBeep(0xFFFFFFFF);
2735                     break;
2736                 }
2737             }
2738             else if (pInfo->pwszValidChars)
2739             {
2740                 if (wcschr(pInfo->pwszValidChars, wch) == NULL)
2741                 {
2742                     MessageBeep(0xFFFFFFFF);
2743                     break;
2744                 }
2745             }
2746             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2747 
2748         case WM_NCDESTROY:
2749             UxSubclassInfo_Destroy(pInfo);
2750             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2751 
2752         default:
2753             return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam);
2754     }
2755 
2756     return 0;
2757 }
2758 
2759 static UxSubclassInfo *
2760 UxSubclassInfo_Create(HWND hwnd, LPWSTR valid, LPWSTR invalid)
2761 {
2762     UxSubclassInfo *pInfo;
2763     pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UxSubclassInfo));
2764     if (!pInfo)
2765     {
2766         ERR("HeapAlloc failed.\n");
2767         CoTaskMemFree(valid);
2768         CoTaskMemFree(invalid);
2769         return NULL;
2770     }
2771 
2772     pInfo->fnWndProc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)LimitEditWindowProc);
2773     if (!pInfo->fnWndProc)
2774     {
2775         ERR("SetWindowLongPtrW failed\n");
2776         CoTaskMemFree(valid);
2777         CoTaskMemFree(invalid);
2778         HeapFree(GetProcessHeap(), 0, pInfo);
2779         return NULL;
2780     }
2781 
2782     pInfo->hwnd = hwnd;
2783     pInfo->pwszValidChars = valid;
2784     pInfo->pwszInvalidChars = invalid;
2785     if (!SetPropW(hwnd, L"UxSubclassInfo", pInfo))
2786     {
2787         UxSubclassInfo_Destroy(pInfo);
2788         pInfo = NULL;
2789     }
2790     return pInfo;
2791 }
2792 
2793 HRESULT WINAPI
2794 SHLimitInputEdit(HWND hWnd, IShellFolder *psf)
2795 {
2796     IItemNameLimits *pLimits;
2797     HRESULT hr;
2798     LPWSTR pwszValidChars, pwszInvalidChars;
2799     UxSubclassInfo *pInfo;
2800 
2801     pInfo = GetPropW(hWnd, L"UxSubclassInfo");
2802     if (pInfo)
2803     {
2804         UxSubclassInfo_Destroy(pInfo);
2805         pInfo = NULL;
2806     }
2807 
2808     hr = psf->lpVtbl->QueryInterface(psf, &IID_IItemNameLimits, (LPVOID *)&pLimits);
2809     if (FAILED(hr))
2810     {
2811         ERR("hr: %x\n", hr);
2812         return hr;
2813     }
2814 
2815     pwszValidChars = pwszInvalidChars = NULL;
2816     hr = pLimits->lpVtbl->GetValidCharacters(pLimits, &pwszValidChars, &pwszInvalidChars);
2817     if (FAILED(hr))
2818     {
2819         ERR("hr: %x\n", hr);
2820         pLimits->lpVtbl->Release(pLimits);
2821         return hr;
2822     }
2823 
2824     pInfo = UxSubclassInfo_Create(hWnd, pwszValidChars, pwszInvalidChars);
2825     if (!pInfo)
2826         hr = E_FAIL;
2827 
2828     pLimits->lpVtbl->Release(pLimits);
2829 
2830     return hr;
2831 }
2832