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