1 /* 2 * Provides default file shell extension 3 * 4 * Copyright 2005 Johannes Anderwald 5 * Copyright 2012 Rafal Harabien 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "precomp.h" 23 24 #define NTOS_MODE_USER 25 #include <ndk/iofuncs.h> 26 #include <ndk/obfuncs.h> 27 28 WINE_DEFAULT_DEBUG_CHANNEL(shell); 29 30 EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath); 31 32 BOOL GetPhysicalFileSize(LPCWSTR PathBuffer, PULARGE_INTEGER Size) 33 { 34 UNICODE_STRING FileName; 35 OBJECT_ATTRIBUTES ObjectAttributes; 36 IO_STATUS_BLOCK IoStatusBlock; 37 HANDLE FileHandle; 38 FILE_STANDARD_INFORMATION FileInfo; 39 NTSTATUS Status; 40 41 if (!RtlDosPathNameToNtPathName_U(PathBuffer, &FileName, NULL, NULL)) 42 { 43 ERR("RtlDosPathNameToNtPathName_U failed\n"); 44 return FALSE; 45 } 46 47 InitializeObjectAttributes(&ObjectAttributes, 48 &FileName, 49 OBJ_CASE_INSENSITIVE, 50 NULL, 51 NULL); 52 Status = NtOpenFile(&FileHandle, 53 FILE_READ_ATTRIBUTES | SYNCHRONIZE, 54 &ObjectAttributes, 55 &IoStatusBlock, 56 FILE_SHARE_READ, 57 FILE_SYNCHRONOUS_IO_NONALERT); 58 RtlFreeUnicodeString(&FileName); 59 if (!NT_SUCCESS(Status)) 60 { 61 ERR("NtOpenFile failed for %S (Status 0x%08lx)\n", PathBuffer, Status); 62 return FALSE; 63 } 64 65 /* Query the file size */ 66 Status = NtQueryInformationFile(FileHandle, 67 &IoStatusBlock, 68 &FileInfo, 69 sizeof(FileInfo), 70 FileStandardInformation); 71 NtClose(FileHandle); 72 if (!NT_SUCCESS(Status)) 73 { 74 ERR("NtQueryInformationFile failed for %S (Status: %08lX)\n", PathBuffer, Status); 75 return FALSE; 76 } 77 78 Size->QuadPart = FileInfo.AllocationSize.QuadPart; 79 return TRUE; 80 } 81 82 BOOL CFileVersionInfo::Load(LPCWSTR pwszPath) 83 { 84 ULONG cbInfo = GetFileVersionInfoSizeW(pwszPath, NULL); 85 if (!cbInfo) 86 { 87 WARN("GetFileVersionInfoSize %ls failed\n", pwszPath); 88 return FALSE; 89 } 90 91 m_pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbInfo); 92 if (!m_pInfo) 93 { 94 ERR("HeapAlloc failed bytes %x\n", cbInfo); 95 return FALSE; 96 } 97 98 if (!GetFileVersionInfoW(pwszPath, 0, cbInfo, m_pInfo)) 99 { 100 ERR("GetFileVersionInfoW failed\n"); 101 return FALSE; 102 } 103 104 LPLANGANDCODEPAGE lpLangCode; 105 UINT cBytes; 106 if (!VerQueryValueW(m_pInfo, L"\\VarFileInfo\\Translation", (LPVOID *)&lpLangCode, &cBytes) || cBytes < sizeof(LANGANDCODEPAGE)) 107 { 108 ERR("VerQueryValueW failed\n"); 109 return FALSE; 110 } 111 112 /* FIXME: find language from current locale / if not available, 113 * default to english 114 * for now default to first available language 115 */ 116 m_wLang = lpLangCode->wLang; 117 m_wCode = lpLangCode->wCode; 118 TRACE("Lang %hx Code %hu\n", m_wLang, m_wCode); 119 120 return TRUE; 121 } 122 123 LPCWSTR CFileVersionInfo::GetString(LPCWSTR pwszName) 124 { 125 if (!m_pInfo) 126 return NULL; 127 128 WCHAR wszBuf[256]; 129 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", m_wLang, m_wCode, pwszName); 130 131 /* Query string in version block */ 132 LPCWSTR pwszResult = NULL; 133 UINT cBytes = 0; 134 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes)) 135 pwszResult = NULL; 136 137 if (!pwszResult) 138 { 139 /* Try US English */ 140 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1252, pwszName); 141 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes)) 142 pwszResult = NULL; 143 } 144 145 if (!pwszResult) 146 ERR("VerQueryValueW %ls failed\n", pwszName); 147 else 148 TRACE("%ls: %ls\n", pwszName, pwszResult); 149 150 return pwszResult; 151 } 152 153 VS_FIXEDFILEINFO *CFileVersionInfo::GetFixedInfo() 154 { 155 if (!m_pInfo) 156 return NULL; 157 158 VS_FIXEDFILEINFO *pInfo; 159 UINT cBytes; 160 if (!VerQueryValueW(m_pInfo, L"\\", (PVOID*)&pInfo, &cBytes)) 161 return NULL; 162 return pInfo; 163 } 164 165 LPCWSTR CFileVersionInfo::GetLangName() 166 { 167 if (!m_pInfo) 168 return NULL; 169 170 if (!m_wszLang[0]) 171 { 172 if (!VerLanguageNameW(m_wLang, m_wszLang, _countof(m_wszLang))) 173 ERR("VerLanguageNameW failed\n"); 174 } 175 176 return m_wszLang; 177 } 178 179 UINT 180 SH_FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax) 181 { 182 // Print the number in uniform mode 183 WCHAR wszNumber[24]; 184 swprintf(wszNumber, L"%I64u", Num); 185 186 // Get system strings for decimal and thousand separators. 187 WCHAR wszDecimalSep[8], wszThousandSep[8]; 188 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep)); 189 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep)); 190 191 // Initialize format for printing the number in bytes 192 NUMBERFMTW nf; 193 ZeroMemory(&nf, sizeof(nf)); 194 nf.lpDecimalSep = wszDecimalSep; 195 nf.lpThousandSep = wszThousandSep; 196 197 // Get system string for groups separator 198 WCHAR wszGrouping[12]; 199 INT cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT, 200 LOCALE_SGROUPING, 201 wszGrouping, 202 _countof(wszGrouping)); 203 204 // Convert grouping specs from string to integer 205 for (INT i = 0; i < cchGrouping; i++) 206 { 207 WCHAR wch = wszGrouping[i]; 208 209 if (wch >= L'0' && wch <= L'9') 210 nf.Grouping = nf.Grouping * 10 + (wch - L'0'); 211 else if (wch != L';') 212 break; 213 } 214 215 if ((nf.Grouping % 10) == 0) 216 nf.Grouping /= 10; 217 else 218 nf.Grouping *= 10; 219 220 // Format the number 221 INT cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT, 222 0, 223 wszNumber, 224 &nf, 225 pwszResult, 226 cchResultMax); 227 228 if (!cchResult) 229 return 0; 230 231 // GetNumberFormatW returns number of characters including UNICODE_NULL 232 return cchResult - 1; 233 } 234 235 UINT 236 SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax) 237 { 238 /* Write formated bytes count */ 239 INT cchWritten = SH_FormatInteger(cbSize, pwszResult, cchResultMax); 240 if (!cchWritten) 241 return 0; 242 243 /* Copy " bytes" to buffer */ 244 LPWSTR pwszEnd = pwszResult + cchWritten; 245 size_t cchRemaining = cchResultMax - cchWritten; 246 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0); 247 cchWritten = LoadStringW(shell32_hInstance, IDS_BYTES_FORMAT, pwszEnd, cchRemaining); 248 cchRemaining -= cchWritten; 249 250 return cchResultMax - cchRemaining; 251 } 252 253 /************************************************************************* 254 * 255 * SH_FormatFileSizeWithBytes 256 * 257 * Format a size in bytes to string. 258 * 259 * lpQwSize = Pointer to 64bit large integer to format 260 * pszBuf = Buffer to fill with output string 261 * cchBuf = size of pszBuf in characters 262 * 263 */ 264 265 LPWSTR 266 SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax) 267 { 268 /* Format bytes in KBs, MBs etc */ 269 if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL) 270 return NULL; 271 272 /* If there is less bytes than 1KB, we have nothing to do */ 273 if (lpQwSize->QuadPart < 1024) 274 return pwszResult; 275 276 /* Concatenate " (" */ 277 UINT cchWritten = wcslen(pwszResult); 278 LPWSTR pwszEnd = pwszResult + cchWritten; 279 size_t cchRemaining = cchResultMax - cchWritten; 280 StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0); 281 282 /* Write formated bytes count */ 283 cchWritten = SH_FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining); 284 pwszEnd += cchWritten; 285 cchRemaining -= cchWritten; 286 287 /* Copy ")" to the buffer */ 288 StringCchCopyW(pwszEnd, cchRemaining, L")"); 289 290 return pwszResult; 291 } 292 293 /************************************************************************* 294 * 295 * SH_CreatePropertySheetPage [Internal] 296 * 297 * creates a property sheet page from a resource id 298 * 299 */ 300 301 HPROPSHEETPAGE 302 SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle) 303 { 304 PROPSHEETPAGEW Page; 305 306 memset(&Page, 0x0, sizeof(PROPSHEETPAGEW)); 307 Page.dwSize = sizeof(PROPSHEETPAGEW); 308 Page.dwFlags = PSP_DEFAULT; 309 Page.hInstance = shell32_hInstance; 310 Page.pszTemplate = MAKEINTRESOURCE(wDialogId); 311 Page.pfnDlgProc = pfnDlgProc; 312 Page.lParam = lParam; 313 Page.pszTitle = pwszTitle; 314 315 if (pwszTitle) 316 Page.dwFlags |= PSP_USETITLE; 317 318 return CreatePropertySheetPageW(&Page); 319 } 320 321 VOID 322 CFileDefExt::InitOpensWithField(HWND hwndDlg) 323 { 324 WCHAR wszBuf[MAX_PATH] = L""; 325 WCHAR wszPath[MAX_PATH] = L""; 326 DWORD dwSize = sizeof(wszBuf); 327 BOOL bUnknownApp = TRUE; 328 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 329 330 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS) 331 { 332 bUnknownApp = FALSE; 333 StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command"); 334 dwSize = sizeof(wszPath); 335 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS) 336 { 337 /* Get path from command line */ 338 ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf)); 339 PathRemoveArgs(wszBuf); 340 PathUnquoteSpacesW(wszBuf); 341 PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath)); 342 343 HICON hIcon; 344 if (ExtractIconExW(wszPath, 0, NULL, &hIcon, 1)) 345 { 346 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025); 347 HWND hDescrCtrl = GetDlgItem(hwndDlg, 14007); 348 ShowWindow(hIconCtrl, SW_SHOW); 349 RECT rcIcon, rcDescr; 350 GetWindowRect(hIconCtrl, &rcIcon); 351 352 rcIcon.right = rcIcon.left + GetSystemMetrics(SM_CXSMICON); 353 rcIcon.bottom = rcIcon.top + GetSystemMetrics(SM_CYSMICON); 354 355 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2); 356 GetWindowRect(hDescrCtrl, &rcDescr); 357 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2); 358 INT cxOffset = rcIcon.right + 2 - rcDescr.left; 359 SetWindowPos(hDescrCtrl, NULL, 360 rcDescr.left + cxOffset, rcDescr.top, 361 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top, 362 SWP_NOZORDER); 363 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0); 364 } else 365 ERR("Failed to extract icon\n"); 366 367 if (PathFileExistsW(wszPath)) 368 { 369 /* Get file description */ 370 CFileVersionInfo VerInfo; 371 VerInfo.Load(wszPath); 372 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription"); 373 if (pwszDescr) 374 SetDlgItemTextW(hwndDlg, 14007, pwszDescr); 375 else 376 { 377 /* File has no description - display filename */ 378 LPWSTR pwszFilename = PathFindFileNameW(wszPath); 379 PathRemoveExtension(pwszFilename); 380 pwszFilename[0] = towupper(pwszFilename[0]); 381 SetDlgItemTextW(hwndDlg, 14007, pwszFilename); 382 } 383 } 384 else 385 bUnknownApp = TRUE; 386 } else 387 WARN("RegGetValueW %ls failed\n", wszBuf); 388 } else 389 WARN("RegGetValueW %ls failed\n", pwszExt); 390 391 if (bUnknownApp) 392 { 393 /* Unknown application */ 394 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf)); 395 SetDlgItemTextW(hwndDlg, 14007, wszBuf); 396 } 397 } 398 399 /************************************************************************* 400 * 401 * SH_FileGeneralFileType [Internal] 402 * 403 * retrieves file extension description from registry and sets it in dialog 404 * 405 * TODO: retrieve file extension default icon and load it 406 * find executable name from registry, retrieve description from executable 407 */ 408 409 BOOL 410 CFileDefExt::InitFileType(HWND hwndDlg) 411 { 412 TRACE("path %s\n", debugstr_w(m_wszPath)); 413 414 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005); 415 if (hDlgCtrl == NULL) 416 return FALSE; 417 418 /* Get file information */ 419 SHFILEINFOW fi; 420 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON)) 421 { 422 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError()); 423 fi.szTypeName[0] = L'\0'; 424 fi.hIcon = NULL; 425 } 426 427 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 428 if (pwszExt[0]) 429 { 430 WCHAR wszBuf[256]; 431 432 if (!fi.szTypeName[0]) 433 { 434 /* The file type is unknown, so default to string "FileExtension File" */ 435 size_t cchRemaining = 0; 436 LPWSTR pwszEnd = NULL; 437 438 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1); 439 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd); 440 441 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf); 442 } 443 else 444 { 445 /* Update file type */ 446 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt); 447 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf); 448 } 449 } 450 451 /* Update file icon */ 452 if (fi.hIcon) 453 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0); 454 else 455 ERR("No icon %ls\n", m_wszPath); 456 457 return TRUE; 458 } 459 460 /************************************************************************* 461 * 462 * CFileDefExt::InitFilePath [Internal] 463 * 464 * sets file path string and filename string 465 * 466 */ 467 468 BOOL 469 CFileDefExt::InitFilePath(HWND hwndDlg) 470 { 471 /* Find the filename */ 472 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath); 473 474 if (pwszFilename > m_wszPath) 475 { 476 /* Location field */ 477 WCHAR wszLocation[MAX_PATH]; 478 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath); 479 PathRemoveBackslashW(wszLocation); 480 481 SetDlgItemTextW(hwndDlg, 14009, wszLocation); 482 } 483 484 /* Filename field */ 485 SetDlgItemTextW(hwndDlg, 14001, pwszFilename); 486 487 return TRUE; 488 } 489 490 /************************************************************************* 491 * 492 * CFileDefExt::GetFileTimeString [Internal] 493 * 494 * formats a given LPFILETIME struct into readable user format 495 */ 496 497 BOOL 498 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult) 499 { 500 FILETIME ft; 501 SYSTEMTIME st; 502 503 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st)) 504 return FALSE; 505 506 size_t cchRemaining = cchResult; 507 LPWSTR pwszEnd = pwszResult; 508 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining); 509 if (cchWritten) 510 --cchWritten; // GetDateFormatW returns count with terminating zero 511 else 512 ERR("GetDateFormatW failed\n"); 513 cchRemaining -= cchWritten; 514 pwszEnd += cchWritten; 515 516 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0); 517 518 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining); 519 if (cchWritten) 520 --cchWritten; // GetTimeFormatW returns count with terminating zero 521 else 522 ERR("GetTimeFormatW failed\n"); 523 TRACE("result %s\n", debugstr_w(pwszResult)); 524 return TRUE; 525 } 526 527 /************************************************************************* 528 * 529 * CFileDefExt::InitFileAttr [Internal] 530 * 531 * retrieves file information from file and sets in dialog 532 * 533 */ 534 535 BOOL 536 CFileDefExt::InitFileAttr(HWND hwndDlg) 537 { 538 BOOL Success; 539 WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA 540 WCHAR wszBuf[MAX_PATH]; 541 542 TRACE("InitFileAttr %ls\n", m_wszPath); 543 544 /* 545 * There are situations where GetFileAttributes(Ex) can fail even if the 546 * specified path represents a file. This happens when e.g. the file is a 547 * locked system file, such as C:\pagefile.sys . In this case, the function 548 * returns INVALID_FILE_ATTRIBUTES and GetLastError returns ERROR_SHARING_VIOLATION. 549 * (this would allow us to distinguish between this failure and a failure 550 * due to the fact that the path actually refers to a directory). 551 * 552 * Because we really want to retrieve the file attributes/size/date&time, 553 * we do the following trick: 554 * - First we call GetFileAttributesEx. If it succeeds we know we have 555 * a file or a directory, and we have retrieved its attributes. 556 * - If GetFileAttributesEx fails, we call FindFirstFile on the full path. 557 * While we could have called FindFirstFile at first and skip GetFileAttributesEx 558 * altogether, we do it after GetFileAttributesEx because it performs more 559 * work to retrieve the file attributes. However it actually works even 560 * for locked system files. 561 * - If FindFirstFile succeeds we have retrieved its attributes. 562 * - Otherwise (FindFirstFile has failed), we do not retrieve anything. 563 * 564 * The following code also relies on the fact that the first 6 members 565 * of WIN32_FIND_DATA are *exactly* the same as the WIN32_FILE_ATTRIBUTE_DATA 566 * structure. Therefore it is safe to use a single WIN32_FIND_DATA 567 * structure for both the GetFileAttributesEx and FindFirstFile calls. 568 */ 569 570 Success = GetFileAttributesExW(m_wszPath, 571 GetFileExInfoStandard, 572 (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo); 573 if (!Success) 574 { 575 HANDLE hFind = FindFirstFileW(m_wszPath, &FileInfo); 576 Success = (hFind != INVALID_HANDLE_VALUE); 577 if (Success) 578 FindClose(hFind); 579 } 580 581 if (Success) 582 { 583 /* Update attribute checkboxes */ 584 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 585 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0); 586 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 587 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0); 588 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) 589 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0); 590 591 /* Update creation time */ 592 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf))) 593 SetDlgItemTextW(hwndDlg, 14015, wszBuf); 594 595 /* For files display last access and last write time */ 596 if (!m_bDir) 597 { 598 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf))) 599 SetDlgItemTextW(hwndDlg, 14019, wszBuf); 600 601 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf))) 602 SetDlgItemTextW(hwndDlg, 14017, wszBuf); 603 604 /* Update size of file */ 605 ULARGE_INTEGER FileSize; 606 FileSize.u.LowPart = FileInfo.nFileSizeLow; 607 FileSize.u.HighPart = FileInfo.nFileSizeHigh; 608 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf))) 609 { 610 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 611 612 // Compute file on disk. If fails, use logical size 613 if (GetPhysicalFileSize(m_wszPath, &FileSize)) 614 SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)); 615 else 616 ERR("Unreliable size on disk\n"); 617 618 SetDlgItemTextW(hwndDlg, 14012, wszBuf); 619 } 620 } 621 } 622 623 if (m_bDir) 624 { 625 /* For directories files have to be counted */ 626 627 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData))); 628 data->This = this; 629 data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR) * MAX_PATH)); 630 data->hwndDlg = hwndDlg; 631 this->AddRef(); 632 StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath); 633 634 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL); 635 636 /* Update size field */ 637 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 638 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 639 640 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf))) 641 SetDlgItemTextW(hwndDlg, 14012, wszBuf); 642 643 /* Display files and folders count */ 644 WCHAR wszFormat[256]; 645 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 646 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 647 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 648 } 649 650 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */ 651 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE); 652 653 return TRUE; 654 } 655 656 /************************************************************************* 657 * 658 * CFileDefExt::InitGeneralPage [Internal] 659 * 660 * sets all file general properties in dialog 661 */ 662 663 BOOL 664 CFileDefExt::InitGeneralPage(HWND hwndDlg) 665 { 666 /* Set general text properties filename filelocation and icon */ 667 InitFilePath(hwndDlg); 668 669 /* Set file type and icon */ 670 InitFileType(hwndDlg); 671 672 /* Set open with application */ 673 if (!m_bDir) 674 { 675 if (!PathIsExeW(m_wszPath)) 676 InitOpensWithField(hwndDlg); 677 else 678 { 679 WCHAR wszBuf[MAX_PATH]; 680 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf)); 681 SetDlgItemTextW(hwndDlg, 14006, wszBuf); 682 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE); 683 684 /* hidden button 14024 allows to draw edit 14007 larger than defined in resources , we use edit 14009 as idol */ 685 RECT rectIdol, rectToAdjust; 686 GetClientRect(GetDlgItem(hwndDlg, 14009), &rectIdol); 687 GetClientRect(GetDlgItem(hwndDlg, 14007), &rectToAdjust); 688 SetWindowPos(GetDlgItem(hwndDlg, 14007), HWND_TOP, 0, 0, 689 rectIdol.right-rectIdol.left /* make it as wide as its idol */, 690 rectToAdjust.bottom-rectToAdjust.top /* but keep its current height */, 691 SWP_NOMOVE | SWP_NOZORDER ); 692 693 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription"); 694 if (pwszDescr) 695 SetDlgItemTextW(hwndDlg, 14007, pwszDescr); 696 else 697 { 698 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath)); 699 PathRemoveExtension(wszBuf); 700 SetDlgItemTextW(hwndDlg, 14007, wszBuf); 701 } 702 } 703 } 704 705 /* Set file created/modfied/accessed time, size and attributes */ 706 InitFileAttr(hwndDlg); 707 708 return TRUE; 709 } 710 711 /************************************************************************* 712 * 713 * CFileDefExt::GeneralPageProc 714 * 715 * wnd proc of 'General' property sheet page 716 * 717 */ 718 719 INT_PTR CALLBACK 720 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 721 { 722 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 723 switch (uMsg) 724 { 725 case WM_INITDIALOG: 726 { 727 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam; 728 729 if (ppsp == NULL || !ppsp->lParam) 730 break; 731 732 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam); 733 734 pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam); 735 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt); 736 pFileDefExt->InitGeneralPage(hwndDlg); 737 break; 738 } 739 case WM_COMMAND: 740 if (LOWORD(wParam) == 14024) /* Opens With - Change */ 741 { 742 OPENASINFO oainfo; 743 oainfo.pcszFile = pFileDefExt->m_wszPath; 744 oainfo.pcszClass = NULL; 745 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION; 746 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo)); 747 } 748 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */ 749 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 750 else if (LOWORD(wParam) == 14001) /* Name */ 751 { 752 if (HIWORD(wParam) == EN_CHANGE) 753 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 754 } 755 break; 756 case WM_NOTIFY: 757 { 758 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam; 759 if (lppsn->hdr.code == PSN_APPLY) 760 { 761 /* Update attributes first */ 762 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath); 763 if (dwAttr) 764 { 765 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE); 766 767 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0)) 768 dwAttr |= FILE_ATTRIBUTE_READONLY; 769 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0)) 770 dwAttr |= FILE_ATTRIBUTE_HIDDEN; 771 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0)) 772 dwAttr |= FILE_ATTRIBUTE_ARCHIVE; 773 774 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr)) 775 ERR("SetFileAttributesW failed\n"); 776 } 777 778 /* Update filename now */ 779 WCHAR wszBuf[MAX_PATH]; 780 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath); 781 LPWSTR pwszFilename = PathFindFileNameW(wszBuf); 782 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf); 783 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax)) 784 { 785 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf)) 786 ERR("MoveFileW failed\n"); 787 } 788 789 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); 790 return TRUE; 791 } 792 break; 793 } 794 case PSM_QUERYSIBLINGS: 795 { 796 // reset icon 797 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025); 798 HICON hIcon = (HICON)SendMessageW(hIconCtrl, STM_GETICON, 0, 0); 799 DestroyIcon(hIcon); 800 hIcon = NULL; 801 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0); 802 803 // refresh the page 804 pFileDefExt->InitGeneralPage(hwndDlg); 805 return FALSE; // continue 806 } 807 default: 808 break; 809 } 810 811 return FALSE; 812 } 813 814 /************************************************************************* 815 * 816 * CFileDefExt::InitVersionPage [Internal] 817 * 818 * sets all file version properties in dialog 819 */ 820 821 BOOL 822 CFileDefExt::InitVersionPage(HWND hwndDlg) 823 { 824 /* Get fixed info */ 825 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo(); 826 if (pInfo) 827 { 828 WCHAR wszVersion[256]; 829 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS), 830 LOWORD(pInfo->dwFileVersionMS), 831 HIWORD(pInfo->dwFileVersionLS), 832 LOWORD(pInfo->dwFileVersionLS)); 833 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion)); 834 SetDlgItemTextW(hwndDlg, 14001, wszVersion); 835 } 836 837 /* Update labels */ 838 SetVersionLabel(hwndDlg, 14003, L"FileDescription"); 839 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright"); 840 841 /* Add items to listbox */ 842 AddVersionString(hwndDlg, L"CompanyName"); 843 LPCWSTR pwszLang = m_VerInfo.GetLangName(); 844 if (pwszLang) 845 { 846 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 847 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language"); 848 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang); 849 } 850 AddVersionString(hwndDlg, L"ProductName"); 851 AddVersionString(hwndDlg, L"InternalName"); 852 AddVersionString(hwndDlg, L"OriginalFilename"); 853 AddVersionString(hwndDlg, L"FileVersion"); 854 AddVersionString(hwndDlg, L"ProductVersion"); 855 856 /* Attach file version to dialog window */ 857 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)this); 858 859 /* Select first item */ 860 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 861 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0); 862 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL); 863 if (pwszText && pwszText != (LPCWSTR)LB_ERR) 864 SetDlgItemTextW(hwndDlg, 14010, pwszText); 865 866 return TRUE; 867 } 868 869 /************************************************************************* 870 * 871 * CFileDefExt::SetVersionLabel [Internal] 872 * 873 * retrieves a version string and uses it to set label text 874 */ 875 876 BOOL 877 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName) 878 { 879 if (hwndDlg == NULL || pwszName == NULL) 880 return FALSE; 881 882 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName); 883 if (pwszValue) 884 { 885 /* file description property */ 886 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue)); 887 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue); 888 return TRUE; 889 } 890 891 return FALSE; 892 } 893 894 /************************************************************************* 895 * 896 * CFileDefExt::AddVersionString [Internal] 897 * 898 * retrieves a version string and adds it to listbox 899 */ 900 901 BOOL 902 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName) 903 { 904 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg); 905 906 if (hwndDlg == NULL || pwszName == NULL) 907 return FALSE; 908 909 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName); 910 if (pwszValue) 911 { 912 /* listbox name property */ 913 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 914 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue)); 915 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName); 916 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue); 917 return TRUE; 918 } 919 920 return FALSE; 921 } 922 923 /************************************************************************* 924 * 925 * CFileDefExt::VersionPageProc 926 * 927 * wnd proc of 'Version' property sheet page 928 */ 929 930 INT_PTR CALLBACK 931 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 932 { 933 switch (uMsg) 934 { 935 case WM_INITDIALOG: 936 { 937 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam; 938 939 if (ppsp == NULL || !ppsp->lParam) 940 break; 941 942 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam); 943 944 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam); 945 return pFileDefExt->InitVersionPage(hwndDlg); 946 } 947 case WM_COMMAND: 948 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE) 949 { 950 HWND hDlgCtrl = (HWND)lParam; 951 952 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL); 953 if (Index == LB_ERR) 954 break; 955 956 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL); 957 if (pwszData == NULL) 958 break; 959 960 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(pwszData)); 961 SetDlgItemTextW(hwndDlg, 14010, pwszData); 962 963 return TRUE; 964 } 965 break; 966 case WM_DESTROY: 967 break; 968 case PSM_QUERYSIBLINGS: 969 return FALSE; // continue 970 default: 971 break; 972 } 973 974 return FALSE; 975 } 976 977 /*************************************************************************/ 978 /* Folder Customize */ 979 980 static const WCHAR s_szShellClassInfo[] = L".ShellClassInfo"; 981 static const WCHAR s_szIconIndex[] = L"IconIndex"; 982 static const WCHAR s_szIconFile[] = L"IconFile"; 983 static const WCHAR s_szIconResource[] = L"IconResource"; 984 985 // IDD_FOLDER_CUSTOMIZE 986 INT_PTR CALLBACK 987 CFileDefExt::FolderCustomizePageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 988 { 989 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 990 switch (uMsg) 991 { 992 case WM_INITDIALOG: 993 { 994 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam; 995 996 if (ppsp == NULL || !ppsp->lParam) 997 break; 998 999 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam); 1000 1001 pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam); 1002 return pFileDefExt->InitFolderCustomizePage(hwndDlg); 1003 } 1004 1005 case WM_COMMAND: 1006 switch (LOWORD(wParam)) 1007 { 1008 case IDC_FOLDERCUST_CHANGE_ICON: 1009 pFileDefExt->OnFolderCustChangeIcon(hwndDlg); 1010 break; 1011 1012 case IDC_FOLDERCUST_CHOOSE_PIC: 1013 // TODO: 1014 break; 1015 1016 case IDC_FOLDERCUST_RESTORE_DEFAULTS: 1017 // TODO: 1018 break; 1019 } 1020 break; 1021 1022 case WM_NOTIFY: 1023 { 1024 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam; 1025 if (lppsn->hdr.code == PSN_APPLY) 1026 { 1027 // apply or not 1028 if (pFileDefExt->OnFolderCustApply(hwndDlg)) 1029 { 1030 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); 1031 } 1032 else 1033 { 1034 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); 1035 } 1036 return TRUE; 1037 } 1038 break; 1039 } 1040 1041 case PSM_QUERYSIBLINGS: 1042 return FALSE; // continue 1043 1044 case WM_DESTROY: 1045 pFileDefExt->OnFolderCustDestroy(hwndDlg); 1046 break; 1047 1048 default: 1049 break; 1050 } 1051 1052 return FALSE; 1053 } 1054 1055 // IDD_FOLDER_CUSTOMIZE WM_DESTROY 1056 void CFileDefExt::OnFolderCustDestroy(HWND hwndDlg) 1057 { 1058 ::DestroyIcon(m_hFolderIcon); 1059 m_hFolderIcon = NULL; 1060 1061 /* Detach the object from dialog window */ 1062 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)0); 1063 } 1064 1065 void CFileDefExt::UpdateFolderIcon(HWND hwndDlg) 1066 { 1067 // destroy icon if any 1068 if (m_hFolderIcon) 1069 { 1070 ::DestroyIcon(m_hFolderIcon); 1071 m_hFolderIcon = NULL; 1072 } 1073 1074 // create the icon 1075 if (m_szFolderIconPath[0] == 0 && m_nFolderIconIndex == 0) 1076 { 1077 m_hFolderIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_FOLDER)); 1078 } 1079 else 1080 { 1081 ExtractIconExW(m_szFolderIconPath, m_nFolderIconIndex, &m_hFolderIcon, NULL, 1); 1082 } 1083 1084 // set icon 1085 SendDlgItemMessageW(hwndDlg, IDC_FOLDERCUST_ICON, STM_SETICON, (WPARAM)m_hFolderIcon, 0); 1086 } 1087 1088 // IDD_FOLDER_CUSTOMIZE WM_INITDIALOG 1089 BOOL CFileDefExt::InitFolderCustomizePage(HWND hwndDlg) 1090 { 1091 /* Attach the object to dialog window */ 1092 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)this); 1093 1094 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_COMBOBOX), FALSE); 1095 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_CHECKBOX), FALSE); 1096 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_CHOOSE_PIC), FALSE); 1097 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_RESTORE_DEFAULTS), FALSE); 1098 1099 // build the desktop.ini file path 1100 WCHAR szIniFile[MAX_PATH]; 1101 StringCchCopyW(szIniFile, _countof(szIniFile), m_wszPath); 1102 PathAppendW(szIniFile, L"desktop.ini"); 1103 1104 // desktop.ini --> m_szFolderIconPath, m_nFolderIconIndex 1105 m_szFolderIconPath[0] = 0; 1106 m_nFolderIconIndex = 0; 1107 if (GetPrivateProfileStringW(s_szShellClassInfo, s_szIconFile, NULL, 1108 m_szFolderIconPath, _countof(m_szFolderIconPath), szIniFile)) 1109 { 1110 m_nFolderIconIndex = GetPrivateProfileIntW(s_szShellClassInfo, s_szIconIndex, 0, szIniFile); 1111 } 1112 else if (GetPrivateProfileStringW(s_szShellClassInfo, s_szIconResource, NULL, 1113 m_szFolderIconPath, _countof(m_szFolderIconPath), szIniFile)) 1114 { 1115 m_nFolderIconIndex = PathParseIconLocationW(m_szFolderIconPath); 1116 } 1117 1118 // update icon 1119 UpdateFolderIcon(hwndDlg); 1120 1121 return TRUE; 1122 } 1123 1124 // IDD_FOLDER_CUSTOMIZE IDC_FOLDERCUST_CHANGE_ICON 1125 void CFileDefExt::OnFolderCustChangeIcon(HWND hwndDlg) 1126 { 1127 WCHAR szPath[MAX_PATH]; 1128 INT nIconIndex; 1129 1130 // m_szFolderIconPath, m_nFolderIconIndex --> szPath, nIconIndex 1131 if (m_szFolderIconPath[0]) 1132 { 1133 StringCchCopyW(szPath, _countof(szPath), m_szFolderIconPath); 1134 nIconIndex = m_nFolderIconIndex; 1135 } 1136 else 1137 { 1138 szPath[0] = 0; 1139 nIconIndex = 0; 1140 } 1141 1142 // let the user choose the icon 1143 if (PickIconDlg(hwndDlg, szPath, _countof(szPath), &nIconIndex)) 1144 { 1145 // changed 1146 m_bFolderIconIsSet = TRUE; 1147 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 1148 1149 // update 1150 StringCchCopyW(m_szFolderIconPath, _countof(m_szFolderIconPath), szPath); 1151 m_nFolderIconIndex = nIconIndex; 1152 UpdateFolderIcon(hwndDlg); 1153 } 1154 } 1155 1156 // IDD_FOLDER_CUSTOMIZE PSN_APPLY 1157 BOOL CFileDefExt::OnFolderCustApply(HWND hwndDlg) 1158 { 1159 // build the desktop.ini file path 1160 WCHAR szIniFile[MAX_PATH]; 1161 StringCchCopyW(szIniFile, _countof(szIniFile), m_wszPath); 1162 PathAppendW(szIniFile, L"desktop.ini"); 1163 1164 if (m_bFolderIconIsSet) // it is set! 1165 { 1166 DWORD attrs; 1167 1168 // change folder attributes (-S -R) 1169 attrs = GetFileAttributesW(m_wszPath); 1170 attrs &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY); 1171 SetFileAttributesW(m_wszPath, attrs); 1172 1173 // change desktop.ini attributes (-S -H -R) 1174 attrs = GetFileAttributesW(szIniFile); 1175 attrs &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); 1176 SetFileAttributesW(szIniFile, attrs); 1177 1178 if (m_szFolderIconPath[0]) 1179 { 1180 // write IconFile and IconIndex 1181 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconFile, m_szFolderIconPath, szIniFile); 1182 1183 WCHAR szInt[32]; 1184 StringCchPrintfW(szInt, _countof(szInt), L"%d", m_nFolderIconIndex); 1185 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconIndex, szInt, szIniFile); 1186 1187 // flush! 1188 WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile); 1189 } 1190 else 1191 { 1192 // erase three values 1193 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconFile, NULL, szIniFile); 1194 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconIndex, NULL, szIniFile); 1195 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconResource, NULL, szIniFile); 1196 1197 // flush! 1198 WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile); 1199 } 1200 1201 // change desktop.ini attributes (+S +H) 1202 attrs = GetFileAttributesW(szIniFile); 1203 attrs |= FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; 1204 SetFileAttributesW(szIniFile, attrs); 1205 1206 // change folder attributes (+R) 1207 attrs = GetFileAttributesW(m_wszPath); 1208 attrs |= FILE_ATTRIBUTE_READONLY; 1209 SetFileAttributesW(m_wszPath, attrs); 1210 1211 // notify to the siblings 1212 PropSheet_QuerySiblings(GetParent(hwndDlg), 0, 0); 1213 1214 // done! 1215 m_bFolderIconIsSet = FALSE; 1216 } 1217 1218 return TRUE; 1219 } 1220 1221 /*****************************************************************************/ 1222 1223 CFileDefExt::CFileDefExt(): 1224 m_bDir(FALSE), m_cFiles(0), m_cFolders(0) 1225 { 1226 m_wszPath[0] = L'\0'; 1227 m_DirSize.QuadPart = 0ull; 1228 m_DirSizeOnDisc.QuadPart = 0ull; 1229 1230 m_szFolderIconPath[0] = 0; 1231 m_nFolderIconIndex = 0; 1232 m_hFolderIcon = NULL; 1233 m_bFolderIconIsSet = FALSE; 1234 } 1235 1236 CFileDefExt::~CFileDefExt() 1237 { 1238 1239 } 1240 1241 HRESULT WINAPI 1242 CFileDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID) 1243 { 1244 FORMATETC format; 1245 STGMEDIUM stgm; 1246 HRESULT hr; 1247 1248 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID); 1249 1250 if (!pDataObj) 1251 return E_FAIL; 1252 1253 format.cfFormat = CF_HDROP; 1254 format.ptd = NULL; 1255 format.dwAspect = DVASPECT_CONTENT; 1256 format.lindex = -1; 1257 format.tymed = TYMED_HGLOBAL; 1258 1259 hr = pDataObj->GetData(&format, &stgm); 1260 if (FAILED_UNEXPECTEDLY(hr)) 1261 return hr; 1262 1263 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath))) 1264 { 1265 ERR("DragQueryFileW failed\n"); 1266 ReleaseStgMedium(&stgm); 1267 return E_FAIL; 1268 } 1269 1270 ReleaseStgMedium(&stgm); 1271 1272 TRACE("File properties %ls\n", m_wszPath); 1273 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE; 1274 if (!m_bDir) 1275 m_VerInfo.Load(m_wszPath); 1276 1277 return S_OK; 1278 } 1279 1280 HRESULT WINAPI 1281 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 1282 { 1283 UNIMPLEMENTED; 1284 return E_NOTIMPL; 1285 } 1286 1287 HRESULT WINAPI 1288 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 1289 { 1290 UNIMPLEMENTED; 1291 return E_NOTIMPL; 1292 } 1293 1294 HRESULT WINAPI 1295 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) 1296 { 1297 UNIMPLEMENTED; 1298 return E_NOTIMPL; 1299 } 1300 1301 HRESULT WINAPI 1302 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) 1303 { 1304 HPROPSHEETPAGE hPage; 1305 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES; 1306 1307 hPage = SH_CreatePropertySheetPage(wResId, 1308 GeneralPageProc, 1309 (LPARAM)this, 1310 NULL); 1311 if (hPage) 1312 pfnAddPage(hPage, lParam); 1313 1314 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL)) 1315 { 1316 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION, 1317 VersionPageProc, 1318 (LPARAM)this, 1319 NULL); 1320 if (hPage) 1321 pfnAddPage(hPage, lParam); 1322 } 1323 1324 if (m_bDir) 1325 { 1326 hPage = SH_CreatePropertySheetPage(IDD_FOLDER_CUSTOMIZE, 1327 FolderCustomizePageProc, 1328 (LPARAM)this, 1329 NULL); 1330 if (hPage) 1331 pfnAddPage(hPage, lParam); 1332 } 1333 1334 return S_OK; 1335 } 1336 1337 HRESULT WINAPI 1338 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) 1339 { 1340 UNIMPLEMENTED; 1341 return E_NOTIMPL; 1342 } 1343 1344 HRESULT WINAPI 1345 CFileDefExt::SetSite(IUnknown *punk) 1346 { 1347 UNIMPLEMENTED; 1348 return E_NOTIMPL; 1349 } 1350 1351 HRESULT WINAPI 1352 CFileDefExt::GetSite(REFIID iid, void **ppvSite) 1353 { 1354 UNIMPLEMENTED; 1355 return E_NOTIMPL; 1356 } 1357 1358 DWORD WINAPI 1359 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter) 1360 { 1361 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter); 1362 DWORD ticks = 0; 1363 data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, &ticks); 1364 1365 //Release the CFileDefExt and data object holds in the copying thread. 1366 data->This->Release(); 1367 HeapFree(GetProcessHeap(), 0, data->pwszBuf); 1368 HeapFree(GetProcessHeap(), 0, data); 1369 1370 return 0; 1371 } 1372 1373 BOOL 1374 CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPCWSTR pwszBuf, DWORD *ticks) 1375 { 1376 CString sBuf = pwszBuf; 1377 sBuf += L"\\" ; 1378 CString sSearch = sBuf; 1379 sSearch += L"*" ; 1380 CString sFileName; 1381 1382 WIN32_FIND_DATAW wfd; 1383 HANDLE hFind = FindFirstFileW(sSearch, &wfd); 1384 if (hFind == INVALID_HANDLE_VALUE) 1385 { 1386 ERR("FindFirstFileW %ls failed\n", sSearch.GetString()); 1387 return FALSE; 1388 } 1389 1390 BOOL root = FALSE; 1391 if (*ticks == 0) { 1392 *ticks = GetTickCount(); 1393 root = TRUE; 1394 } 1395 1396 do 1397 { 1398 sFileName = sBuf; 1399 sFileName += wfd.cFileName; 1400 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1401 { 1402 /* Don't process "." and ".." items */ 1403 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L"..")) 1404 continue; 1405 1406 ++m_cFolders; 1407 1408 CountFolderAndFiles(hwndDlg, sFileName, ticks); 1409 } 1410 else 1411 { 1412 m_cFiles++; 1413 1414 ULARGE_INTEGER FileSize; 1415 FileSize.u.LowPart = wfd.nFileSizeLow; 1416 FileSize.u.HighPart = wfd.nFileSizeHigh; 1417 m_DirSize.QuadPart += FileSize.QuadPart; 1418 // Calculate size on disc 1419 if (!GetPhysicalFileSize(sFileName.GetString(), &FileSize)) 1420 ERR("GetPhysicalFileSize failed for %ls\n", sFileName.GetString()); 1421 m_DirSizeOnDisc.QuadPart += FileSize.QuadPart; 1422 } 1423 if (GetTickCount() - *ticks > (DWORD) 300) 1424 { 1425 /* FIXME Using IsWindow is generally ill advised */ 1426 if (IsWindow(hwndDlg)) 1427 { 1428 WCHAR wszBuf[100]; 1429 1430 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 1431 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 1432 1433 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf))) 1434 SetDlgItemTextW(hwndDlg, 14012, wszBuf); 1435 1436 /* Display files and folders count */ 1437 WCHAR wszFormat[100]; 1438 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 1439 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 1440 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 1441 *ticks = GetTickCount(); 1442 } 1443 else 1444 break; 1445 } 1446 } while(FindNextFileW(hFind, &wfd)); 1447 1448 if (root && IsWindow(hwndDlg)) 1449 { 1450 WCHAR wszBuf[100]; 1451 1452 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 1453 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 1454 1455 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf))) 1456 SetDlgItemTextW(hwndDlg, 14012, wszBuf); 1457 1458 /* Display files and folders count */ 1459 WCHAR wszFormat[100]; 1460 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 1461 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 1462 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 1463 } 1464 1465 FindClose(hFind); 1466 return TRUE; 1467 } 1468