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
GetPhysicalFileSize(LPCWSTR PathBuffer,PULARGE_INTEGER Size)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
Load(LPCWSTR pwszPath)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
GetString(LPCWSTR pwszName)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
GetFixedInfo()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
GetLangName()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
SH_FormatInteger(LONGLONG Num,LPWSTR pwszResult,UINT cchResultMax)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
SH_FormatByteSize(LONGLONG cbSize,LPWSTR pwszResult,UINT cchResultMax)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
SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize,LPWSTR pwszResult,UINT cchResultMax)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 VOID
InitOpensWithField(HWND hwndDlg)294 CFileDefExt::InitOpensWithField(HWND hwndDlg)
295 {
296 WCHAR wszBuf[MAX_PATH] = L"";
297 WCHAR wszPath[MAX_PATH] = L"";
298 DWORD dwSize = sizeof(wszBuf);
299 BOOL bUnknownApp = TRUE;
300 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
301
302 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS)
303 {
304 bUnknownApp = FALSE;
305 StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command");
306 dwSize = sizeof(wszPath);
307 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS)
308 {
309 /* Get path from command line */
310 ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf));
311 PathRemoveArgs(wszBuf);
312 PathUnquoteSpacesW(wszBuf);
313 PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath));
314
315 HICON hIcon;
316 if (ExtractIconExW(wszPath, 0, NULL, &hIcon, 1))
317 {
318 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025);
319 HWND hDescrCtrl = GetDlgItem(hwndDlg, 14007);
320 ShowWindow(hIconCtrl, SW_SHOW);
321 RECT rcIcon, rcDescr;
322 GetWindowRect(hIconCtrl, &rcIcon);
323
324 rcIcon.right = rcIcon.left + GetSystemMetrics(SM_CXSMICON);
325 rcIcon.bottom = rcIcon.top + GetSystemMetrics(SM_CYSMICON);
326
327 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2);
328 GetWindowRect(hDescrCtrl, &rcDescr);
329 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2);
330 INT cxOffset = rcIcon.right + 2 - rcDescr.left;
331 SetWindowPos(hDescrCtrl, NULL,
332 rcDescr.left + cxOffset, rcDescr.top,
333 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top,
334 SWP_NOZORDER);
335 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0);
336 } else
337 ERR("Failed to extract icon\n");
338
339 if (PathFileExistsW(wszPath))
340 {
341 /* Get file description */
342 CFileVersionInfo VerInfo;
343 VerInfo.Load(wszPath);
344 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription");
345 if (pwszDescr)
346 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
347 else
348 {
349 /* File has no description - display filename */
350 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
351 PathRemoveExtension(pwszFilename);
352 pwszFilename[0] = towupper(pwszFilename[0]);
353 SetDlgItemTextW(hwndDlg, 14007, pwszFilename);
354 }
355 }
356 else
357 bUnknownApp = TRUE;
358 } else
359 WARN("RegGetValueW %ls failed\n", wszBuf);
360 } else
361 WARN("RegGetValueW %ls failed\n", pwszExt);
362
363 if (bUnknownApp)
364 {
365 /* Unknown application */
366 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf));
367 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
368 }
369 }
370
371 /*************************************************************************
372 *
373 * SH_FileGeneralFileType [Internal]
374 *
375 * retrieves file extension description from registry and sets it in dialog
376 *
377 * TODO: retrieve file extension default icon and load it
378 * find executable name from registry, retrieve description from executable
379 */
380
381 BOOL
InitFileType(HWND hwndDlg)382 CFileDefExt::InitFileType(HWND hwndDlg)
383 {
384 TRACE("path %s\n", debugstr_w(m_wszPath));
385
386 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005);
387 if (hDlgCtrl == NULL)
388 return FALSE;
389
390 /* Get file information */
391 SHFILEINFOW fi;
392 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
393 {
394 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError());
395 fi.szTypeName[0] = L'\0';
396 fi.hIcon = NULL;
397 }
398
399 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
400 if (pwszExt[0])
401 {
402 WCHAR wszBuf[256];
403
404 if (!fi.szTypeName[0])
405 {
406 /* The file type is unknown, so default to string "FileExtension File" */
407 size_t cchRemaining = 0;
408 LPWSTR pwszEnd = NULL;
409
410 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1);
411 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd);
412
413 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
414 }
415 else
416 {
417 /* Update file type */
418 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt);
419 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
420 }
421 }
422
423 /* Update file icon */
424 if (fi.hIcon)
425 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
426 else
427 ERR("No icon %ls\n", m_wszPath);
428
429 return TRUE;
430 }
431
432 /*************************************************************************
433 *
434 * CFileDefExt::InitFilePath [Internal]
435 *
436 * sets file path string and filename string
437 *
438 */
439
440 BOOL
InitFilePath(HWND hwndDlg)441 CFileDefExt::InitFilePath(HWND hwndDlg)
442 {
443 /* Find the filename */
444 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath);
445
446 if (pwszFilename > m_wszPath)
447 {
448 /* Location field */
449 WCHAR wszLocation[MAX_PATH];
450 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath);
451 PathRemoveBackslashW(wszLocation);
452
453 SetDlgItemTextW(hwndDlg, 14009, wszLocation);
454 }
455
456 /* Filename field */
457 SetDlgItemTextW(hwndDlg, 14001, pwszFilename);
458
459 return TRUE;
460 }
461
462 /*************************************************************************
463 *
464 * CFileDefExt::GetFileTimeString [Internal]
465 *
466 * formats a given LPFILETIME struct into readable user format
467 */
468
469 BOOL
GetFileTimeString(LPFILETIME lpFileTime,LPWSTR pwszResult,UINT cchResult)470 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
471 {
472 FILETIME ft;
473 SYSTEMTIME st;
474
475 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
476 return FALSE;
477
478 size_t cchRemaining = cchResult;
479 LPWSTR pwszEnd = pwszResult;
480 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
481 if (cchWritten)
482 --cchWritten; // GetDateFormatW returns count with terminating zero
483 else
484 ERR("GetDateFormatW failed\n");
485 cchRemaining -= cchWritten;
486 pwszEnd += cchWritten;
487
488 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
489
490 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
491 if (cchWritten)
492 --cchWritten; // GetTimeFormatW returns count with terminating zero
493 else
494 ERR("GetTimeFormatW failed\n");
495 TRACE("result %s\n", debugstr_w(pwszResult));
496 return TRUE;
497 }
498
499 /*************************************************************************
500 *
501 * CFileDefExt::InitFileAttr [Internal]
502 *
503 * retrieves file information from file and sets in dialog
504 *
505 */
506
507 BOOL
InitFileAttr(HWND hwndDlg)508 CFileDefExt::InitFileAttr(HWND hwndDlg)
509 {
510 BOOL Success;
511 WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
512 WCHAR wszBuf[MAX_PATH];
513
514 TRACE("InitFileAttr %ls\n", m_wszPath);
515
516 /*
517 * There are situations where GetFileAttributes(Ex) can fail even if the
518 * specified path represents a file. This happens when e.g. the file is a
519 * locked system file, such as C:\pagefile.sys . In this case, the function
520 * returns INVALID_FILE_ATTRIBUTES and GetLastError returns ERROR_SHARING_VIOLATION.
521 * (this would allow us to distinguish between this failure and a failure
522 * due to the fact that the path actually refers to a directory).
523 *
524 * Because we really want to retrieve the file attributes/size/date&time,
525 * we do the following trick:
526 * - First we call GetFileAttributesEx. If it succeeds we know we have
527 * a file or a directory, and we have retrieved its attributes.
528 * - If GetFileAttributesEx fails, we call FindFirstFile on the full path.
529 * While we could have called FindFirstFile at first and skip GetFileAttributesEx
530 * altogether, we do it after GetFileAttributesEx because it performs more
531 * work to retrieve the file attributes. However it actually works even
532 * for locked system files.
533 * - If FindFirstFile succeeds we have retrieved its attributes.
534 * - Otherwise (FindFirstFile has failed), we do not retrieve anything.
535 *
536 * The following code also relies on the fact that the first 6 members
537 * of WIN32_FIND_DATA are *exactly* the same as the WIN32_FILE_ATTRIBUTE_DATA
538 * structure. Therefore it is safe to use a single WIN32_FIND_DATA
539 * structure for both the GetFileAttributesEx and FindFirstFile calls.
540 */
541
542 Success = GetFileAttributesExW(m_wszPath,
543 GetFileExInfoStandard,
544 (LPWIN32_FILE_ATTRIBUTE_DATA)&FileInfo);
545 if (!Success)
546 {
547 HANDLE hFind = FindFirstFileW(m_wszPath, &FileInfo);
548 Success = (hFind != INVALID_HANDLE_VALUE);
549 if (Success)
550 FindClose(hFind);
551 }
552
553 if (Success)
554 {
555 /* Update attribute checkboxes */
556 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
557 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0);
558 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
559 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0);
560 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
561 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0);
562
563 /* Update creation time */
564 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf)))
565 SetDlgItemTextW(hwndDlg, 14015, wszBuf);
566
567 /* For files display last access and last write time */
568 if (!m_bDir)
569 {
570 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf)))
571 SetDlgItemTextW(hwndDlg, 14019, wszBuf);
572
573 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf)))
574 SetDlgItemTextW(hwndDlg, 14017, wszBuf);
575
576 /* Update size of file */
577 ULARGE_INTEGER FileSize;
578 FileSize.u.LowPart = FileInfo.nFileSizeLow;
579 FileSize.u.HighPart = FileInfo.nFileSizeHigh;
580 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)))
581 {
582 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
583
584 // Compute file on disk. If fails, use logical size
585 if (GetPhysicalFileSize(m_wszPath, &FileSize))
586 SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf));
587 else
588 ERR("Unreliable size on disk\n");
589
590 SetDlgItemTextW(hwndDlg, 14012, wszBuf);
591 }
592 }
593 }
594
595 if (m_bDir)
596 {
597 /* For directories files have to be counted */
598
599 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData)));
600 data->This = this;
601 data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR) * MAX_PATH));
602 data->hwndDlg = hwndDlg;
603 this->AddRef();
604 StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath);
605
606 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL);
607
608 /* Update size field */
609 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
610 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
611
612 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf)))
613 SetDlgItemTextW(hwndDlg, 14012, wszBuf);
614
615 /* Display files and folders count */
616 WCHAR wszFormat[256];
617 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
618 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
619 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
620 }
621
622 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
623 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE);
624
625 return TRUE;
626 }
627
628 /*************************************************************************
629 *
630 * CFileDefExt::InitGeneralPage [Internal]
631 *
632 * sets all file general properties in dialog
633 */
634
635 BOOL
InitGeneralPage(HWND hwndDlg)636 CFileDefExt::InitGeneralPage(HWND hwndDlg)
637 {
638 /* Set general text properties filename filelocation and icon */
639 InitFilePath(hwndDlg);
640
641 /* Set file type and icon */
642 InitFileType(hwndDlg);
643
644 /* Set open with application */
645 if (!m_bDir)
646 {
647 if (!PathIsExeW(m_wszPath))
648 InitOpensWithField(hwndDlg);
649 else
650 {
651 WCHAR wszBuf[MAX_PATH];
652 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
653 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
654 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
655
656 /* hidden button 14024 allows to draw edit 14007 larger than defined in resources , we use edit 14009 as idol */
657 RECT rectIdol, rectToAdjust;
658 GetClientRect(GetDlgItem(hwndDlg, 14009), &rectIdol);
659 GetClientRect(GetDlgItem(hwndDlg, 14007), &rectToAdjust);
660 SetWindowPos(GetDlgItem(hwndDlg, 14007), HWND_TOP, 0, 0,
661 rectIdol.right-rectIdol.left /* make it as wide as its idol */,
662 rectToAdjust.bottom-rectToAdjust.top /* but keep its current height */,
663 SWP_NOMOVE | SWP_NOZORDER );
664
665 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
666 if (pwszDescr)
667 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
668 else
669 {
670 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
671 PathRemoveExtension(wszBuf);
672 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
673 }
674 }
675 }
676
677 /* Set file created/modfied/accessed time, size and attributes */
678 InitFileAttr(hwndDlg);
679
680 return TRUE;
681 }
682
683 /*************************************************************************
684 *
685 * CFileDefExt::GeneralPageProc
686 *
687 * wnd proc of 'General' property sheet page
688 *
689 */
690
691 INT_PTR CALLBACK
GeneralPageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)692 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
693 {
694 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
695 switch (uMsg)
696 {
697 case WM_INITDIALOG:
698 {
699 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
700
701 if (ppsp == NULL || !ppsp->lParam)
702 break;
703
704 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam);
705
706 pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
707 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt);
708 pFileDefExt->InitGeneralPage(hwndDlg);
709 break;
710 }
711 case WM_COMMAND:
712 if (LOWORD(wParam) == 14024) /* Opens With - Change */
713 {
714 OPENASINFO oainfo;
715 oainfo.pcszFile = pFileDefExt->m_wszPath;
716 oainfo.pcszClass = NULL;
717 oainfo.oaifInFlags = OAIF_REGISTER_EXT | OAIF_FORCE_REGISTRATION;
718 if (SHOpenWithDialog(hwndDlg, &oainfo) == S_OK)
719 {
720 pFileDefExt->InitGeneralPage(hwndDlg);
721 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
722 }
723 break;
724 }
725 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */
726 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
727 else if (LOWORD(wParam) == 14001) /* Name */
728 {
729 if (HIWORD(wParam) == EN_CHANGE)
730 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
731 }
732 break;
733 case WM_NOTIFY:
734 {
735 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
736 if (lppsn->hdr.code == PSN_APPLY)
737 {
738 /* Update attributes first */
739 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath);
740 if (dwAttr)
741 {
742 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE);
743
744 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0))
745 dwAttr |= FILE_ATTRIBUTE_READONLY;
746 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0))
747 dwAttr |= FILE_ATTRIBUTE_HIDDEN;
748 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0))
749 dwAttr |= FILE_ATTRIBUTE_ARCHIVE;
750
751 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr))
752 ERR("SetFileAttributesW failed\n");
753 }
754
755 /* Update filename now */
756 WCHAR wszBuf[MAX_PATH];
757 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath);
758 LPWSTR pwszFilename = PathFindFileNameW(wszBuf);
759 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf);
760 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax))
761 {
762 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf))
763 ERR("MoveFileW failed\n");
764 }
765
766 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
767 return TRUE;
768 }
769 break;
770 }
771 case PSM_QUERYSIBLINGS:
772 {
773 // reset icon
774 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025);
775 HICON hIcon = (HICON)SendMessageW(hIconCtrl, STM_GETICON, 0, 0);
776 DestroyIcon(hIcon);
777 hIcon = NULL;
778 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0);
779
780 // refresh the page
781 pFileDefExt->InitGeneralPage(hwndDlg);
782 return FALSE; // continue
783 }
784 default:
785 break;
786 }
787
788 return FALSE;
789 }
790
791 /*************************************************************************
792 *
793 * CFileDefExt::InitVersionPage [Internal]
794 *
795 * sets all file version properties in dialog
796 */
797
798 BOOL
InitVersionPage(HWND hwndDlg)799 CFileDefExt::InitVersionPage(HWND hwndDlg)
800 {
801 /* Get fixed info */
802 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo();
803 if (pInfo)
804 {
805 WCHAR wszVersion[256];
806 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS),
807 LOWORD(pInfo->dwFileVersionMS),
808 HIWORD(pInfo->dwFileVersionLS),
809 LOWORD(pInfo->dwFileVersionLS));
810 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion));
811 SetDlgItemTextW(hwndDlg, 14001, wszVersion);
812 }
813
814 /* Update labels */
815 SetVersionLabel(hwndDlg, 14003, L"FileDescription");
816 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright");
817
818 /* Add items to listbox */
819 AddVersionString(hwndDlg, L"CompanyName");
820 LPCWSTR pwszLang = m_VerInfo.GetLangName();
821 if (pwszLang)
822 {
823 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
824 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language");
825 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang);
826 }
827 AddVersionString(hwndDlg, L"ProductName");
828 AddVersionString(hwndDlg, L"InternalName");
829 AddVersionString(hwndDlg, L"OriginalFilename");
830 AddVersionString(hwndDlg, L"FileVersion");
831 AddVersionString(hwndDlg, L"ProductVersion");
832 AddVersionString(hwndDlg, L"Comments");
833 AddVersionString(hwndDlg, L"LegalTrademarks");
834
835 if (pInfo && (pInfo->dwFileFlags & VS_FF_PRIVATEBUILD))
836 AddVersionString(hwndDlg, L"PrivateBuild");
837
838 if (pInfo && (pInfo->dwFileFlags & VS_FF_SPECIALBUILD))
839 AddVersionString(hwndDlg, L"SpecialBuild");
840
841 /* Attach file version to dialog window */
842 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)this);
843
844 /* Select first item */
845 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
846 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0);
847 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL);
848 if (pwszText && pwszText != (LPCWSTR)LB_ERR)
849 SetDlgItemTextW(hwndDlg, 14010, pwszText);
850
851 return TRUE;
852 }
853
854 /*************************************************************************
855 *
856 * CFileDefExt::SetVersionLabel [Internal]
857 *
858 * retrieves a version string and uses it to set label text
859 */
860
861 BOOL
SetVersionLabel(HWND hwndDlg,DWORD idCtrl,LPCWSTR pwszName)862 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName)
863 {
864 if (hwndDlg == NULL || pwszName == NULL)
865 return FALSE;
866
867 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
868 if (pwszValue)
869 {
870 /* file description property */
871 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
872 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue);
873 return TRUE;
874 }
875
876 return FALSE;
877 }
878
879 /*************************************************************************
880 *
881 * CFileDefExt::AddVersionString [Internal]
882 *
883 * retrieves a version string and adds it to listbox
884 */
885
886 BOOL
AddVersionString(HWND hwndDlg,LPCWSTR pwszName)887 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName)
888 {
889 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg);
890
891 if (hwndDlg == NULL || pwszName == NULL)
892 return FALSE;
893
894 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
895 if (pwszValue)
896 {
897 /* listbox name property */
898 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
899 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
900 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName);
901 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue);
902 return TRUE;
903 }
904
905 return FALSE;
906 }
907
908 /*************************************************************************
909 *
910 * CFileDefExt::VersionPageProc
911 *
912 * wnd proc of 'Version' property sheet page
913 */
914
915 INT_PTR CALLBACK
VersionPageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)916 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
917 {
918 switch (uMsg)
919 {
920 case WM_INITDIALOG:
921 {
922 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam;
923
924 if (ppsp == NULL || !ppsp->lParam)
925 break;
926
927 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam);
928
929 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
930 return pFileDefExt->InitVersionPage(hwndDlg);
931 }
932 case WM_COMMAND:
933 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE)
934 {
935 HWND hDlgCtrl = (HWND)lParam;
936
937 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL);
938 if (Index == LB_ERR)
939 break;
940
941 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL);
942 if (pwszData == NULL)
943 break;
944
945 CString str(pwszData);
946 str.Trim();
947
948 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(str));
949 SetDlgItemTextW(hwndDlg, 14010, str);
950
951 return TRUE;
952 }
953 break;
954 case WM_DESTROY:
955 break;
956 case PSM_QUERYSIBLINGS:
957 return FALSE; // continue
958 default:
959 break;
960 }
961
962 return FALSE;
963 }
964
965 /*************************************************************************/
966 /* Folder Customize */
967
968 static const WCHAR s_szShellClassInfo[] = L".ShellClassInfo";
969 static const WCHAR s_szIconIndex[] = L"IconIndex";
970 static const WCHAR s_szIconFile[] = L"IconFile";
971 static const WCHAR s_szIconResource[] = L"IconResource";
972
973 // IDD_FOLDER_CUSTOMIZE
974 INT_PTR CALLBACK
FolderCustomizePageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)975 CFileDefExt::FolderCustomizePageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
976 {
977 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
978 switch (uMsg)
979 {
980 case WM_INITDIALOG:
981 {
982 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam;
983
984 if (ppsp == NULL || !ppsp->lParam)
985 break;
986
987 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam);
988
989 pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
990 return pFileDefExt->InitFolderCustomizePage(hwndDlg);
991 }
992
993 case WM_COMMAND:
994 switch (LOWORD(wParam))
995 {
996 case IDC_FOLDERCUST_CHANGE_ICON:
997 pFileDefExt->OnFolderCustChangeIcon(hwndDlg);
998 break;
999
1000 case IDC_FOLDERCUST_CHOOSE_PIC:
1001 // TODO:
1002 break;
1003
1004 case IDC_FOLDERCUST_RESTORE_DEFAULTS:
1005 // TODO:
1006 break;
1007 }
1008 break;
1009
1010 case WM_NOTIFY:
1011 {
1012 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
1013 if (lppsn->hdr.code == PSN_APPLY)
1014 {
1015 // apply or not
1016 if (pFileDefExt->OnFolderCustApply(hwndDlg))
1017 {
1018 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1019 }
1020 else
1021 {
1022 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
1023 }
1024 return TRUE;
1025 }
1026 break;
1027 }
1028
1029 case PSM_QUERYSIBLINGS:
1030 return FALSE; // continue
1031
1032 case WM_DESTROY:
1033 pFileDefExt->OnFolderCustDestroy(hwndDlg);
1034 break;
1035
1036 default:
1037 break;
1038 }
1039
1040 return FALSE;
1041 }
1042
1043 // IDD_FOLDER_CUSTOMIZE WM_DESTROY
OnFolderCustDestroy(HWND hwndDlg)1044 void CFileDefExt::OnFolderCustDestroy(HWND hwndDlg)
1045 {
1046 ::DestroyIcon(m_hFolderIcon);
1047 m_hFolderIcon = NULL;
1048
1049 /* Detach the object from dialog window */
1050 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)0);
1051 }
1052
UpdateFolderIcon(HWND hwndDlg)1053 void CFileDefExt::UpdateFolderIcon(HWND hwndDlg)
1054 {
1055 // destroy icon if any
1056 if (m_hFolderIcon)
1057 {
1058 ::DestroyIcon(m_hFolderIcon);
1059 m_hFolderIcon = NULL;
1060 }
1061
1062 // create the icon
1063 if (m_szFolderIconPath[0] == 0 && m_nFolderIconIndex == 0)
1064 {
1065 // Windows ignores shell customization here and uses the default icon
1066 m_hFolderIcon = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_FOLDER));
1067 }
1068 else
1069 {
1070 ExtractIconExW(m_szFolderIconPath, m_nFolderIconIndex, &m_hFolderIcon, NULL, 1);
1071 }
1072
1073 // set icon
1074 SendDlgItemMessageW(hwndDlg, IDC_FOLDERCUST_ICON, STM_SETICON, (WPARAM)m_hFolderIcon, 0);
1075 }
1076
1077 // IDD_FOLDER_CUSTOMIZE WM_INITDIALOG
InitFolderCustomizePage(HWND hwndDlg)1078 BOOL CFileDefExt::InitFolderCustomizePage(HWND hwndDlg)
1079 {
1080 /* Attach the object to dialog window */
1081 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)this);
1082
1083 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_COMBOBOX), FALSE);
1084 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_CHECKBOX), FALSE);
1085 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_CHOOSE_PIC), FALSE);
1086 EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDERCUST_RESTORE_DEFAULTS), FALSE);
1087
1088 // build the desktop.ini file path
1089 WCHAR szIniFile[MAX_PATH];
1090 StringCchCopyW(szIniFile, _countof(szIniFile), m_wszPath);
1091 PathAppendW(szIniFile, L"desktop.ini");
1092
1093 // desktop.ini --> m_szFolderIconPath, m_nFolderIconIndex
1094 m_szFolderIconPath[0] = 0;
1095 m_nFolderIconIndex = 0;
1096 if (GetPrivateProfileStringW(s_szShellClassInfo, s_szIconFile, NULL,
1097 m_szFolderIconPath, _countof(m_szFolderIconPath), szIniFile))
1098 {
1099 m_nFolderIconIndex = GetPrivateProfileIntW(s_szShellClassInfo, s_szIconIndex, 0, szIniFile);
1100 }
1101 else if (GetPrivateProfileStringW(s_szShellClassInfo, s_szIconResource, NULL,
1102 m_szFolderIconPath, _countof(m_szFolderIconPath), szIniFile))
1103 {
1104 m_nFolderIconIndex = PathParseIconLocationW(m_szFolderIconPath);
1105 }
1106
1107 // update icon
1108 UpdateFolderIcon(hwndDlg);
1109
1110 return TRUE;
1111 }
1112
1113 // IDD_FOLDER_CUSTOMIZE IDC_FOLDERCUST_CHANGE_ICON
OnFolderCustChangeIcon(HWND hwndDlg)1114 void CFileDefExt::OnFolderCustChangeIcon(HWND hwndDlg)
1115 {
1116 WCHAR szPath[MAX_PATH];
1117 INT nIconIndex;
1118
1119 // m_szFolderIconPath, m_nFolderIconIndex --> szPath, nIconIndex
1120 if (m_szFolderIconPath[0])
1121 {
1122 StringCchCopyW(szPath, _countof(szPath), m_szFolderIconPath);
1123 nIconIndex = m_nFolderIconIndex;
1124 }
1125 else
1126 {
1127 szPath[0] = 0;
1128 nIconIndex = 0;
1129 }
1130
1131 // let the user choose the icon
1132 if (PickIconDlg(hwndDlg, szPath, _countof(szPath), &nIconIndex))
1133 {
1134 // changed
1135 m_bFolderIconIsSet = TRUE;
1136 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1137
1138 // update
1139 StringCchCopyW(m_szFolderIconPath, _countof(m_szFolderIconPath), szPath);
1140 m_nFolderIconIndex = nIconIndex;
1141 UpdateFolderIcon(hwndDlg);
1142 }
1143 }
1144
1145 // IDD_FOLDER_CUSTOMIZE PSN_APPLY
OnFolderCustApply(HWND hwndDlg)1146 BOOL CFileDefExt::OnFolderCustApply(HWND hwndDlg)
1147 {
1148 // build the desktop.ini file path
1149 WCHAR szIniFile[MAX_PATH];
1150 StringCchCopyW(szIniFile, _countof(szIniFile), m_wszPath);
1151 PathAppendW(szIniFile, L"desktop.ini");
1152
1153 if (m_bFolderIconIsSet) // it is set!
1154 {
1155 DWORD attrs;
1156
1157 // change folder attributes (-S -R)
1158 attrs = GetFileAttributesW(m_wszPath);
1159 attrs &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY);
1160 SetFileAttributesW(m_wszPath, attrs);
1161
1162 // change desktop.ini attributes (-S -H -R)
1163 attrs = GetFileAttributesW(szIniFile);
1164 attrs &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
1165 SetFileAttributesW(szIniFile, attrs);
1166
1167 if (m_szFolderIconPath[0])
1168 {
1169 // write IconFile and IconIndex
1170 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconFile, m_szFolderIconPath, szIniFile);
1171
1172 WCHAR szInt[32];
1173 StringCchPrintfW(szInt, _countof(szInt), L"%d", m_nFolderIconIndex);
1174 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconIndex, szInt, szIniFile);
1175
1176 // flush!
1177 WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
1178 }
1179 else
1180 {
1181 // erase three values
1182 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconFile, NULL, szIniFile);
1183 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconIndex, NULL, szIniFile);
1184 WritePrivateProfileStringW(s_szShellClassInfo, s_szIconResource, NULL, szIniFile);
1185
1186 // flush!
1187 WritePrivateProfileStringW(NULL, NULL, NULL, szIniFile);
1188 }
1189
1190 // change desktop.ini attributes (+S +H)
1191 attrs = GetFileAttributesW(szIniFile);
1192 attrs |= FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1193 SetFileAttributesW(szIniFile, attrs);
1194
1195 // change folder attributes (+R)
1196 attrs = GetFileAttributesW(m_wszPath);
1197 attrs |= FILE_ATTRIBUTE_READONLY;
1198 SetFileAttributesW(m_wszPath, attrs);
1199
1200 // notify to the siblings
1201 PropSheet_QuerySiblings(GetParent(hwndDlg), 0, 0);
1202
1203 // done!
1204 m_bFolderIconIsSet = FALSE;
1205 }
1206
1207 return TRUE;
1208 }
1209
1210 /*****************************************************************************/
1211
CFileDefExt()1212 CFileDefExt::CFileDefExt():
1213 m_bDir(FALSE), m_cFiles(0), m_cFolders(0)
1214 {
1215 m_wszPath[0] = L'\0';
1216 m_DirSize.QuadPart = 0ull;
1217 m_DirSizeOnDisc.QuadPart = 0ull;
1218
1219 m_szFolderIconPath[0] = 0;
1220 m_nFolderIconIndex = 0;
1221 m_hFolderIcon = NULL;
1222 m_bFolderIconIsSet = FALSE;
1223 }
1224
~CFileDefExt()1225 CFileDefExt::~CFileDefExt()
1226 {
1227
1228 }
1229
1230 HRESULT WINAPI
Initialize(PCIDLIST_ABSOLUTE pidlFolder,IDataObject * pDataObj,HKEY hkeyProgID)1231 CFileDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
1232 {
1233 FORMATETC format;
1234 STGMEDIUM stgm;
1235 HRESULT hr;
1236
1237 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
1238
1239 if (!pDataObj)
1240 return E_FAIL;
1241
1242 format.cfFormat = CF_HDROP;
1243 format.ptd = NULL;
1244 format.dwAspect = DVASPECT_CONTENT;
1245 format.lindex = -1;
1246 format.tymed = TYMED_HGLOBAL;
1247
1248 hr = pDataObj->GetData(&format, &stgm);
1249 if (FAILED_UNEXPECTEDLY(hr))
1250 return hr;
1251
1252 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath)))
1253 {
1254 ERR("DragQueryFileW failed\n");
1255 ReleaseStgMedium(&stgm);
1256 return E_FAIL;
1257 }
1258
1259 ReleaseStgMedium(&stgm);
1260
1261 TRACE("File properties %ls\n", m_wszPath);
1262 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE;
1263 if (!m_bDir)
1264 m_VerInfo.Load(m_wszPath);
1265
1266 return S_OK;
1267 }
1268
1269 HRESULT WINAPI
QueryContextMenu(HMENU hmenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)1270 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
1271 {
1272 UNIMPLEMENTED;
1273 return E_NOTIMPL;
1274 }
1275
1276 HRESULT WINAPI
InvokeCommand(LPCMINVOKECOMMANDINFO lpici)1277 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1278 {
1279 UNIMPLEMENTED;
1280 return E_NOTIMPL;
1281 }
1282
1283 HRESULT WINAPI
GetCommandString(UINT_PTR idCmd,UINT uType,UINT * pwReserved,LPSTR pszName,UINT cchMax)1284 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
1285 {
1286 UNIMPLEMENTED;
1287 return E_NOTIMPL;
1288 }
1289
1290 HRESULT WINAPI
AddPages(LPFNADDPROPSHEETPAGE pfnAddPage,LPARAM lParam)1291 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
1292 {
1293 HPROPSHEETPAGE hPage;
1294 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES;
1295
1296 hPage = SH_CreatePropertySheetPageEx(wResId, GeneralPageProc, (LPARAM)this, NULL,
1297 &PropSheetPageLifetimeCallback<CFileDefExt>);
1298 HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam);
1299 if (FAILED_UNEXPECTEDLY(hr))
1300 return hr;
1301 else
1302 AddRef(); // For PropSheetPageLifetimeCallback
1303
1304 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL))
1305 {
1306 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION,
1307 VersionPageProc,
1308 (LPARAM)this,
1309 NULL);
1310 AddPropSheetPage(hPage, pfnAddPage, lParam);
1311 }
1312
1313 if (m_bDir)
1314 {
1315 hPage = SH_CreatePropertySheetPage(IDD_FOLDER_CUSTOMIZE,
1316 FolderCustomizePageProc,
1317 (LPARAM)this,
1318 NULL);
1319 AddPropSheetPage(hPage, pfnAddPage, lParam);
1320 }
1321
1322 return S_OK;
1323 }
1324
1325 HRESULT WINAPI
ReplacePage(UINT uPageID,LPFNADDPROPSHEETPAGE pfnReplacePage,LPARAM lParam)1326 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
1327 {
1328 UNIMPLEMENTED;
1329 return E_NOTIMPL;
1330 }
1331
1332 HRESULT WINAPI
SetSite(IUnknown * punk)1333 CFileDefExt::SetSite(IUnknown *punk)
1334 {
1335 UNIMPLEMENTED;
1336 return E_NOTIMPL;
1337 }
1338
1339 HRESULT WINAPI
GetSite(REFIID iid,void ** ppvSite)1340 CFileDefExt::GetSite(REFIID iid, void **ppvSite)
1341 {
1342 UNIMPLEMENTED;
1343 return E_NOTIMPL;
1344 }
1345
1346 DWORD WINAPI
_CountFolderAndFilesThreadProc(LPVOID lpParameter)1347 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter)
1348 {
1349 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter);
1350 DWORD ticks = 0;
1351 data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, &ticks);
1352
1353 //Release the CFileDefExt and data object holds in the copying thread.
1354 data->This->Release();
1355 HeapFree(GetProcessHeap(), 0, data->pwszBuf);
1356 HeapFree(GetProcessHeap(), 0, data);
1357
1358 return 0;
1359 }
1360
1361 BOOL
CountFolderAndFiles(HWND hwndDlg,LPCWSTR pwszBuf,DWORD * ticks)1362 CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPCWSTR pwszBuf, DWORD *ticks)
1363 {
1364 CString sBuf = pwszBuf;
1365 sBuf += L"\\" ;
1366 CString sSearch = sBuf;
1367 sSearch += L"*" ;
1368 CString sFileName;
1369
1370 WIN32_FIND_DATAW wfd;
1371 HANDLE hFind = FindFirstFileW(sSearch, &wfd);
1372 if (hFind == INVALID_HANDLE_VALUE)
1373 {
1374 ERR("FindFirstFileW %ls failed\n", sSearch.GetString());
1375 return FALSE;
1376 }
1377
1378 BOOL root = FALSE;
1379 if (*ticks == 0) {
1380 *ticks = GetTickCount();
1381 root = TRUE;
1382 }
1383
1384 do
1385 {
1386 sFileName = sBuf;
1387 sFileName += wfd.cFileName;
1388 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1389 {
1390 /* Don't process "." and ".." items */
1391 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
1392 continue;
1393
1394 ++m_cFolders;
1395
1396 CountFolderAndFiles(hwndDlg, sFileName, ticks);
1397 }
1398 else
1399 {
1400 m_cFiles++;
1401
1402 ULARGE_INTEGER FileSize;
1403 FileSize.u.LowPart = wfd.nFileSizeLow;
1404 FileSize.u.HighPart = wfd.nFileSizeHigh;
1405 m_DirSize.QuadPart += FileSize.QuadPart;
1406 // Calculate size on disc
1407 if (!GetPhysicalFileSize(sFileName.GetString(), &FileSize))
1408 ERR("GetPhysicalFileSize failed for %ls\n", sFileName.GetString());
1409 m_DirSizeOnDisc.QuadPart += FileSize.QuadPart;
1410 }
1411 if (GetTickCount() - *ticks > (DWORD) 300)
1412 {
1413 /* FIXME Using IsWindow is generally ill advised */
1414 if (IsWindow(hwndDlg))
1415 {
1416 WCHAR wszBuf[100];
1417
1418 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1419 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1420
1421 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf)))
1422 SetDlgItemTextW(hwndDlg, 14012, wszBuf);
1423
1424 /* Display files and folders count */
1425 WCHAR wszFormat[100];
1426 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1427 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1428 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1429 *ticks = GetTickCount();
1430 }
1431 else
1432 break;
1433 }
1434 } while(FindNextFileW(hFind, &wfd));
1435
1436 if (root && IsWindow(hwndDlg))
1437 {
1438 WCHAR wszBuf[100];
1439
1440 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1441 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1442
1443 if (SH_FormatFileSizeWithBytes(&m_DirSizeOnDisc, wszBuf, _countof(wszBuf)))
1444 SetDlgItemTextW(hwndDlg, 14012, wszBuf);
1445
1446 /* Display files and folders count */
1447 WCHAR wszFormat[100];
1448 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1449 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1450 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1451 }
1452
1453 FindClose(hFind);
1454 return TRUE;
1455 }
1456