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 (DWORD v, DWORD w, DWORD x, DWORD z) 1373 { FIXME("0x%08x 0x%08x 0x%08x 0x%08x stub\n",v,w,x,z); 1374 return 0; 1375 } 1376 /************************************************************************* 1377 * SHRunControlPanel [SHELL32.161] 1378 * 1379 */ 1380 BOOL WINAPI SHRunControlPanel (LPCWSTR commandLine, HWND parent) 1381 { 1382 FIXME("(%s, %p): stub\n", debugstr_w(commandLine), parent); 1383 return FALSE; 1384 } 1385 1386 static LPUNKNOWN SHELL32_IExplorerInterface=0; 1387 /************************************************************************* 1388 * SHSetInstanceExplorer [SHELL32.176] 1389 * 1390 * NOTES 1391 * Sets the interface 1392 */ 1393 VOID WINAPI SHSetInstanceExplorer (LPUNKNOWN lpUnknown) 1394 { TRACE("%p\n", lpUnknown); 1395 SHELL32_IExplorerInterface = lpUnknown; 1396 } 1397 /************************************************************************* 1398 * SHGetInstanceExplorer [SHELL32.@] 1399 * 1400 * NOTES 1401 * gets the interface pointer of the explorer and a reference 1402 */ 1403 HRESULT WINAPI SHGetInstanceExplorer (IUnknown **lpUnknown) 1404 { TRACE("%p\n", lpUnknown); 1405 1406 *lpUnknown = SHELL32_IExplorerInterface; 1407 1408 if (!SHELL32_IExplorerInterface) 1409 return E_FAIL; 1410 1411 IUnknown_AddRef(SHELL32_IExplorerInterface); 1412 return S_OK; 1413 } 1414 /************************************************************************* 1415 * SHFreeUnusedLibraries [SHELL32.123] 1416 * 1417 * Probably equivalent to CoFreeUnusedLibraries but under Windows 9x it could use 1418 * the shell32 built-in "mini-COM" without the need to load ole32.dll - see SHLoadOLE 1419 * for details 1420 * 1421 * NOTES 1422 * exported by ordinal 1423 * 1424 * SEE ALSO 1425 * CoFreeUnusedLibraries, SHLoadOLE 1426 */ 1427 void WINAPI SHFreeUnusedLibraries (void) 1428 { 1429 FIXME("stub\n"); 1430 CoFreeUnusedLibraries(); 1431 } 1432 /************************************************************************* 1433 * DAD_AutoScroll [SHELL32.129] 1434 * 1435 */ 1436 BOOL WINAPI DAD_AutoScroll(HWND hwnd, AUTO_SCROLL_DATA *samples, const POINT * pt) 1437 { 1438 FIXME("hwnd = %p %p %p\n",hwnd,samples,pt); 1439 return FALSE; 1440 } 1441 /************************************************************************* 1442 * DAD_DragEnter [SHELL32.130] 1443 * 1444 */ 1445 BOOL WINAPI DAD_DragEnter(HWND hwnd) 1446 { 1447 FIXME("hwnd = %p\n",hwnd); 1448 return FALSE; 1449 } 1450 /************************************************************************* 1451 * DAD_DragEnterEx [SHELL32.131] 1452 * 1453 */ 1454 BOOL WINAPI DAD_DragEnterEx(HWND hwnd, POINT p) 1455 { 1456 FIXME("hwnd = %p (%d,%d)\n",hwnd,p.x,p.y); 1457 return FALSE; 1458 } 1459 /************************************************************************* 1460 * DAD_DragMove [SHELL32.134] 1461 * 1462 */ 1463 BOOL WINAPI DAD_DragMove(POINT p) 1464 { 1465 FIXME("(%d,%d)\n",p.x,p.y); 1466 return FALSE; 1467 } 1468 /************************************************************************* 1469 * DAD_DragLeave [SHELL32.132] 1470 * 1471 */ 1472 BOOL WINAPI DAD_DragLeave(VOID) 1473 { 1474 FIXME("\n"); 1475 return FALSE; 1476 } 1477 /************************************************************************* 1478 * DAD_SetDragImage [SHELL32.136] 1479 * 1480 * NOTES 1481 * exported by name 1482 */ 1483 BOOL WINAPI DAD_SetDragImage( 1484 HIMAGELIST himlTrack, 1485 LPPOINT lppt) 1486 { 1487 FIXME("%p %p stub\n",himlTrack, lppt); 1488 return FALSE; 1489 } 1490 /************************************************************************* 1491 * DAD_ShowDragImage [SHELL32.137] 1492 * 1493 * NOTES 1494 * exported by name 1495 */ 1496 BOOL WINAPI DAD_ShowDragImage(BOOL bShow) 1497 { 1498 FIXME("0x%08x stub\n",bShow); 1499 return FALSE; 1500 } 1501 1502 static const WCHAR szwCabLocation[] = { 1503 'S','o','f','t','w','a','r','e','\\', 1504 'M','i','c','r','o','s','o','f','t','\\', 1505 'W','i','n','d','o','w','s','\\', 1506 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 1507 'E','x','p','l','o','r','e','r','\\', 1508 'C','a','b','i','n','e','t','S','t','a','t','e',0 1509 }; 1510 1511 static const WCHAR szwSettings[] = { 'S','e','t','t','i','n','g','s',0 }; 1512 1513 /************************************************************************* 1514 * ReadCabinetState [SHELL32.651] NT 4.0 1515 * 1516 */ 1517 BOOL WINAPI ReadCabinetState(CABINETSTATE *cs, int length) 1518 { 1519 HKEY hkey = 0; 1520 DWORD type, r; 1521 1522 TRACE("%p %d\n", cs, length); 1523 1524 if( (cs == NULL) || (length < (int)sizeof(*cs)) ) 1525 return FALSE; 1526 1527 r = RegOpenKeyW( HKEY_CURRENT_USER, szwCabLocation, &hkey ); 1528 if( r == ERROR_SUCCESS ) 1529 { 1530 type = REG_BINARY; 1531 r = RegQueryValueExW( hkey, szwSettings, 1532 NULL, &type, (LPBYTE)cs, (LPDWORD)&length ); 1533 RegCloseKey( hkey ); 1534 1535 } 1536 1537 /* if we can't read from the registry, create default values */ 1538 if ( (r != ERROR_SUCCESS) || (cs->cLength < sizeof(*cs)) || 1539 (cs->cLength != length) ) 1540 { 1541 TRACE("Initializing shell cabinet settings\n"); 1542 memset(cs, 0, sizeof(*cs)); 1543 cs->cLength = sizeof(*cs); 1544 cs->nVersion = 2; 1545 cs->fFullPathTitle = FALSE; 1546 cs->fSaveLocalView = TRUE; 1547 cs->fNotShell = FALSE; 1548 cs->fSimpleDefault = TRUE; 1549 cs->fDontShowDescBar = FALSE; 1550 cs->fNewWindowMode = FALSE; 1551 cs->fShowCompColor = FALSE; 1552 cs->fDontPrettyNames = FALSE; 1553 cs->fAdminsCreateCommonGroups = TRUE; 1554 cs->fMenuEnumFilter = 96; 1555 } 1556 1557 return TRUE; 1558 } 1559 1560 /************************************************************************* 1561 * WriteCabinetState [SHELL32.652] NT 4.0 1562 * 1563 */ 1564 BOOL WINAPI WriteCabinetState(CABINETSTATE *cs) 1565 { 1566 DWORD r; 1567 HKEY hkey = 0; 1568 1569 TRACE("%p\n",cs); 1570 1571 if( cs == NULL ) 1572 return FALSE; 1573 1574 r = RegCreateKeyExW( HKEY_CURRENT_USER, szwCabLocation, 0, 1575 NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); 1576 if( r == ERROR_SUCCESS ) 1577 { 1578 r = RegSetValueExW( hkey, szwSettings, 0, 1579 REG_BINARY, (LPBYTE) cs, cs->cLength); 1580 1581 RegCloseKey( hkey ); 1582 } 1583 1584 return (r==ERROR_SUCCESS); 1585 } 1586 1587 /************************************************************************* 1588 * FileIconInit [SHELL32.660] 1589 * 1590 */ 1591 BOOL WINAPI FileIconInit(BOOL bFullInit) 1592 { 1593 return SIC_Initialize(); 1594 } 1595 1596 /************************************************************************* 1597 * SetAppStartingCursor [SHELL32.99] 1598 */ 1599 HRESULT WINAPI SetAppStartingCursor(HWND u, DWORD v) 1600 { FIXME("hwnd=%p 0x%04x stub\n",u,v ); 1601 return 0; 1602 } 1603 1604 /************************************************************************* 1605 * SHLoadOLE [SHELL32.151] 1606 * 1607 * To reduce the memory usage of Windows 95, its shell32 contained an 1608 * internal implementation of a part of COM (see e.g. SHGetMalloc, SHCoCreateInstance, 1609 * SHRegisterDragDrop etc.) that allowed to use in-process STA objects without 1610 * the need to load OLE32.DLL. If OLE32.DLL was already loaded, the SH* function 1611 * would just call the Co* functions. 1612 * 1613 * The SHLoadOLE was called when OLE32.DLL was being loaded to transfer all the 1614 * information from the shell32 "mini-COM" to ole32.dll. 1615 * 1616 * See http://blogs.msdn.com/oldnewthing/archive/2004/07/05/173226.aspx for a 1617 * detailed description. 1618 * 1619 * Under wine ole32.dll is always loaded as it is imported by shlwapi.dll which is 1620 * imported by shell32 and no "mini-COM" is used (except for the "LoadWithoutCOM" 1621 * hack in SHCoCreateInstance) 1622 */ 1623 HRESULT WINAPI SHLoadOLE(LPARAM lParam) 1624 { FIXME("0x%08lx stub\n",lParam); 1625 return S_OK; 1626 } 1627 /************************************************************************* 1628 * DriveType [SHELL32.64] 1629 * 1630 */ 1631 int WINAPI DriveType(int DriveType) 1632 { 1633 WCHAR root[] = L"A:\\"; 1634 root[0] = L'A' + DriveType; 1635 return GetDriveTypeW(root); 1636 } 1637 /************************************************************************* 1638 * InvalidateDriveType [SHELL32.65] 1639 * Unimplemented in XP SP3 1640 */ 1641 int WINAPI InvalidateDriveType(int u) 1642 { 1643 TRACE("0x%08x stub\n",u); 1644 return 0; 1645 } 1646 /************************************************************************* 1647 * SHAbortInvokeCommand [SHELL32.198] 1648 * 1649 */ 1650 HRESULT WINAPI SHAbortInvokeCommand(void) 1651 { FIXME("stub\n"); 1652 return 1; 1653 } 1654 /************************************************************************* 1655 * SHOutOfMemoryMessageBox [SHELL32.126] 1656 * 1657 */ 1658 int WINAPI SHOutOfMemoryMessageBox( 1659 HWND hwndOwner, 1660 LPCSTR lpCaption, 1661 UINT uType) 1662 { 1663 FIXME("%p %s 0x%08x stub\n",hwndOwner, lpCaption, uType); 1664 return 0; 1665 } 1666 /************************************************************************* 1667 * SHFlushClipboard [SHELL32.121] 1668 * 1669 */ 1670 HRESULT WINAPI SHFlushClipboard(void) 1671 { 1672 return OleFlushClipboard(); 1673 } 1674 1675 /************************************************************************* 1676 * SHWaitForFileToOpen [SHELL32.97] 1677 * 1678 */ 1679 BOOL WINAPI SHWaitForFileToOpen( 1680 LPCITEMIDLIST pidl, 1681 DWORD dwFlags, 1682 DWORD dwTimeout) 1683 { 1684 FIXME("%p 0x%08x 0x%08x stub\n", pidl, dwFlags, dwTimeout); 1685 return FALSE; 1686 } 1687 1688 /************************************************************************ 1689 * RLBuildListOfPaths [SHELL32.146] 1690 * 1691 * NOTES 1692 * builds a DPA 1693 */ 1694 DWORD WINAPI RLBuildListOfPaths (void) 1695 { FIXME("stub\n"); 1696 return 0; 1697 } 1698 /************************************************************************ 1699 * SHValidateUNC [SHELL32.173] 1700 * 1701 */ 1702 BOOL WINAPI SHValidateUNC (HWND hwndOwner, PWSTR pszFile, UINT fConnect) 1703 { 1704 FIXME("(%p, %s, 0x%08x): stub\n", hwndOwner, debugstr_w(pszFile), fConnect); 1705 return FALSE; 1706 } 1707 1708 /************************************************************************ 1709 * DoEnvironmentSubstA [SHELL32.@] 1710 * 1711 * See DoEnvironmentSubstW. 1712 */ 1713 DWORD WINAPI DoEnvironmentSubstA(LPSTR pszString, UINT cchString) 1714 { 1715 LPSTR dst; 1716 BOOL res = FALSE; 1717 DWORD len = cchString; 1718 1719 TRACE("(%s, %d)\n", debugstr_a(pszString), cchString); 1720 if (pszString == NULL) /* Really return 0? */ 1721 return 0; 1722 if ((dst = (LPSTR)HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(CHAR)))) 1723 { 1724 len = ExpandEnvironmentStringsA(pszString, dst, cchString); 1725 /* len includes the terminating 0 */ 1726 if (len && len < cchString) 1727 { 1728 res = TRUE; 1729 memcpy(pszString, dst, len); 1730 } 1731 else 1732 len = cchString; 1733 1734 HeapFree(GetProcessHeap(), 0, dst); 1735 } 1736 return MAKELONG(len, res); 1737 } 1738 1739 /************************************************************************ 1740 * DoEnvironmentSubstW [SHELL32.@] 1741 * 1742 * Replace all %KEYWORD% in the string with the value of the named 1743 * environment variable. If the buffer is too small, the string is not modified. 1744 * 1745 * PARAMS 1746 * pszString [I] '\0' terminated string with %keyword%. 1747 * [O] '\0' terminated string with %keyword% substituted. 1748 * cchString [I] size of str. 1749 * 1750 * RETURNS 1751 * Success: The string in the buffer is updated 1752 * HIWORD: TRUE 1753 * LOWORD: characters used in the buffer, including space for the terminating 0 1754 * Failure: buffer too small. The string is not modified. 1755 * HIWORD: FALSE 1756 * LOWORD: provided size of the buffer in characters 1757 */ 1758 DWORD WINAPI DoEnvironmentSubstW(LPWSTR pszString, UINT cchString) 1759 { 1760 LPWSTR dst; 1761 BOOL res = FALSE; 1762 DWORD len = cchString; 1763 1764 TRACE("(%s, %d)\n", debugstr_w(pszString), cchString); 1765 1766 if ((cchString < MAXLONG) && (dst = HeapAlloc(GetProcessHeap(), 0, cchString * sizeof(WCHAR)))) 1767 { 1768 len = ExpandEnvironmentStringsW(pszString, dst, cchString); 1769 /* len includes the terminating 0 */ 1770 if (len && len <= cchString) 1771 { 1772 res = TRUE; 1773 memcpy(pszString, dst, len * sizeof(WCHAR)); 1774 } 1775 else 1776 len = cchString; 1777 1778 HeapFree(GetProcessHeap(), 0, dst); 1779 } 1780 return MAKELONG(len, res); 1781 } 1782 1783 /************************************************************************ 1784 * DoEnvironmentSubst [SHELL32.53] 1785 * 1786 * See DoEnvironmentSubstA. 1787 */ 1788 DWORD WINAPI DoEnvironmentSubstAW(LPVOID x, UINT y) 1789 { 1790 if (SHELL_OsIsUnicode()) 1791 return DoEnvironmentSubstW(x, y); 1792 return DoEnvironmentSubstA(x, y); 1793 } 1794 1795 /************************************************************************* 1796 * GUIDFromStringA [SHELL32.703] 1797 */ 1798 BOOL WINAPI GUIDFromStringA(LPCSTR str, LPGUID guid) 1799 { 1800 TRACE("GUIDFromStringA() stub\n"); 1801 return FALSE; 1802 } 1803 1804 /************************************************************************* 1805 * GUIDFromStringW [SHELL32.704] 1806 */ 1807 BOOL WINAPI GUIDFromStringW(LPCWSTR str, LPGUID guid) 1808 { 1809 UNICODE_STRING guid_str; 1810 1811 RtlInitUnicodeString(&guid_str, str); 1812 return !RtlGUIDFromString(&guid_str, guid); 1813 } 1814 1815 /************************************************************************* 1816 * PathIsTemporaryA [SHELL32.713] 1817 */ 1818 BOOL WINAPI PathIsTemporaryA(LPSTR Str) 1819 { 1820 FIXME("(%s)stub\n", debugstr_a(Str)); 1821 return FALSE; 1822 } 1823 1824 /************************************************************************* 1825 * PathIsTemporaryW [SHELL32.714] 1826 */ 1827 BOOL WINAPI PathIsTemporaryW(LPWSTR Str) 1828 { 1829 FIXME("(%s)stub\n", debugstr_w(Str)); 1830 return FALSE; 1831 } 1832 1833 typedef struct _PSXA 1834 { 1835 UINT uiCount; 1836 UINT uiAllocated; 1837 IShellPropSheetExt *pspsx[1]; 1838 } PSXA, *PPSXA; 1839 1840 typedef struct _PSXA_CALL 1841 { 1842 LPFNADDPROPSHEETPAGE lpfnAddReplaceWith; 1843 LPARAM lParam; 1844 BOOL bCalled; 1845 BOOL bMultiple; 1846 UINT uiCount; 1847 } PSXA_CALL, *PPSXA_CALL; 1848 1849 static BOOL CALLBACK PsxaCall(HPROPSHEETPAGE hpage, LPARAM lParam) 1850 { 1851 PPSXA_CALL Call = (PPSXA_CALL)lParam; 1852 1853 if (Call != NULL) 1854 { 1855 if ((Call->bMultiple || !Call->bCalled) && 1856 Call->lpfnAddReplaceWith(hpage, Call->lParam)) 1857 { 1858 Call->bCalled = TRUE; 1859 Call->uiCount++; 1860 return TRUE; 1861 } 1862 } 1863 1864 return FALSE; 1865 } 1866 1867 /************************************************************************* 1868 * SHAddFromPropSheetExtArray [SHELL32.167] 1869 */ 1870 UINT WINAPI SHAddFromPropSheetExtArray(HPSXA hpsxa, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) 1871 { 1872 PSXA_CALL Call; 1873 UINT i; 1874 PPSXA psxa = (PPSXA)hpsxa; 1875 1876 TRACE("(%p,%p,%08lx)\n", hpsxa, lpfnAddPage, lParam); 1877 1878 if (psxa) 1879 { 1880 ZeroMemory(&Call, sizeof(Call)); 1881 Call.lpfnAddReplaceWith = lpfnAddPage; 1882 Call.lParam = lParam; 1883 Call.bMultiple = TRUE; 1884 1885 /* Call the AddPage method of all registered IShellPropSheetExt interfaces */ 1886 for (i = 0; i != psxa->uiCount; i++) 1887 { 1888 psxa->pspsx[i]->lpVtbl->AddPages(psxa->pspsx[i], PsxaCall, (LPARAM)&Call); 1889 } 1890 1891 return Call.uiCount; 1892 } 1893 1894 return 0; 1895 } 1896 1897 /************************************************************************* 1898 * SHCreatePropSheetExtArray [SHELL32.168] 1899 */ 1900 HPSXA WINAPI SHCreatePropSheetExtArray(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface) 1901 { 1902 return SHCreatePropSheetExtArrayEx(hKey, pszSubKey, max_iface, NULL); 1903 } 1904 1905 /************************************************************************* 1906 * SHCreatePropSheetExtArrayEx [SHELL32.194] 1907 */ 1908 HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, LPDATAOBJECT pDataObj) 1909 { 1910 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}; 1911 WCHAR szHandler[64]; 1912 DWORD dwHandlerLen; 1913 WCHAR szClsidHandler[39]; 1914 DWORD dwClsidSize; 1915 CLSID clsid; 1916 LONG lRet; 1917 DWORD dwIndex; 1918 IShellExtInit *psxi; 1919 IShellPropSheetExt *pspsx; 1920 HKEY hkBase, hkPropSheetHandlers; 1921 PPSXA psxa = NULL; 1922 1923 TRACE("(%p,%s,%u)\n", hKey, debugstr_w(pszSubKey), max_iface); 1924 1925 if (max_iface == 0) 1926 return NULL; 1927 1928 /* Open the registry key */ 1929 lRet = RegOpenKeyW(hKey, pszSubKey, &hkBase); 1930 if (lRet != ERROR_SUCCESS) 1931 return NULL; 1932 1933 lRet = RegOpenKeyExW(hkBase, szPropSheetSubKey, 0, KEY_ENUMERATE_SUB_KEYS, &hkPropSheetHandlers); 1934 RegCloseKey(hkBase); 1935 if (lRet == ERROR_SUCCESS) 1936 { 1937 /* Create and initialize the Property Sheet Extensions Array */ 1938 psxa = LocalAlloc(LMEM_FIXED, FIELD_OFFSET(PSXA, pspsx[max_iface])); 1939 if (psxa) 1940 { 1941 ZeroMemory(psxa, FIELD_OFFSET(PSXA, pspsx[max_iface])); 1942 psxa->uiAllocated = max_iface; 1943 1944 /* Enumerate all subkeys and attempt to load the shell extensions */ 1945 dwIndex = 0; 1946 do 1947 { 1948 dwHandlerLen = sizeof(szHandler) / sizeof(szHandler[0]); 1949 lRet = RegEnumKeyExW(hkPropSheetHandlers, dwIndex++, szHandler, &dwHandlerLen, NULL, NULL, NULL, NULL); 1950 if (lRet != ERROR_SUCCESS) 1951 { 1952 if (lRet == ERROR_MORE_DATA) 1953 continue; 1954 1955 if (lRet == ERROR_NO_MORE_ITEMS) 1956 lRet = ERROR_SUCCESS; 1957 break; 1958 } 1959 1960 /* The CLSID is stored either in the key itself or in its default value. */ 1961 if (FAILED(lRet = SHCLSIDFromStringW(szHandler, &clsid))) 1962 { 1963 dwClsidSize = sizeof(szClsidHandler); 1964 if (SHGetValueW(hkPropSheetHandlers, szHandler, NULL, NULL, szClsidHandler, &dwClsidSize) == ERROR_SUCCESS) 1965 { 1966 /* Force a NULL-termination and convert the string */ 1967 szClsidHandler[(sizeof(szClsidHandler) / sizeof(szClsidHandler[0])) - 1] = 0; 1968 lRet = SHCLSIDFromStringW(szClsidHandler, &clsid); 1969 } 1970 } 1971 1972 if (SUCCEEDED(lRet)) 1973 { 1974 /* Attempt to get an IShellPropSheetExt and an IShellExtInit instance. 1975 Only if both interfaces are supported it's a real shell extension. 1976 Then call IShellExtInit's Initialize method. */ 1977 if (SUCCEEDED(CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER/* | CLSCTX_NO_CODE_DOWNLOAD */, &IID_IShellPropSheetExt, (LPVOID *)&pspsx))) 1978 { 1979 if (SUCCEEDED(pspsx->lpVtbl->QueryInterface(pspsx, &IID_IShellExtInit, (PVOID *)&psxi))) 1980 { 1981 if (SUCCEEDED(psxi->lpVtbl->Initialize(psxi, NULL, pDataObj, hKey))) 1982 { 1983 /* Add the IShellPropSheetExt instance to the array */ 1984 psxa->pspsx[psxa->uiCount++] = pspsx; 1985 } 1986 else 1987 { 1988 psxi->lpVtbl->Release(psxi); 1989 pspsx->lpVtbl->Release(pspsx); 1990 } 1991 } 1992 else 1993 pspsx->lpVtbl->Release(pspsx); 1994 } 1995 } 1996 1997 } while (psxa->uiCount != psxa->uiAllocated); 1998 } 1999 else 2000 lRet = ERROR_NOT_ENOUGH_MEMORY; 2001 2002 RegCloseKey(hkPropSheetHandlers); 2003 } 2004 2005 if (lRet != ERROR_SUCCESS && psxa) 2006 { 2007 SHDestroyPropSheetExtArray((HPSXA)psxa); 2008 psxa = NULL; 2009 } 2010 2011 return (HPSXA)psxa; 2012 } 2013 2014 /************************************************************************* 2015 * SHReplaceFromPropSheetExtArray [SHELL32.170] 2016 */ 2017 UINT WINAPI SHReplaceFromPropSheetExtArray(HPSXA hpsxa, UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam) 2018 { 2019 PSXA_CALL Call; 2020 UINT i; 2021 PPSXA psxa = (PPSXA)hpsxa; 2022 2023 TRACE("(%p,%u,%p,%08lx)\n", hpsxa, uPageID, lpfnReplaceWith, lParam); 2024 2025 if (psxa) 2026 { 2027 ZeroMemory(&Call, sizeof(Call)); 2028 Call.lpfnAddReplaceWith = lpfnReplaceWith; 2029 Call.lParam = lParam; 2030 2031 /* Call the ReplacePage method of all registered IShellPropSheetExt interfaces. 2032 Each shell extension is only allowed to call the callback once during the callback. */ 2033 for (i = 0; i != psxa->uiCount; i++) 2034 { 2035 Call.bCalled = FALSE; 2036 psxa->pspsx[i]->lpVtbl->ReplacePage(psxa->pspsx[i], uPageID, PsxaCall, (LPARAM)&Call); 2037 } 2038 2039 return Call.uiCount; 2040 } 2041 2042 return 0; 2043 } 2044 2045 /************************************************************************* 2046 * SHDestroyPropSheetExtArray [SHELL32.169] 2047 */ 2048 void WINAPI SHDestroyPropSheetExtArray(HPSXA hpsxa) 2049 { 2050 UINT i; 2051 PPSXA psxa = (PPSXA)hpsxa; 2052 2053 TRACE("(%p)\n", hpsxa); 2054 2055 if (psxa) 2056 { 2057 for (i = 0; i != psxa->uiCount; i++) 2058 { 2059 psxa->pspsx[i]->lpVtbl->Release(psxa->pspsx[i]); 2060 } 2061 2062 LocalFree(psxa); 2063 } 2064 } 2065 2066 /************************************************************************* 2067 * CIDLData_CreateFromIDArray [SHELL32.83] 2068 * 2069 * Create IDataObject from PIDLs?? 2070 */ 2071 HRESULT WINAPI CIDLData_CreateFromIDArray( 2072 PCIDLIST_ABSOLUTE pidlFolder, 2073 UINT cpidlFiles, 2074 PCUIDLIST_RELATIVE_ARRAY lppidlFiles, 2075 LPDATAOBJECT *ppdataObject) 2076 { 2077 UINT i; 2078 HWND hwnd = 0; /*FIXME: who should be hwnd of owner? set to desktop */ 2079 HRESULT hResult; 2080 2081 TRACE("(%p, %d, %p, %p)\n", pidlFolder, cpidlFiles, lppidlFiles, ppdataObject); 2082 if (TRACE_ON(pidl)) 2083 { 2084 pdump (pidlFolder); 2085 for (i=0; i<cpidlFiles; i++) pdump (lppidlFiles[i]); 2086 } 2087 hResult = IDataObject_Constructor(hwnd, pidlFolder, lppidlFiles, cpidlFiles, FALSE, ppdataObject); 2088 return hResult; 2089 } 2090 2091 /************************************************************************* 2092 * SHCreateStdEnumFmtEtc [SHELL32.74] 2093 * 2094 * NOTES 2095 * 2096 */ 2097 HRESULT WINAPI SHCreateStdEnumFmtEtc( 2098 UINT cFormats, 2099 const FORMATETC *lpFormats, 2100 LPENUMFORMATETC *ppenumFormatetc) 2101 { 2102 IEnumFORMATETC *pef; 2103 HRESULT hRes; 2104 TRACE("cf=%d fe=%p pef=%p\n", cFormats, lpFormats, ppenumFormatetc); 2105 2106 hRes = IEnumFORMATETC_Constructor(cFormats, lpFormats, &pef); 2107 if (FAILED(hRes)) 2108 return hRes; 2109 2110 IEnumFORMATETC_AddRef(pef); 2111 hRes = IEnumFORMATETC_QueryInterface(pef, &IID_IEnumFORMATETC, (LPVOID*)ppenumFormatetc); 2112 IEnumFORMATETC_Release(pef); 2113 2114 return hRes; 2115 } 2116 2117 /************************************************************************* 2118 * SHFindFiles (SHELL32.90) 2119 */ 2120 BOOL WINAPI SHFindFiles( PCIDLIST_ABSOLUTE pidlFolder, PCIDLIST_ABSOLUTE pidlSaveFile ) 2121 { 2122 FIXME("params ignored: %p %p\n", pidlFolder, pidlSaveFile); 2123 if (SHRestricted(REST_NOFIND)) 2124 { 2125 return FALSE; 2126 } 2127 /* Open the search results folder */ 2128 /* FIXME: CSearchBar should be opened as well */ 2129 return ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{E17D4FC0-5564-11D1-83F2-00A0C90DC849}", NULL, SW_SHOWNORMAL) > (HINSTANCE)32; 2130 } 2131 2132 /************************************************************************* 2133 * SHUpdateImageW (SHELL32.192) 2134 * 2135 * Notifies the shell that an icon in the system image list has been changed. 2136 * 2137 * PARAMS 2138 * pszHashItem [I] Path to file that contains the icon. 2139 * iIndex [I] Zero-based index of the icon in the file. 2140 * uFlags [I] Flags determining the icon attributes. See notes. 2141 * iImageIndex [I] Index of the icon in the system image list. 2142 * 2143 * RETURNS 2144 * Nothing 2145 * 2146 * NOTES 2147 * uFlags can be one or more of the following flags: 2148 * GIL_NOTFILENAME - pszHashItem is not a file name. 2149 * GIL_SIMULATEDOC - Create a document icon using the specified icon. 2150 */ 2151 void WINAPI SHUpdateImageW(LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex) 2152 { 2153 FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_w(pszHashItem), iIndex, uFlags, iImageIndex); 2154 } 2155 2156 /************************************************************************* 2157 * SHUpdateImageA (SHELL32.191) 2158 * 2159 * See SHUpdateImageW. 2160 */ 2161 VOID WINAPI SHUpdateImageA(LPCSTR pszHashItem, INT iIndex, UINT uFlags, INT iImageIndex) 2162 { 2163 FIXME("%s, %d, 0x%x, %d - stub\n", debugstr_a(pszHashItem), iIndex, uFlags, iImageIndex); 2164 } 2165 2166 INT WINAPI SHHandleUpdateImage(PCIDLIST_ABSOLUTE pidlExtra) 2167 { 2168 FIXME("%p - stub\n", pidlExtra); 2169 2170 return -1; 2171 } 2172 2173 BOOL WINAPI SHObjectProperties(HWND hwnd, DWORD dwType, LPCWSTR szObject, LPCWSTR szPage) 2174 { 2175 FIXME("%p, 0x%08x, %s, %s - stub\n", hwnd, dwType, debugstr_w(szObject), debugstr_w(szPage)); 2176 2177 return TRUE; 2178 } 2179 2180 BOOL WINAPI SHGetNewLinkInfoA(LPCSTR pszLinkTo, LPCSTR pszDir, LPSTR pszName, BOOL *pfMustCopy, 2181 UINT uFlags) 2182 { 2183 WCHAR wszLinkTo[MAX_PATH]; 2184 WCHAR wszDir[MAX_PATH]; 2185 WCHAR wszName[MAX_PATH]; 2186 BOOL res; 2187 2188 MultiByteToWideChar(CP_ACP, 0, pszLinkTo, -1, wszLinkTo, MAX_PATH); 2189 MultiByteToWideChar(CP_ACP, 0, pszDir, -1, wszDir, MAX_PATH); 2190 2191 res = SHGetNewLinkInfoW(wszLinkTo, wszDir, wszName, pfMustCopy, uFlags); 2192 2193 if (res) 2194 WideCharToMultiByte(CP_ACP, 0, wszName, -1, pszName, MAX_PATH, NULL, NULL); 2195 2196 return res; 2197 } 2198 2199 BOOL WINAPI SHGetNewLinkInfoW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL *pfMustCopy, 2200 UINT uFlags) 2201 { 2202 const WCHAR *basename; 2203 WCHAR *dst_basename; 2204 int i=2; 2205 static const WCHAR lnkformat[] = {'%','s','.','l','n','k',0}; 2206 static const WCHAR lnkformatnum[] = {'%','s',' ','(','%','d',')','.','l','n','k',0}; 2207 2208 TRACE("(%s, %s, %p, %p, 0x%08x)\n", debugstr_w(pszLinkTo), debugstr_w(pszDir), 2209 pszName, pfMustCopy, uFlags); 2210 2211 *pfMustCopy = FALSE; 2212 2213 if (uFlags & SHGNLI_PIDL) 2214 { 2215 FIXME("SHGNLI_PIDL flag unsupported\n"); 2216 return FALSE; 2217 } 2218 2219 if (uFlags) 2220 FIXME("ignoring flags: 0x%08x\n", uFlags); 2221 2222 /* FIXME: should test if the file is a shortcut or DOS program */ 2223 if (GetFileAttributesW(pszLinkTo) == INVALID_FILE_ATTRIBUTES) 2224 return FALSE; 2225 2226 basename = strrchrW(pszLinkTo, '\\'); 2227 if (basename) 2228 basename = basename+1; 2229 else 2230 basename = pszLinkTo; 2231 2232 lstrcpynW(pszName, pszDir, MAX_PATH); 2233 if (!PathAddBackslashW(pszName)) 2234 return FALSE; 2235 2236 dst_basename = pszName + strlenW(pszName); 2237 2238 snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, lnkformat, basename); 2239 2240 while (GetFileAttributesW(pszName) != INVALID_FILE_ATTRIBUTES) 2241 { 2242 snprintfW(dst_basename, pszName + MAX_PATH - dst_basename, lnkformatnum, basename, i); 2243 i++; 2244 } 2245 2246 return TRUE; 2247 } 2248 2249 HRESULT WINAPI SHStartNetConnectionDialog(HWND hwnd, LPCSTR pszRemoteName, DWORD dwType) 2250 { 2251 FIXME("%p, %s, 0x%08x - stub\n", hwnd, debugstr_a(pszRemoteName), dwType); 2252 2253 return S_OK; 2254 } 2255 /************************************************************************* 2256 * SHSetLocalizedName (SHELL32.@) 2257 */ 2258 HRESULT WINAPI SHSetLocalizedName(LPCWSTR pszPath, LPCWSTR pszResModule, int idsRes) 2259 { 2260 FIXME("%p, %s, %d - stub\n", pszPath, debugstr_w(pszResModule), idsRes); 2261 2262 return S_OK; 2263 } 2264 2265 /************************************************************************* 2266 * LinkWindow_RegisterClass (SHELL32.258) 2267 */ 2268 BOOL WINAPI LinkWindow_RegisterClass(void) 2269 { 2270 FIXME("()\n"); 2271 return TRUE; 2272 } 2273 2274 /************************************************************************* 2275 * LinkWindow_UnregisterClass (SHELL32.259) 2276 */ 2277 BOOL WINAPI LinkWindow_UnregisterClass(DWORD dwUnused) 2278 { 2279 FIXME("()\n"); 2280 return TRUE; 2281 } 2282 2283 /************************************************************************* 2284 * SHFlushSFCache (SHELL32.526) 2285 * 2286 * Notifies the shell that a user-specified special folder location has changed. 2287 * 2288 * NOTES 2289 * In Wine, the shell folder registry values are not cached, so this function 2290 * has no effect. 2291 */ 2292 void WINAPI SHFlushSFCache(void) 2293 { 2294 } 2295 2296 /************************************************************************* 2297 * SHGetImageList (SHELL32.727) 2298 * 2299 * Returns a copy of a shell image list. 2300 * 2301 * NOTES 2302 * Windows XP features 4 sizes of image list, and Vista 5. Wine currently 2303 * only supports the traditional small and large image lists, so requests 2304 * for the others will currently fail. 2305 */ 2306 HRESULT WINAPI SHGetImageList(int iImageList, REFIID riid, void **ppv) 2307 { 2308 HIMAGELIST hLarge, hSmall; 2309 HIMAGELIST hNew; 2310 HRESULT ret = E_FAIL; 2311 2312 /* Wine currently only maintains large and small image lists */ 2313 if ((iImageList != SHIL_LARGE) && (iImageList != SHIL_SMALL) && (iImageList != SHIL_SYSSMALL)) 2314 { 2315 FIXME("Unsupported image list %i requested\n", iImageList); 2316 return E_FAIL; 2317 } 2318 2319 Shell_GetImageLists(&hLarge, &hSmall); 2320 #ifndef __REACTOS__ 2321 hNew = ImageList_Duplicate(iImageList == SHIL_LARGE ? hLarge : hSmall); 2322 2323 /* Get the interface for the new image list */ 2324 if (hNew) 2325 { 2326 ret = HIMAGELIST_QueryInterface(hNew, riid, ppv); 2327 ImageList_Destroy(hNew); 2328 } 2329 #else 2330 /* Duplicating the imagelist causes the start menu items not to draw on 2331 * the first show. Was the Duplicate necessary for some reason? I believe 2332 * Windows returns the raw pointer here. */ 2333 hNew = (iImageList == SHIL_LARGE ? hLarge : hSmall); 2334 ret = IImageList2_QueryInterface((IImageList2 *) hNew, riid, ppv); 2335 #endif 2336 2337 return ret; 2338 } 2339 2340 #ifndef __REACTOS__ 2341 2342 /************************************************************************* 2343 * SHCreateShellFolderView [SHELL32.256] 2344 * 2345 * Create a new instance of the default Shell folder view object. 2346 * 2347 * RETURNS 2348 * Success: S_OK 2349 * Failure: error value 2350 * 2351 * NOTES 2352 * see IShellFolder::CreateViewObject 2353 */ 2354 HRESULT WINAPI SHCreateShellFolderView(const SFV_CREATE *pcsfv, 2355 IShellView **ppsv) 2356 { 2357 IShellView * psf; 2358 HRESULT hRes; 2359 2360 *ppsv = NULL; 2361 if (!pcsfv || pcsfv->cbSize != sizeof(*pcsfv)) 2362 return E_INVALIDARG; 2363 2364 TRACE("sf=%p outer=%p callback=%p\n", 2365 pcsfv->pshf, pcsfv->psvOuter, pcsfv->psfvcb); 2366 2367 hRes = IShellView_Constructor(pcsfv->pshf, &psf); 2368 if (FAILED(hRes)) 2369 return hRes; 2370 2371 hRes = IShellView_QueryInterface(psf, &IID_IShellView, (LPVOID *)ppsv); 2372 IShellView_Release(psf); 2373 2374 return hRes; 2375 } 2376 #endif 2377 2378 2379 /************************************************************************* 2380 * SHTestTokenMembership [SHELL32.245] 2381 * 2382 * Checks whether a given token is a mamber of a local group with the 2383 * specified RID. 2384 * 2385 */ 2386 EXTERN_C BOOL 2387 WINAPI 2388 SHTestTokenMembership(HANDLE TokenHandle, ULONG ulRID) 2389 { 2390 SID_IDENTIFIER_AUTHORITY ntAuth = {SECURITY_NT_AUTHORITY}; 2391 DWORD nSubAuthority0, nSubAuthority1; 2392 DWORD nSubAuthorityCount; 2393 PSID SidToCheck; 2394 BOOL IsMember = FALSE; 2395 2396 if ((ulRID == SECURITY_SERVICE_RID) || ulRID == SECURITY_LOCAL_SYSTEM_RID) 2397 { 2398 nSubAuthority0 = ulRID; 2399 nSubAuthority1 = 0; 2400 nSubAuthorityCount= 1; 2401 } 2402 else 2403 { 2404 nSubAuthority0 = SECURITY_BUILTIN_DOMAIN_RID; 2405 nSubAuthority1 = ulRID; 2406 nSubAuthorityCount= 2; 2407 } 2408 2409 if (!AllocateAndInitializeSid(&ntAuth, 2410 nSubAuthorityCount, 2411 nSubAuthority0, 2412 nSubAuthority1, 2413 0, 0, 0, 0, 0, 0, 2414 &SidToCheck)) 2415 { 2416 return FALSE; 2417 } 2418 2419 if (!CheckTokenMembership(TokenHandle, SidToCheck, &IsMember)) 2420 { 2421 IsMember = FALSE; 2422 } 2423 2424 FreeSid(SidToCheck); 2425 return IsMember; 2426 } 2427 2428 /************************************************************************* 2429 * IsUserAnAdmin [SHELL32.680] NT 4.0 2430 * 2431 * Checks whether the current user is a member of the Administrators group. 2432 * 2433 * PARAMS 2434 * None 2435 * 2436 * RETURNS 2437 * Success: TRUE 2438 * Failure: FALSE 2439 */ 2440 BOOL WINAPI IsUserAnAdmin(VOID) 2441 { 2442 return SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS); 2443 } 2444 2445 /************************************************************************* 2446 * SHLimitInputEdit(SHELL32.@) 2447 */ 2448 2449 /* TODO: Show baloon popup window with TTS_BALLOON */ 2450 2451 typedef struct UxSubclassInfo 2452 { 2453 HWND hwnd; 2454 WNDPROC fnWndProc; 2455 LPWSTR pwszValidChars; 2456 LPWSTR pwszInvalidChars; 2457 } UxSubclassInfo; 2458 2459 static void 2460 UxSubclassInfo_Destroy(UxSubclassInfo *pInfo) 2461 { 2462 if (!pInfo) 2463 return; 2464 2465 RemovePropW(pInfo->hwnd, L"UxSubclassInfo"); 2466 2467 CoTaskMemFree(pInfo->pwszValidChars); 2468 CoTaskMemFree(pInfo->pwszInvalidChars); 2469 2470 SetWindowLongPtrW(pInfo->hwnd, GWLP_WNDPROC, (LONG_PTR)pInfo->fnWndProc); 2471 2472 HeapFree(GetProcessHeap(), 0, pInfo); 2473 } 2474 2475 static BOOL 2476 DoSanitizeText(LPWSTR pszSanitized, LPCWSTR pszInvalidChars, LPCWSTR pszValidChars) 2477 { 2478 LPWSTR pch1, pch2; 2479 BOOL bFound = FALSE; 2480 2481 for (pch1 = pch2 = pszSanitized; *pch1; ++pch1) 2482 { 2483 if (pszInvalidChars) 2484 { 2485 if (wcschr(pszInvalidChars, *pch1) != NULL) 2486 { 2487 bFound = TRUE; 2488 continue; 2489 } 2490 } 2491 else if (pszValidChars) 2492 { 2493 if (wcschr(pszValidChars, *pch1) == NULL) 2494 { 2495 bFound = TRUE; 2496 continue; 2497 } 2498 } 2499 2500 *pch2 = *pch1; 2501 ++pch2; 2502 } 2503 *pch2 = 0; 2504 2505 return bFound; 2506 } 2507 2508 static void 2509 DoSanitizeClipboard(HWND hwnd, UxSubclassInfo *pInfo) 2510 { 2511 HGLOBAL hData; 2512 LPWSTR pszText, pszSanitized; 2513 DWORD cbData; 2514 2515 if (GetWindowLongPtrW(hwnd, GWL_STYLE) & ES_READONLY) 2516 return; 2517 if (!OpenClipboard(hwnd)) 2518 return; 2519 2520 hData = GetClipboardData(CF_UNICODETEXT); 2521 pszText = GlobalLock(hData); 2522 if (!pszText) 2523 { 2524 CloseClipboard(); 2525 return; 2526 } 2527 SHStrDupW(pszText, &pszSanitized); 2528 GlobalUnlock(hData); 2529 2530 if (pszSanitized && 2531 DoSanitizeText(pszSanitized, pInfo->pwszInvalidChars, pInfo->pwszValidChars)) 2532 { 2533 MessageBeep(0xFFFFFFFF); 2534 2535 /* Update clipboard text */ 2536 cbData = (lstrlenW(pszSanitized) + 1) * sizeof(WCHAR); 2537 hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbData); 2538 pszText = GlobalLock(hData); 2539 if (pszText) 2540 { 2541 CopyMemory(pszText, pszSanitized, cbData); 2542 GlobalUnlock(hData); 2543 2544 SetClipboardData(CF_UNICODETEXT, hData); 2545 } 2546 } 2547 2548 CoTaskMemFree(pszSanitized); 2549 CloseClipboard(); 2550 } 2551 2552 static LRESULT CALLBACK 2553 LimitEditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 2554 { 2555 WNDPROC fnWndProc; 2556 WCHAR wch; 2557 UxSubclassInfo *pInfo = GetPropW(hwnd, L"UxSubclassInfo"); 2558 if (!pInfo) 2559 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 2560 2561 fnWndProc = pInfo->fnWndProc; 2562 2563 switch (uMsg) 2564 { 2565 case WM_KEYDOWN: 2566 if (GetKeyState(VK_SHIFT) < 0 && wParam == VK_INSERT) 2567 DoSanitizeClipboard(hwnd, pInfo); 2568 else if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V') 2569 DoSanitizeClipboard(hwnd, pInfo); 2570 2571 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2572 2573 case WM_PASTE: 2574 DoSanitizeClipboard(hwnd, pInfo); 2575 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2576 2577 case WM_CHAR: 2578 if (GetKeyState(VK_CONTROL) < 0 && wParam == L'V') 2579 break; 2580 2581 if (pInfo->pwszInvalidChars) 2582 { 2583 if (wcschr(pInfo->pwszInvalidChars, (WCHAR)wParam) != NULL) 2584 { 2585 MessageBeep(0xFFFFFFFF); 2586 break; 2587 } 2588 } 2589 else if (pInfo->pwszValidChars) 2590 { 2591 if (wcschr(pInfo->pwszValidChars, (WCHAR)wParam) == NULL) 2592 { 2593 MessageBeep(0xFFFFFFFF); 2594 break; 2595 } 2596 } 2597 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2598 2599 case WM_UNICHAR: 2600 if (wParam == UNICODE_NOCHAR) 2601 return TRUE; 2602 2603 /* FALL THROUGH */ 2604 2605 case WM_IME_CHAR: 2606 wch = (WCHAR)wParam; 2607 if (GetKeyState(VK_CONTROL) < 0 && wch == L'V') 2608 break; 2609 2610 if (!IsWindowUnicode(hwnd) && HIBYTE(wch) != 0) 2611 { 2612 CHAR data[] = {HIBYTE(wch), LOBYTE(wch)}; 2613 MultiByteToWideChar(CP_ACP, 0, data, 2, &wch, 1); 2614 } 2615 2616 if (pInfo->pwszInvalidChars) 2617 { 2618 if (wcschr(pInfo->pwszInvalidChars, wch) != NULL) 2619 { 2620 MessageBeep(0xFFFFFFFF); 2621 break; 2622 } 2623 } 2624 else if (pInfo->pwszValidChars) 2625 { 2626 if (wcschr(pInfo->pwszValidChars, wch) == NULL) 2627 { 2628 MessageBeep(0xFFFFFFFF); 2629 break; 2630 } 2631 } 2632 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2633 2634 case WM_NCDESTROY: 2635 UxSubclassInfo_Destroy(pInfo); 2636 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2637 2638 default: 2639 return CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam); 2640 } 2641 2642 return 0; 2643 } 2644 2645 static UxSubclassInfo * 2646 UxSubclassInfo_Create(HWND hwnd, LPWSTR valid, LPWSTR invalid) 2647 { 2648 UxSubclassInfo *pInfo; 2649 pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UxSubclassInfo)); 2650 if (!pInfo) 2651 { 2652 ERR("HeapAlloc failed.\n"); 2653 CoTaskMemFree(valid); 2654 CoTaskMemFree(invalid); 2655 return NULL; 2656 } 2657 2658 pInfo->fnWndProc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)LimitEditWindowProc); 2659 if (!pInfo->fnWndProc) 2660 { 2661 ERR("SetWindowLongPtrW failed\n"); 2662 CoTaskMemFree(valid); 2663 CoTaskMemFree(invalid); 2664 HeapFree(GetProcessHeap(), 0, pInfo); 2665 return NULL; 2666 } 2667 2668 pInfo->hwnd = hwnd; 2669 pInfo->pwszValidChars = valid; 2670 pInfo->pwszInvalidChars = invalid; 2671 if (!SetPropW(hwnd, L"UxSubclassInfo", pInfo)) 2672 { 2673 UxSubclassInfo_Destroy(pInfo); 2674 pInfo = NULL; 2675 } 2676 return pInfo; 2677 } 2678 2679 HRESULT WINAPI 2680 SHLimitInputEdit(HWND hWnd, IShellFolder *psf) 2681 { 2682 IItemNameLimits *pLimits; 2683 HRESULT hr; 2684 LPWSTR pwszValidChars, pwszInvalidChars; 2685 UxSubclassInfo *pInfo; 2686 2687 pInfo = GetPropW(hWnd, L"UxSubclassInfo"); 2688 if (pInfo) 2689 { 2690 UxSubclassInfo_Destroy(pInfo); 2691 pInfo = NULL; 2692 } 2693 2694 hr = psf->lpVtbl->QueryInterface(psf, &IID_IItemNameLimits, (LPVOID *)&pLimits); 2695 if (FAILED(hr)) 2696 { 2697 ERR("hr: %x\n", hr); 2698 return hr; 2699 } 2700 2701 pwszValidChars = pwszInvalidChars = NULL; 2702 hr = pLimits->lpVtbl->GetValidCharacters(pLimits, &pwszValidChars, &pwszInvalidChars); 2703 if (FAILED(hr)) 2704 { 2705 ERR("hr: %x\n", hr); 2706 pLimits->lpVtbl->Release(pLimits); 2707 return hr; 2708 } 2709 2710 pInfo = UxSubclassInfo_Create(hWnd, pwszValidChars, pwszInvalidChars); 2711 if (!pInfo) 2712 hr = E_FAIL; 2713 2714 pLimits->lpVtbl->Release(pLimits); 2715 2716 return hr; 2717 } 2718