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