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