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