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