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