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