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