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