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