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