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