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 WINE_DEFAULT_DEBUG_CHANNEL(shell); 25 26 EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath); 27 28 BOOL CFileVersionInfo::Load(LPCWSTR pwszPath) 29 { 30 ULONG cbInfo = GetFileVersionInfoSizeW(pwszPath, NULL); 31 if (!cbInfo) 32 { 33 WARN("GetFileVersionInfoSize %ls failed\n", pwszPath); 34 return FALSE; 35 } 36 37 m_pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbInfo); 38 if (!m_pInfo) 39 { 40 ERR("HeapAlloc failed bytes %x\n", cbInfo); 41 return FALSE; 42 } 43 44 if (!GetFileVersionInfoW(pwszPath, 0, cbInfo, m_pInfo)) 45 { 46 ERR("GetFileVersionInfoW failed\n"); 47 return FALSE; 48 } 49 50 LPLANGANDCODEPAGE lpLangCode; 51 UINT cBytes; 52 if (!VerQueryValueW(m_pInfo, L"\\VarFileInfo\\Translation", (LPVOID *)&lpLangCode, &cBytes) || cBytes < sizeof(LANGANDCODEPAGE)) 53 { 54 ERR("VerQueryValueW failed\n"); 55 return FALSE; 56 } 57 58 /* FIXME: find language from current locale / if not available, 59 * default to english 60 * for now default to first available language 61 */ 62 m_wLang = lpLangCode->wLang; 63 m_wCode = lpLangCode->wCode; 64 TRACE("Lang %hx Code %hu\n", m_wLang, m_wCode); 65 66 return TRUE; 67 } 68 69 LPCWSTR CFileVersionInfo::GetString(LPCWSTR pwszName) 70 { 71 if (!m_pInfo) 72 return NULL; 73 74 WCHAR wszBuf[256]; 75 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", m_wLang, m_wCode, pwszName); 76 77 /* Query string in version block */ 78 LPCWSTR pwszResult = NULL; 79 UINT cBytes = 0; 80 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes)) 81 pwszResult = NULL; 82 83 if (!m_wLang && !m_wCode) 84 { 85 /* Try US English */ 86 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1252, pwszName); 87 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes)) 88 pwszResult = NULL; 89 } 90 91 if (!pwszResult) 92 ERR("VerQueryValueW %ls failed\n", pwszName); 93 else 94 TRACE("%ls: %ls\n", pwszName, pwszResult); 95 96 return pwszResult; 97 } 98 99 VS_FIXEDFILEINFO *CFileVersionInfo::GetFixedInfo() 100 { 101 if (!m_pInfo) 102 return NULL; 103 104 VS_FIXEDFILEINFO *pInfo; 105 UINT cBytes; 106 if (!VerQueryValueW(m_pInfo, L"\\", (PVOID*)&pInfo, &cBytes)) 107 return NULL; 108 return pInfo; 109 } 110 111 LPCWSTR CFileVersionInfo::GetLangName() 112 { 113 if (!m_pInfo) 114 return NULL; 115 116 if (!m_wszLang[0]) 117 { 118 if (!VerLanguageNameW(m_wLang, m_wszLang, _countof(m_wszLang))) 119 ERR("VerLanguageNameW failed\n"); 120 } 121 122 return m_wszLang; 123 } 124 125 UINT 126 SH_FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax) 127 { 128 // Print the number in uniform mode 129 WCHAR wszNumber[24]; 130 swprintf(wszNumber, L"%I64u", Num); 131 132 // Get system strings for decimal and thousand separators. 133 WCHAR wszDecimalSep[8], wszThousandSep[8]; 134 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep)); 135 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep)); 136 137 // Initialize format for printing the number in bytes 138 NUMBERFMTW nf; 139 ZeroMemory(&nf, sizeof(nf)); 140 nf.lpDecimalSep = wszDecimalSep; 141 nf.lpThousandSep = wszThousandSep; 142 143 // Get system string for groups separator 144 WCHAR wszGrouping[12]; 145 INT cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT, 146 LOCALE_SGROUPING, 147 wszGrouping, 148 _countof(wszGrouping)); 149 150 // Convert grouping specs from string to integer 151 for (INT i = 0; i < cchGrouping; i++) 152 { 153 WCHAR wch = wszGrouping[i]; 154 155 if (wch >= L'0' && wch <= L'9') 156 nf.Grouping = nf.Grouping * 10 + (wch - L'0'); 157 else if (wch != L';') 158 break; 159 } 160 161 if ((nf.Grouping % 10) == 0) 162 nf.Grouping /= 10; 163 else 164 nf.Grouping *= 10; 165 166 // Format the number 167 INT cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT, 168 0, 169 wszNumber, 170 &nf, 171 pwszResult, 172 cchResultMax); 173 174 if (!cchResult) 175 return 0; 176 177 // GetNumberFormatW returns number of characters including UNICODE_NULL 178 return cchResult - 1; 179 } 180 181 UINT 182 SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax) 183 { 184 /* Write formated bytes count */ 185 INT cchWritten = SH_FormatInteger(cbSize, pwszResult, cchResultMax); 186 if (!cchWritten) 187 return 0; 188 189 /* Copy " bytes" to buffer */ 190 LPWSTR pwszEnd = pwszResult + cchWritten; 191 size_t cchRemaining = cchResultMax - cchWritten; 192 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0); 193 cchWritten = LoadStringW(shell32_hInstance, IDS_BYTES_FORMAT, pwszEnd, cchRemaining); 194 cchRemaining -= cchWritten; 195 196 return cchResultMax - cchRemaining; 197 } 198 199 /************************************************************************* 200 * 201 * SH_FormatFileSizeWithBytes 202 * 203 * Format a size in bytes to string. 204 * 205 * lpQwSize = Pointer to 64bit large integer to format 206 * pszBuf = Buffer to fill with output string 207 * cchBuf = size of pszBuf in characters 208 * 209 */ 210 211 LPWSTR 212 SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax) 213 { 214 /* Format bytes in KBs, MBs etc */ 215 if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL) 216 return NULL; 217 218 /* If there is less bytes than 1KB, we have nothing to do */ 219 if (lpQwSize->QuadPart < 1024) 220 return pwszResult; 221 222 /* Concatenate " (" */ 223 UINT cchWritten = wcslen(pwszResult); 224 LPWSTR pwszEnd = pwszResult + cchWritten; 225 size_t cchRemaining = cchResultMax - cchWritten; 226 StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0); 227 228 /* Write formated bytes count */ 229 cchWritten = SH_FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining); 230 pwszEnd += cchWritten; 231 cchRemaining -= cchWritten; 232 233 /* Copy ")" to the buffer */ 234 StringCchCopyW(pwszEnd, cchRemaining, L")"); 235 236 return pwszResult; 237 } 238 239 /************************************************************************* 240 * 241 * SH_CreatePropertySheetPage [Internal] 242 * 243 * creates a property sheet page from a resource id 244 * 245 */ 246 247 HPROPSHEETPAGE 248 SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle) 249 { 250 PROPSHEETPAGEW Page; 251 252 memset(&Page, 0x0, sizeof(PROPSHEETPAGEW)); 253 Page.dwSize = sizeof(PROPSHEETPAGEW); 254 Page.dwFlags = PSP_DEFAULT; 255 Page.hInstance = shell32_hInstance; 256 Page.pszTemplate = MAKEINTRESOURCE(wDialogId); 257 Page.pfnDlgProc = pfnDlgProc; 258 Page.lParam = lParam; 259 Page.pszTitle = pwszTitle; 260 261 if (pwszTitle) 262 Page.dwFlags |= PSP_USETITLE; 263 264 return CreatePropertySheetPageW(&Page); 265 } 266 267 VOID 268 CFileDefExt::InitOpensWithField(HWND hwndDlg) 269 { 270 WCHAR wszBuf[MAX_PATH] = L""; 271 WCHAR wszPath[MAX_PATH] = L""; 272 DWORD dwSize = sizeof(wszBuf); 273 BOOL bUnknownApp = TRUE; 274 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 275 276 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS) 277 { 278 bUnknownApp = FALSE; 279 StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command"); 280 dwSize = sizeof(wszPath); 281 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS) 282 { 283 /* Get path from command line */ 284 ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf)); 285 PathRemoveArgs(wszBuf); 286 PathUnquoteSpacesW(wszBuf); 287 PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath)); 288 289 HICON hIcon; 290 if (ExtractIconExW(wszPath, 0, NULL, &hIcon, 1)) 291 { 292 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025); 293 HWND hDescrCtrl = GetDlgItem(hwndDlg, 14007); 294 ShowWindow(hIconCtrl, SW_SHOW); 295 RECT rcIcon, rcDescr; 296 GetWindowRect(hIconCtrl, &rcIcon); 297 298 rcIcon.right = rcIcon.left + GetSystemMetrics(SM_CXSMICON); 299 rcIcon.bottom = rcIcon.top + GetSystemMetrics(SM_CYSMICON); 300 301 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2); 302 GetWindowRect(hDescrCtrl, &rcDescr); 303 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2); 304 INT cxOffset = rcIcon.right + 2 - rcDescr.left; 305 SetWindowPos(hDescrCtrl, NULL, 306 rcDescr.left + cxOffset, rcDescr.top, 307 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top, 308 SWP_NOZORDER); 309 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0); 310 } else 311 ERR("Failed to extract icon\n"); 312 313 if (PathFileExistsW(wszPath)) 314 { 315 /* Get file description */ 316 CFileVersionInfo VerInfo; 317 VerInfo.Load(wszPath); 318 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription"); 319 if (pwszDescr) 320 SetDlgItemTextW(hwndDlg, 14007, pwszDescr); 321 else 322 { 323 /* File has no description - display filename */ 324 LPWSTR pwszFilename = PathFindFileNameW(wszPath); 325 PathRemoveExtension(pwszFilename); 326 pwszFilename[0] = towupper(pwszFilename[0]); 327 SetDlgItemTextW(hwndDlg, 14007, pwszFilename); 328 } 329 } 330 else 331 bUnknownApp = TRUE; 332 } else 333 WARN("RegGetValueW %ls failed\n", wszBuf); 334 } else 335 WARN("RegGetValueW %ls failed\n", pwszExt); 336 337 if (bUnknownApp) 338 { 339 /* Unknown application */ 340 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf)); 341 SetDlgItemTextW(hwndDlg, 14007, wszBuf); 342 } 343 } 344 345 /************************************************************************* 346 * 347 * SH_FileGeneralFileType [Internal] 348 * 349 * retrieves file extension description from registry and sets it in dialog 350 * 351 * TODO: retrieve file extension default icon and load it 352 * find executable name from registry, retrieve description from executable 353 */ 354 355 BOOL 356 CFileDefExt::InitFileType(HWND hwndDlg) 357 { 358 TRACE("path %s\n", debugstr_w(m_wszPath)); 359 360 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005); 361 if (hDlgCtrl == NULL) 362 return FALSE; 363 364 /* Get file information */ 365 SHFILEINFOW fi; 366 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON)) 367 { 368 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError()); 369 fi.szTypeName[0] = L'\0'; 370 fi.hIcon = NULL; 371 } 372 373 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 374 if (pwszExt[0]) 375 { 376 WCHAR wszBuf[256]; 377 378 if (!fi.szTypeName[0]) 379 { 380 /* The file type is unknown, so default to string "FileExtension File" */ 381 size_t cchRemaining = 0; 382 LPWSTR pwszEnd = NULL; 383 384 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1); 385 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd); 386 387 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf); 388 } 389 else 390 { 391 /* Update file type */ 392 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt); 393 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf); 394 } 395 } 396 397 /* Update file icon */ 398 if (fi.hIcon) 399 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0); 400 else 401 ERR("No icon %ls\n", m_wszPath); 402 403 return TRUE; 404 } 405 406 /************************************************************************* 407 * 408 * CFileDefExt::InitFilePath [Internal] 409 * 410 * sets file path string and filename string 411 * 412 */ 413 414 BOOL 415 CFileDefExt::InitFilePath(HWND hwndDlg) 416 { 417 /* Find the filename */ 418 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath); 419 420 if (pwszFilename > m_wszPath) 421 { 422 /* Location field */ 423 WCHAR wszLocation[MAX_PATH]; 424 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath); 425 PathRemoveBackslashW(wszLocation); 426 427 SetDlgItemTextW(hwndDlg, 14009, wszLocation); 428 } 429 430 /* Filename field */ 431 SetDlgItemTextW(hwndDlg, 14001, pwszFilename); 432 433 return TRUE; 434 } 435 436 /************************************************************************* 437 * 438 * CFileDefExt::GetFileTimeString [Internal] 439 * 440 * formats a given LPFILETIME struct into readable user format 441 */ 442 443 BOOL 444 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult) 445 { 446 FILETIME ft; 447 SYSTEMTIME st; 448 449 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st)) 450 return FALSE; 451 452 size_t cchRemaining = cchResult; 453 LPWSTR pwszEnd = pwszResult; 454 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining); 455 if (cchWritten) 456 --cchWritten; // GetDateFormatW returns count with terminating zero 457 else 458 ERR("GetDateFormatW failed\n"); 459 cchRemaining -= cchWritten; 460 pwszEnd += cchWritten; 461 462 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0); 463 464 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining); 465 if (cchWritten) 466 --cchWritten; // GetTimeFormatW returns count with terminating zero 467 else 468 ERR("GetTimeFormatW failed\n"); 469 TRACE("result %s\n", debugstr_w(pwszResult)); 470 return TRUE; 471 } 472 473 /************************************************************************* 474 * 475 * CFileDefExt::InitFileAttr [Internal] 476 * 477 * retrieves file information from file and sets in dialog 478 * 479 */ 480 481 BOOL 482 CFileDefExt::InitFileAttr(HWND hwndDlg) 483 { 484 BOOL Success; 485 WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA 486 WCHAR wszBuf[MAX_PATH]; 487 488 TRACE("InitFileAttr %ls\n", m_wszPath); 489 490 /* 491 * There are situations where GetFileAttributes(Ex) can fail even if the 492 * specified path represents a file. This happens when e.g. the file is a 493 * locked system file, such as C:\pagefile.sys . In this case, the function 494 * returns INVALID_FILE_ATTRIBUTES and GetLastError returns ERROR_SHARING_VIOLATION. 495 * (this would allow us to distinguish between this failure and a failure 496 * due to the fact that the path actually refers to a directory). 497 * 498 * Because we really want to retrieve the file attributes/size/date&time, 499 * we do the following trick: 500 * - First we call GetFileAttributesEx. If it succeeds we know we have 501 * a file or a directory, and we have retrieved its attributes. 502 * - If GetFileAttributesEx fails, we call FindFirstFile on the full path. 503 * While we could have called FindFirstFile at first and skip GetFileAttributesEx 504 * altogether, we do it after GetFileAttributesEx because it performs more 505 * work to retrieve the file attributes. However it actually works even 506 * for locked system files. 507 * - If FindFirstFile succeeds we have retrieved its attributes. 508 * - Otherwise (FindFirstFile has failed), we do not retrieve anything. 509 * 510 * The following code also relies on the fact that the first 6 members 511 * of WIN32_FIND_DATA are *exactly* the same as the WIN32_FILE_ATTRIBUTE_DATA 512 * structure. Therefore it is safe to use a single WIN32_FIND_DATA 513 * structure for both the GetFileAttributesEx and FindFirstFile calls. 514 */ 515 516 Success = GetFileAttributesExW(m_wszPath, 517 GetFileExInfoStandard, 518 (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo); 519 if (!Success) 520 { 521 HANDLE hFind = FindFirstFileW(m_wszPath, &FileInfo); 522 Success = (hFind != INVALID_HANDLE_VALUE); 523 if (Success) 524 FindClose(hFind); 525 } 526 527 if (Success) 528 { 529 /* Update attribute checkboxes */ 530 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) 531 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0); 532 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 533 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0); 534 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) 535 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0); 536 537 /* Update creation time */ 538 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf))) 539 SetDlgItemTextW(hwndDlg, 14015, wszBuf); 540 541 /* For files display last access and last write time */ 542 if (!m_bDir) 543 { 544 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf))) 545 SetDlgItemTextW(hwndDlg, 14019, wszBuf); 546 547 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf))) 548 SetDlgItemTextW(hwndDlg, 14017, wszBuf); 549 550 /* Update size of file */ 551 ULARGE_INTEGER FileSize; 552 FileSize.u.LowPart = FileInfo.nFileSizeLow; 553 FileSize.u.HighPart = FileInfo.nFileSizeHigh; 554 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf))) 555 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 556 } 557 } 558 559 if (m_bDir) 560 { 561 /* For directories files have to be counted */ 562 563 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData))); 564 data->This = this; 565 data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * MAX_PATH)); 566 data->cchBufMax = MAX_PATH; 567 data->hwndDlg = hwndDlg; 568 this->AddRef(); 569 StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath); 570 571 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL); 572 573 /* Update size field */ 574 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 575 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 576 577 /* Display files and folders count */ 578 WCHAR wszFormat[256]; 579 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 580 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 581 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 582 } 583 584 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */ 585 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE); 586 587 return TRUE; 588 } 589 590 /************************************************************************* 591 * 592 * CFileDefExt::InitGeneralPage [Internal] 593 * 594 * sets all file general properties in dialog 595 */ 596 597 BOOL 598 CFileDefExt::InitGeneralPage(HWND hwndDlg) 599 { 600 /* Set general text properties filename filelocation and icon */ 601 InitFilePath(hwndDlg); 602 603 /* Set file type and icon */ 604 InitFileType(hwndDlg); 605 606 /* Set open with application */ 607 if (!m_bDir) 608 { 609 if (!PathIsExeW(m_wszPath)) 610 InitOpensWithField(hwndDlg); 611 else 612 { 613 WCHAR wszBuf[MAX_PATH]; 614 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf)); 615 SetDlgItemTextW(hwndDlg, 14006, wszBuf); 616 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE); 617 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription"); 618 if (pwszDescr) 619 SetDlgItemTextW(hwndDlg, 14007, pwszDescr); 620 else 621 { 622 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath)); 623 PathRemoveExtension(wszBuf); 624 SetDlgItemTextW(hwndDlg, 14007, wszBuf); 625 } 626 } 627 } 628 629 /* Set file created/modfied/accessed time, size and attributes */ 630 InitFileAttr(hwndDlg); 631 632 return TRUE; 633 } 634 635 /************************************************************************* 636 * 637 * CFileDefExt::GeneralPageProc 638 * 639 * wnd proc of 'General' property sheet page 640 * 641 */ 642 643 INT_PTR CALLBACK 644 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 645 { 646 switch (uMsg) 647 { 648 case WM_INITDIALOG: 649 { 650 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam; 651 652 if (ppsp == NULL || !ppsp->lParam) 653 break; 654 655 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam); 656 657 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam); 658 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt); 659 pFileDefExt->InitGeneralPage(hwndDlg); 660 break; 661 } 662 case WM_COMMAND: 663 if (LOWORD(wParam) == 14024) /* Opens With - Change */ 664 { 665 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 666 OPENASINFO oainfo; 667 oainfo.pcszFile = pFileDefExt->m_wszPath; 668 oainfo.pcszClass = NULL; 669 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION; 670 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo)); 671 } 672 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */ 673 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 674 else if (LOWORD(wParam) == 14001) /* Name */ 675 { 676 if (HIWORD(wParam) == EN_CHANGE) 677 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 678 } 679 break; 680 case WM_NOTIFY: 681 { 682 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam; 683 if (lppsn->hdr.code == PSN_APPLY) 684 { 685 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER)); 686 687 /* Update attributes first */ 688 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath); 689 if (dwAttr) 690 { 691 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE); 692 693 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0)) 694 dwAttr |= FILE_ATTRIBUTE_READONLY; 695 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0)) 696 dwAttr |= FILE_ATTRIBUTE_HIDDEN; 697 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0)) 698 dwAttr |= FILE_ATTRIBUTE_ARCHIVE; 699 700 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr)) 701 ERR("SetFileAttributesW failed\n"); 702 } 703 704 /* Update filename now */ 705 WCHAR wszBuf[MAX_PATH]; 706 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath); 707 LPWSTR pwszFilename = PathFindFileNameW(wszBuf); 708 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf); 709 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax)) 710 { 711 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf)) 712 ERR("MoveFileW failed\n"); 713 } 714 715 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR); 716 return TRUE; 717 } 718 break; 719 } 720 default: 721 break; 722 } 723 724 return FALSE; 725 } 726 727 /************************************************************************* 728 * 729 * CFileDefExt::InitVersionPage [Internal] 730 * 731 * sets all file version properties in dialog 732 */ 733 734 BOOL 735 CFileDefExt::InitVersionPage(HWND hwndDlg) 736 { 737 /* Get fixed info */ 738 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo(); 739 if (pInfo) 740 { 741 WCHAR wszVersion[256]; 742 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS), 743 LOWORD(pInfo->dwFileVersionMS), 744 HIWORD(pInfo->dwFileVersionLS), 745 LOWORD(pInfo->dwFileVersionLS)); 746 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion)); 747 SetDlgItemTextW(hwndDlg, 14001, wszVersion); 748 } 749 750 /* Update labels */ 751 SetVersionLabel(hwndDlg, 14003, L"FileDescription"); 752 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright"); 753 754 /* Add items to listbox */ 755 AddVersionString(hwndDlg, L"CompanyName"); 756 LPCWSTR pwszLang = m_VerInfo.GetLangName(); 757 if (pwszLang) 758 { 759 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 760 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language"); 761 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang); 762 } 763 AddVersionString(hwndDlg, L"ProductName"); 764 AddVersionString(hwndDlg, L"InternalName"); 765 AddVersionString(hwndDlg, L"OriginalFilename"); 766 AddVersionString(hwndDlg, L"FileVersion"); 767 AddVersionString(hwndDlg, L"ProductVersion"); 768 769 /* Attach file version to dialog window */ 770 SetWindowLongPtr(hwndDlg, DWL_USER, (LONG_PTR)this); 771 772 /* Select first item */ 773 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 774 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0); 775 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL); 776 if (pwszText && pwszText != (LPCWSTR)LB_ERR) 777 SetDlgItemTextW(hwndDlg, 14010, pwszText); 778 779 return TRUE; 780 } 781 782 /************************************************************************* 783 * 784 * CFileDefExt::SetVersionLabel [Internal] 785 * 786 * retrieves a version string and uses it to set label text 787 */ 788 789 BOOL 790 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName) 791 { 792 if (hwndDlg == NULL || pwszName == NULL) 793 return FALSE; 794 795 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName); 796 if (pwszValue) 797 { 798 /* file description property */ 799 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue)); 800 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue); 801 return TRUE; 802 } 803 804 return FALSE; 805 } 806 807 /************************************************************************* 808 * 809 * CFileDefExt::AddVersionString [Internal] 810 * 811 * retrieves a version string and adds it to listbox 812 */ 813 814 BOOL 815 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName) 816 { 817 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg); 818 819 if (hwndDlg == NULL || pwszName == NULL) 820 return FALSE; 821 822 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName); 823 if (pwszValue) 824 { 825 /* listbox name property */ 826 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009); 827 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue)); 828 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName); 829 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue); 830 return TRUE; 831 } 832 833 return FALSE; 834 } 835 836 /************************************************************************* 837 * 838 * CFileDefExt::VersionPageProc 839 * 840 * wnd proc of 'Version' property sheet page 841 */ 842 843 INT_PTR CALLBACK 844 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 845 { 846 switch (uMsg) 847 { 848 case WM_INITDIALOG: 849 { 850 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam; 851 852 if (ppsp == NULL || !ppsp->lParam) 853 break; 854 855 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam); 856 857 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam); 858 return pFileDefExt->InitVersionPage(hwndDlg); 859 } 860 case WM_COMMAND: 861 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE) 862 { 863 HWND hDlgCtrl = (HWND)lParam; 864 865 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL); 866 if (Index == LB_ERR) 867 break; 868 869 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL); 870 if (pwszData == NULL) 871 break; 872 873 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(pwszData)); 874 SetDlgItemTextW(hwndDlg, 14010, pwszData); 875 876 return TRUE; 877 } 878 break; 879 case WM_DESTROY: 880 break; 881 default: 882 break; 883 } 884 885 return FALSE; 886 } 887 888 CFileDefExt::CFileDefExt(): 889 m_bDir(FALSE), m_cFiles(0), m_cFolders(0) 890 { 891 m_wszPath[0] = L'\0'; 892 m_DirSize.QuadPart = 0ull; 893 } 894 895 CFileDefExt::~CFileDefExt() 896 { 897 898 } 899 900 HRESULT WINAPI 901 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID) 902 { 903 FORMATETC format; 904 STGMEDIUM stgm; 905 HRESULT hr; 906 907 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID); 908 909 if (!pDataObj) 910 return E_FAIL; 911 912 format.cfFormat = CF_HDROP; 913 format.ptd = NULL; 914 format.dwAspect = DVASPECT_CONTENT; 915 format.lindex = -1; 916 format.tymed = TYMED_HGLOBAL; 917 918 hr = pDataObj->GetData(&format, &stgm); 919 if (FAILED(hr)) 920 return hr; 921 922 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath))) 923 { 924 ERR("DragQueryFileW failed\n"); 925 ReleaseStgMedium(&stgm); 926 return E_FAIL; 927 } 928 929 ReleaseStgMedium(&stgm); 930 931 TRACE("File properties %ls\n", m_wszPath); 932 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE; 933 if (!m_bDir) 934 m_VerInfo.Load(m_wszPath); 935 936 return S_OK; 937 } 938 939 HRESULT WINAPI 940 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 941 { 942 UNIMPLEMENTED; 943 return E_NOTIMPL; 944 } 945 946 HRESULT WINAPI 947 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 948 { 949 UNIMPLEMENTED; 950 return E_NOTIMPL; 951 } 952 953 HRESULT WINAPI 954 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) 955 { 956 UNIMPLEMENTED; 957 return E_NOTIMPL; 958 } 959 960 HRESULT WINAPI 961 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) 962 { 963 HPROPSHEETPAGE hPage; 964 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES; 965 966 hPage = SH_CreatePropertySheetPage(wResId, 967 GeneralPageProc, 968 (LPARAM)this, 969 NULL); 970 if (hPage) 971 pfnAddPage(hPage, lParam); 972 973 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL)) 974 { 975 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION, 976 VersionPageProc, 977 (LPARAM)this, 978 NULL); 979 if (hPage) 980 pfnAddPage(hPage, lParam); 981 } 982 983 return S_OK; 984 } 985 986 HRESULT WINAPI 987 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) 988 { 989 UNIMPLEMENTED; 990 return E_NOTIMPL; 991 } 992 993 HRESULT WINAPI 994 CFileDefExt::SetSite(IUnknown *punk) 995 { 996 UNIMPLEMENTED; 997 return E_NOTIMPL; 998 } 999 1000 HRESULT WINAPI 1001 CFileDefExt::GetSite(REFIID iid, void **ppvSite) 1002 { 1003 UNIMPLEMENTED; 1004 return E_NOTIMPL; 1005 } 1006 1007 DWORD WINAPI 1008 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter) 1009 { 1010 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter); 1011 DWORD ticks = 0; 1012 data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, data->cchBufMax, &ticks); 1013 1014 //Release the CFileDefExt and data object holds in the copying thread. 1015 data->This->Release(); 1016 HeapFree(GetProcessHeap(), 0, data->pwszBuf); 1017 HeapFree(GetProcessHeap(), 0, data); 1018 1019 return 0; 1020 } 1021 1022 BOOL 1023 CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPWSTR pwszBuf, UINT cchBufMax, DWORD *ticks) 1024 { 1025 /* Find filename position */ 1026 UINT cchBuf = wcslen(pwszBuf); 1027 WCHAR *pwszFilename = pwszBuf + cchBuf; 1028 size_t cchFilenameMax = cchBufMax - cchBuf; 1029 if (!cchFilenameMax) 1030 return FALSE; 1031 *(pwszFilename++) = '\\'; 1032 --cchFilenameMax; 1033 1034 /* Find all files, FIXME: shouldn't be "*"? */ 1035 StringCchCopyW(pwszFilename, cchFilenameMax, L"*"); 1036 1037 WIN32_FIND_DATAW wfd; 1038 HANDLE hFind = FindFirstFileW(pwszBuf, &wfd); 1039 if (hFind == INVALID_HANDLE_VALUE) 1040 { 1041 ERR("FindFirstFileW %ls failed\n", pwszBuf); 1042 return FALSE; 1043 } 1044 1045 BOOL root = FALSE; 1046 if (*ticks == 0) { 1047 *ticks = GetTickCount(); 1048 root = TRUE; 1049 } 1050 1051 do 1052 { 1053 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 1054 { 1055 /* Don't process "." and ".." items */ 1056 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L"..")) 1057 continue; 1058 1059 ++m_cFolders; 1060 1061 StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName); 1062 CountFolderAndFiles(hwndDlg, pwszBuf, cchBufMax, ticks); 1063 } 1064 else 1065 { 1066 m_cFiles++; 1067 1068 ULARGE_INTEGER FileSize; 1069 FileSize.u.LowPart = wfd.nFileSizeLow; 1070 FileSize.u.HighPart = wfd.nFileSizeHigh; 1071 m_DirSize.QuadPart += FileSize.QuadPart; 1072 } 1073 if (GetTickCount() - *ticks > (DWORD) 300) 1074 { 1075 /* FIXME Using IsWindow is generally ill advised */ 1076 if (IsWindow(hwndDlg)) 1077 { 1078 WCHAR wszBuf[MAX_PATH]; 1079 1080 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 1081 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 1082 1083 /* Display files and folders count */ 1084 WCHAR wszFormat[256]; 1085 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 1086 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 1087 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 1088 *ticks = GetTickCount(); 1089 } 1090 else 1091 break; 1092 } 1093 } while(FindNextFileW(hFind, &wfd)); 1094 1095 if (root && IsWindow(hwndDlg)) 1096 { 1097 WCHAR wszBuf[MAX_PATH]; 1098 1099 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf))) 1100 SetDlgItemTextW(hwndDlg, 14011, wszBuf); 1101 1102 /* Display files and folders count */ 1103 WCHAR wszFormat[256]; 1104 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat)); 1105 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders); 1106 SetDlgItemTextW(hwndDlg, 14027, wszBuf); 1107 } 1108 1109 FindClose(hFind); 1110 return TRUE; 1111 } 1112