* Slap *some* sense into our header inclusions.
[reactos.git] / reactos / dll / win32 / shell32 / 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 BOOL PathIsExeW(LPCWSTR lpszPath);
27
28 BOOL CFileVersionInfo::Load(LPCWSTR pwszPath)
29 {
30 ULONG cbInfo = GetFileVersionInfoSizeW(pwszPath, NULL);
31 if (!cbInfo)
32 {
33 WARN("GetFileVersionInfoSize %ls failed\n", pwszPath);
34 return FALSE;
35 }
36
37 m_pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbInfo);
38 if (!m_pInfo)
39 {
40 ERR("HeapAlloc failed bytes %x\n", cbInfo);
41 return FALSE;
42 }
43
44 if (!GetFileVersionInfoW(pwszPath, 0, cbInfo, m_pInfo))
45 {
46 ERR("GetFileVersionInfoW failed\n");
47 return FALSE;
48 }
49
50 LPLANGANDCODEPAGE lpLangCode;
51 UINT cBytes;
52 if (!VerQueryValueW(m_pInfo, L"\\VarFileInfo\\Translation", (LPVOID *)&lpLangCode, &cBytes) || cBytes < sizeof(LANGANDCODEPAGE))
53 {
54 ERR("VerQueryValueW failed\n");
55 return FALSE;
56 }
57
58 /* FIXME: find language from current locale / if not available,
59 * default to english
60 * for now default to first available language
61 */
62 m_wLang = lpLangCode->wLang;
63 m_wCode = lpLangCode->wCode;
64 TRACE("Lang %hx Code %hu\n", m_wLang, m_wCode);
65
66 return TRUE;
67 }
68
69 LPCWSTR CFileVersionInfo::GetString(LPCWSTR pwszName)
70 {
71 if (!m_pInfo)
72 return NULL;
73
74 WCHAR wszBuf[256];
75 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", m_wLang, m_wCode, pwszName);
76
77 /* Query string in version block */
78 LPCWSTR pwszResult = NULL;
79 UINT cBytes = 0;
80 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes))
81 pwszResult = NULL;
82
83 if (!m_wLang && !m_wCode)
84 {
85 /* Try US English */
86 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1252, pwszName);
87 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes))
88 pwszResult = NULL;
89 }
90
91 if (!pwszResult)
92 ERR("VerQueryValueW %ls failed\n", pwszName);
93 else
94 TRACE("%ls: %ls\n", pwszName, pwszResult);
95
96 return pwszResult;
97 }
98
99 VS_FIXEDFILEINFO *CFileVersionInfo::GetFixedInfo()
100 {
101 if (!m_pInfo)
102 return NULL;
103
104 VS_FIXEDFILEINFO *pInfo;
105 UINT cBytes;
106 if (!VerQueryValueW(m_pInfo, L"\\", (PVOID*)&pInfo, &cBytes))
107 return NULL;
108 return pInfo;
109 }
110
111 LPCWSTR CFileVersionInfo::GetLangName()
112 {
113 if (!m_pInfo)
114 return NULL;
115
116 if (!m_wszLang[0])
117 {
118 if (!VerLanguageNameW(m_wLang, m_wszLang, _countof(m_wszLang)))
119 ERR("VerLanguageNameW failed\n");
120 }
121
122 return m_wszLang;
123 }
124
125 UINT
126 SH_FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
127 {
128 // Print the number in uniform mode
129 WCHAR wszNumber[24];
130 swprintf(wszNumber, L"%I64u", Num);
131
132 // Get system strings for decimal and thousand separators.
133 WCHAR wszDecimalSep[8], wszThousandSep[8];
134 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
135 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
136
137 // Initialize format for printing the number in bytes
138 NUMBERFMTW nf;
139 ZeroMemory(&nf, sizeof(nf));
140 nf.lpDecimalSep = wszDecimalSep;
141 nf.lpThousandSep = wszThousandSep;
142
143 // Get system string for groups separator
144 WCHAR wszGrouping[12];
145 INT cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
146 LOCALE_SGROUPING,
147 wszGrouping,
148 _countof(wszGrouping));
149
150 // Convert grouping specs from string to integer
151 for (INT i = 0; i < cchGrouping; i++)
152 {
153 WCHAR wch = wszGrouping[i];
154
155 if (wch >= L'0' && wch <= L'9')
156 nf.Grouping = nf.Grouping * 10 + (wch - L'0');
157 else if (wch != L';')
158 break;
159 }
160
161 if ((nf.Grouping % 10) == 0)
162 nf.Grouping /= 10;
163 else
164 nf.Grouping *= 10;
165
166 // Format the number
167 INT cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
168 0,
169 wszNumber,
170 &nf,
171 pwszResult,
172 cchResultMax);
173
174 if (!cchResult)
175 return 0;
176
177 // GetNumberFormatW returns number of characters including UNICODE_NULL
178 return cchResult - 1;
179 }
180
181 UINT
182 SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
183 {
184 /* Write formated bytes count */
185 INT cchWritten = SH_FormatInteger(cbSize, pwszResult, cchResultMax);
186 if (!cchWritten)
187 return 0;
188
189 /* Copy " bytes" to buffer */
190 LPWSTR pwszEnd = pwszResult + cchWritten;
191 size_t cchRemaining = cchResultMax - cchWritten;
192 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, NULL);
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 /* Concate " (" */
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 an resource name
244 *
245 */
246
247 HPROPSHEETPAGE
248 SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle)
249 {
250 HRSRC hRes = FindResourceW(shell32_hInstance, MAKEINTRESOURCEW(wDialogId), (LPWSTR)RT_DIALOG);
251 if (hRes == NULL)
252 {
253 ERR("failed to find resource id\n");
254 return NULL;
255 }
256
257 LPVOID pTemplate = LoadResource(shell32_hInstance, hRes);
258 if (pTemplate == NULL)
259 {
260 ERR("failed to load resource\n");
261 return NULL;
262 }
263
264 PROPSHEETPAGEW Page;
265 memset(&Page, 0x0, sizeof(PROPSHEETPAGEW));
266 Page.dwSize = sizeof(PROPSHEETPAGEW);
267 Page.dwFlags = PSP_DLGINDIRECT;
268 Page.pResource = (DLGTEMPLATE*)pTemplate;
269 Page.pfnDlgProc = pfnDlgProc;
270 Page.lParam = lParam;
271 Page.pszTitle = pwszTitle;
272
273 if (pwszTitle)
274 Page.dwFlags |= PSP_USETITLE;
275
276 return CreatePropertySheetPageW(&Page);
277 }
278
279 VOID
280 CFileDefExt::InitOpensWithField(HWND hwndDlg)
281 {
282 WCHAR wszBuf[MAX_PATH] = L"";
283 WCHAR wszPath[MAX_PATH] = L"";
284 DWORD dwSize = sizeof(wszBuf);
285 BOOL bUnknownApp = TRUE;
286 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
287
288 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS)
289 {
290 bUnknownApp = FALSE;
291 StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command");
292 dwSize = sizeof(wszPath);
293 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS)
294 {
295 /* Get path from command line */
296 ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf));
297 PathRemoveArgs(wszBuf);
298 PathUnquoteSpacesW(wszBuf);
299 PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath));
300
301 HICON hIcon;
302 if (ExtractIconExW(wszPath, 0, NULL, &hIcon, 1))
303 {
304 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025);
305 HWND hDescrCtrl = GetDlgItem(hwndDlg, 14007);
306 ShowWindow(hIconCtrl, SW_SHOW);
307 RECT rcIcon, rcDescr;
308 GetWindowRect(hIconCtrl, &rcIcon);
309 if (rcIcon.left == rcIcon.right)
310 ERR("Icon control has invalid width: %d-%d\n", rcIcon.left, rcIcon.right);
311 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2);
312 GetWindowRect(hDescrCtrl, &rcDescr);
313 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2);
314 INT cxOffset = rcIcon.right + 2 - rcDescr.left;
315 SetWindowPos(hDescrCtrl, NULL,
316 rcDescr.left + cxOffset, rcDescr.top,
317 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top,
318 SWP_NOZORDER);
319 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0);
320 } else
321 ERR("Failed to extract icon\n");
322
323 if (PathFileExistsW(wszPath))
324 {
325 /* Get file description */
326 CFileVersionInfo VerInfo;
327 VerInfo.Load(wszPath);
328 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription");
329 if (pwszDescr)
330 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
331 else
332 {
333 /* File has no description - display filename */
334 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
335 PathRemoveExtension(pwszFilename);
336 pwszFilename[0] = towupper(pwszFilename[0]);
337 SetDlgItemTextW(hwndDlg, 14007, pwszFilename);
338 }
339 }
340 else
341 bUnknownApp = TRUE;
342 } else
343 WARN("RegGetValueW %ls failed\n", wszBuf);
344 } else
345 WARN("RegGetValueW %ls failed\n", pwszExt);
346
347 if (bUnknownApp)
348 {
349 /* Unknown application */
350 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf));
351 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
352 }
353 }
354
355 /*************************************************************************
356 *
357 * SH_FileGeneralFileType [Internal]
358 *
359 * retrieves file extension description from registry and sets it in dialog
360 *
361 * TODO: retrieve file extension default icon and load it
362 * find executable name from registry, retrieve description from executable
363 */
364
365 BOOL
366 CFileDefExt::InitFileType(HWND hwndDlg)
367 {
368 TRACE("path %s\n", debugstr_w(m_wszPath));
369
370 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005);
371 if (hDlgCtrl == NULL)
372 return FALSE;
373
374 /* Get file information */
375 SHFILEINFO fi;
376 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
377 {
378 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError());
379 fi.szTypeName[0] = L'\0';
380 fi.hIcon = NULL;
381 }
382
383 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
384 if (pwszExt[0])
385 {
386 WCHAR wszBuf[256];
387
388 if (!fi.szTypeName[0])
389 {
390 /* The file type is unknown, so default to string "FileExtension File" */
391 size_t cchRemaining = 0;
392 LPWSTR pwszEnd = NULL;
393
394 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1);
395 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd);
396
397 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
398 }
399 else
400 {
401 /* Update file type */
402 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt);
403 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
404 }
405 }
406
407 /* Update file icon */
408 if (fi.hIcon)
409 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
410 else
411 ERR("No icon %ls\n", m_wszPath);
412
413 return TRUE;
414 }
415
416 /*************************************************************************
417 *
418 * CFileDefExt::InitFilePath [Internal]
419 *
420 * sets file path string and filename string
421 *
422 */
423
424 BOOL
425 CFileDefExt::InitFilePath(HWND hwndDlg)
426 {
427 /* Find the filename */
428 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath);
429
430 if (pwszFilename > m_wszPath)
431 {
432 /* Location field */
433 WCHAR wszLocation[MAX_PATH];
434 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath);
435 PathRemoveBackslashW(wszLocation);
436
437 SetDlgItemTextW(hwndDlg, 14009, wszLocation);
438 }
439
440 /* Filename field */
441 SetDlgItemTextW(hwndDlg, 14001, pwszFilename);
442
443 return TRUE;
444 }
445
446 /*************************************************************************
447 *
448 * CFileDefExt::GetFileTimeString [Internal]
449 *
450 * formats a given LPFILETIME struct into readable user format
451 */
452
453 BOOL
454 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
455 {
456 FILETIME ft;
457 SYSTEMTIME st;
458
459 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
460 return FALSE;
461
462 size_t cchRemaining = cchResult;
463 LPWSTR pwszEnd = pwszResult;
464 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
465 if (cchWritten)
466 --cchWritten; // GetDateFormatW returns count with terminating zero
467 else
468 ERR("GetDateFormatW failed\n");
469 cchRemaining -= cchWritten;
470 pwszEnd += cchWritten;
471
472 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
473
474 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
475 if (cchWritten)
476 --cchWritten; // GetTimeFormatW returns count with terminating zero
477 else
478 ERR("GetTimeFormatW failed\n");
479 TRACE("result %s\n", debugstr_w(pwszResult));
480 return TRUE;
481 }
482
483 /*************************************************************************
484 *
485 * CFileDefExt::InitFileAttr [Internal]
486 *
487 * retrieves file information from file and sets in dialog
488 *
489 */
490
491 BOOL
492 CFileDefExt::InitFileAttr(HWND hwndDlg)
493 {
494 WCHAR wszBuf[MAX_PATH];
495
496 TRACE("InitFileAttr %ls\n", m_wszPath);
497
498 WIN32_FILE_ATTRIBUTE_DATA FileInfo;
499 if (GetFileAttributesExW(m_wszPath, GetFileExInfoStandard, &FileInfo))
500 {
501 /* Update attribute checkboxes */
502 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
503 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0);
504 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
505 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0);
506 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
507 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0);
508
509 /* Update creation time */
510 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf)))
511 SetDlgItemTextW(hwndDlg, 14015, wszBuf);
512
513 /* For files display last access and last write time */
514 if (!m_bDir)
515 {
516 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf)))
517 SetDlgItemTextW(hwndDlg, 14019, wszBuf);
518
519 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf)))
520 SetDlgItemTextW(hwndDlg, 14017, wszBuf);
521
522 /* Update size of file */
523 ULARGE_INTEGER FileSize;
524 FileSize.u.LowPart = FileInfo.nFileSizeLow;
525 FileSize.u.HighPart = FileInfo.nFileSizeHigh;
526 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)))
527 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
528 }
529 }
530
531 if (m_bDir)
532 {
533 /* For directories files have to be counted */
534 StringCchCopyW(wszBuf, _countof(wszBuf), m_wszPath);
535 CountFolderAndFiles(wszBuf, _countof(wszBuf));
536
537 /* Update size filed */
538 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
539 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
540
541 /* Display files and folders count */
542 WCHAR wszFormat[256];
543 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
544 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
545 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
546 }
547
548 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
549 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE);
550
551 return TRUE;
552 }
553
554 /*************************************************************************
555 *
556 * CFileDefExt::InitGeneralPage [Internal]
557 *
558 * sets all file general properties in dialog
559 */
560
561 BOOL
562 CFileDefExt::InitGeneralPage(HWND hwndDlg)
563 {
564 /* Set general text properties filename filelocation and icon */
565 InitFilePath(hwndDlg);
566
567 /* Set file type and icon */
568 InitFileType(hwndDlg);
569
570 /* Set open with application */
571 if (!m_bDir)
572 {
573 if (!PathIsExeW(m_wszPath))
574 InitOpensWithField(hwndDlg);
575 else
576 {
577 WCHAR wszBuf[MAX_PATH];
578 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
579 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
580 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
581 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
582 if (pwszDescr)
583 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
584 else
585 {
586 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
587 PathRemoveExtension(wszBuf);
588 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
589 }
590 }
591 }
592
593 /* Set file created/modfied/accessed time, size and attributes */
594 InitFileAttr(hwndDlg);
595
596 return TRUE;
597 }
598
599 /*************************************************************************
600 *
601 * CFileDefExt::GeneralPageProc
602 *
603 * wnd proc of 'General' property sheet page
604 *
605 */
606
607 INT_PTR CALLBACK
608 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
609 {
610 switch (uMsg)
611 {
612 case WM_INITDIALOG:
613 {
614 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
615
616 if (ppsp == NULL || !ppsp->lParam)
617 break;
618
619 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam);
620
621 CFileDefExt *pFileDefExt = (CFileDefExt*)ppsp->lParam;
622 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt);
623 pFileDefExt->InitGeneralPage(hwndDlg);
624 break;
625 }
626 case WM_COMMAND:
627 if (LOWORD(wParam) == 14024) /* Opens With - Change */
628 {
629 CFileDefExt *pFileDefExt = (CFileDefExt*)GetWindowLongPtr(hwndDlg, DWLP_USER);
630 OPENASINFO oainfo;
631 oainfo.pcszFile = pFileDefExt->m_wszPath;
632 oainfo.pcszClass = NULL;
633 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION;
634 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo));
635 }
636 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */
637 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
638 else if (LOWORD(wParam) == 14001) /* Name */
639 {
640 if (HIWORD(wParam) == EN_CHANGE)
641 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
642 }
643 break;
644 case WM_NOTIFY:
645 {
646 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
647 if (lppsn->hdr.code == PSN_APPLY)
648 {
649 CFileDefExt *pFileDefExt = (CFileDefExt*)GetWindowLongPtr(hwndDlg, DWLP_USER);
650
651 /* Update attributes first */
652 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath);
653 if (dwAttr)
654 {
655 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE);
656
657 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0))
658 dwAttr |= FILE_ATTRIBUTE_READONLY;
659 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0))
660 dwAttr |= FILE_ATTRIBUTE_HIDDEN;
661 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0))
662 dwAttr |= FILE_ATTRIBUTE_ARCHIVE;
663
664 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr))
665 ERR("SetFileAttributesW failed\n");
666 }
667
668 /* Update filename now */
669 WCHAR wszBuf[MAX_PATH];
670 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath);
671 LPWSTR pwszFilename = PathFindFileNameW(wszBuf);
672 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf);
673 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax))
674 {
675 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf))
676 ERR("MoveFileW failed\n");
677 }
678
679 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
680 return TRUE;
681 }
682 break;
683 }
684 default:
685 break;
686 }
687
688 return FALSE;
689 }
690
691 /*************************************************************************
692 *
693 * CFileDefExt::InitVersionPage [Internal]
694 *
695 * sets all file version properties in dialog
696 */
697
698 BOOL
699 CFileDefExt::InitVersionPage(HWND hwndDlg)
700 {
701 /* Get fixed info */
702 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo();
703 if (pInfo)
704 {
705 WCHAR wszVersion[256];
706 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS),
707 LOWORD(pInfo->dwFileVersionMS),
708 HIWORD(pInfo->dwFileVersionLS),
709 LOWORD(pInfo->dwFileVersionLS));
710 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion));
711 SetDlgItemTextW(hwndDlg, 14001, wszVersion);
712 }
713
714 /* Update labels */
715 SetVersionLabel(hwndDlg, 14003, L"FileDescription");
716 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright");
717
718 /* Add items to listbox */
719 AddVersionString(hwndDlg, L"CompanyName");
720 LPCWSTR pwszLang = m_VerInfo.GetLangName();
721 if (pwszLang)
722 {
723 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
724 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language");
725 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang);
726 }
727 AddVersionString(hwndDlg, L"ProductName");
728 AddVersionString(hwndDlg, L"InternalName");
729 AddVersionString(hwndDlg, L"OriginalFilename");
730 AddVersionString(hwndDlg, L"FileVersion");
731 AddVersionString(hwndDlg, L"ProductVersion");
732
733 /* Attach file version to dialog window */
734 SetWindowLongPtr(hwndDlg, DWL_USER, (LONG_PTR)this);
735
736 /* Select first item */
737 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
738 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0);
739 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL);
740 if (pwszText && pwszText != (LPCWSTR)LB_ERR)
741 SetDlgItemTextW(hwndDlg, 14010, pwszText);
742
743 return TRUE;
744 }
745
746 /*************************************************************************
747 *
748 * CFileDefExt::SetVersionLabel [Internal]
749 *
750 * retrieves a version string and uses it to set label text
751 */
752
753 BOOL
754 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName)
755 {
756 if (hwndDlg == NULL || pwszName == NULL)
757 return FALSE;
758
759 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
760 if (pwszValue)
761 {
762 /* file description property */
763 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
764 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue);
765 return TRUE;
766 }
767
768 return FALSE;
769 }
770
771 /*************************************************************************
772 *
773 * CFileDefExt::AddVersionString [Internal]
774 *
775 * retrieves a version string and adds it to listbox
776 */
777
778 BOOL
779 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName)
780 {
781 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg);
782
783 if (hwndDlg == NULL || pwszName == NULL)
784 return FALSE;
785
786 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
787 if (pwszValue)
788 {
789 /* listbox name property */
790 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
791 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
792 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName);
793 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue);
794 return TRUE;
795 }
796
797 return FALSE;
798 }
799
800 /*************************************************************************
801 *
802 * CFileDefExt::VersionPageProc
803 *
804 * wnd proc of 'Version' property sheet page
805 */
806
807 INT_PTR CALLBACK
808 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
809 {
810 switch (uMsg)
811 {
812 case WM_INITDIALOG:
813 {
814 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam;
815
816 if (ppsp == NULL || !ppsp->lParam)
817 break;
818
819 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam);
820
821 CFileDefExt *pFileDefExt = (CFileDefExt*)ppsp->lParam;
822 return pFileDefExt->InitVersionPage(hwndDlg);
823 }
824 case WM_COMMAND:
825 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE)
826 {
827 HWND hDlgCtrl = (HWND)lParam;
828
829 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL);
830 if (Index == LB_ERR)
831 break;
832
833 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL);
834 if (pwszData == NULL)
835 break;
836
837 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(pwszData));
838 SetDlgItemTextW(hwndDlg, 14010, pwszData);
839
840 return TRUE;
841 }
842 break;
843 case WM_DESTROY:
844 break;
845 default:
846 break;
847 }
848
849 return FALSE;
850 }
851
852 CFileDefExt::CFileDefExt():
853 m_bDir(FALSE), m_cFiles(0), m_cFolders(0)
854 {
855 m_wszPath[0] = L'\0';
856 m_DirSize.QuadPart = 0ull;
857 }
858
859 CFileDefExt::~CFileDefExt()
860 {
861
862 }
863
864 HRESULT WINAPI
865 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
866 {
867 FORMATETC format;
868 STGMEDIUM stgm;
869 HRESULT hr;
870
871 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
872
873 if (!pDataObj)
874 return E_FAIL;
875
876 format.cfFormat = CF_HDROP;
877 format.ptd = NULL;
878 format.dwAspect = DVASPECT_CONTENT;
879 format.lindex = -1;
880 format.tymed = TYMED_HGLOBAL;
881
882 hr = pDataObj->GetData(&format, &stgm);
883 if (FAILED(hr))
884 return hr;
885
886 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath)))
887 {
888 ERR("DragQueryFileW failed\n");
889 ReleaseStgMedium(&stgm);
890 return E_FAIL;
891 }
892
893 ReleaseStgMedium(&stgm);
894
895 TRACE("File properties %ls\n", m_wszPath);
896 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE;
897 if (!m_bDir)
898 m_VerInfo.Load(m_wszPath);
899
900 return S_OK;
901 }
902
903 HRESULT WINAPI
904 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
905 {
906 UNIMPLEMENTED;
907 return E_NOTIMPL;
908 }
909
910 HRESULT WINAPI
911 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
912 {
913 UNIMPLEMENTED;
914 return E_NOTIMPL;
915 }
916
917 HRESULT WINAPI
918 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
919 {
920 UNIMPLEMENTED;
921 return E_NOTIMPL;
922 }
923
924 HRESULT WINAPI
925 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
926 {
927 HPROPSHEETPAGE hPage;
928 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES;
929
930 hPage = SH_CreatePropertySheetPage(wResId,
931 GeneralPageProc,
932 (LPARAM)this,
933 NULL);
934 if (hPage)
935 pfnAddPage(hPage, lParam);
936
937 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL))
938 {
939 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION,
940 VersionPageProc,
941 (LPARAM)this,
942 NULL);
943 if (hPage)
944 pfnAddPage(hPage, lParam);
945 }
946
947 return S_OK;
948 }
949
950 HRESULT WINAPI
951 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
952 {
953 UNIMPLEMENTED;
954 return E_NOTIMPL;
955 }
956
957 HRESULT WINAPI
958 CFileDefExt::SetSite(IUnknown *punk)
959 {
960 UNIMPLEMENTED;
961 return E_NOTIMPL;
962 }
963
964 HRESULT WINAPI
965 CFileDefExt::GetSite(REFIID iid, void **ppvSite)
966 {
967 UNIMPLEMENTED;
968 return E_NOTIMPL;
969 }
970
971 BOOL
972 CFileDefExt::CountFolderAndFiles(LPWSTR pwszBuf, UINT cchBufMax)
973 {
974 /* Find filename position */
975 UINT cchBuf = wcslen(pwszBuf);
976 WCHAR *pwszFilename = pwszBuf + cchBuf;
977 size_t cchFilenameMax = cchBufMax - cchBuf;
978 if (!cchFilenameMax)
979 return FALSE;
980 *(pwszFilename++) = '\\';
981 --cchFilenameMax;
982
983 /* Find all files, FIXME: shouldn't be "*"? */
984 StringCchCopyW(pwszFilename, cchFilenameMax, L"*");
985
986 WIN32_FIND_DATAW wfd;
987 HANDLE hFind = FindFirstFileW(pwszBuf, &wfd);
988 if (hFind == INVALID_HANDLE_VALUE)
989 {
990 ERR("FindFirstFileW %ls failed\n", pwszBuf);
991 return FALSE;
992 }
993
994 do
995 {
996 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
997 {
998 /* Don't process "." and ".." items */
999 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
1000 continue;
1001
1002 ++m_cFolders;
1003
1004 StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName);
1005 CountFolderAndFiles(pwszBuf, cchBufMax);
1006 }
1007 else
1008 {
1009 m_cFiles++;
1010
1011 ULARGE_INTEGER FileSize;
1012 FileSize.u.LowPart = wfd.nFileSizeLow;
1013 FileSize.u.HighPart = wfd.nFileSizeHigh;
1014 m_DirSize.QuadPart += FileSize.QuadPart;
1015 }
1016 } while(FindNextFileW(hFind, &wfd));
1017
1018 FindClose(hFind);
1019 return TRUE;
1020 }