2 * Provides default file shell extension
4 * Copyright 2005 Johannes Anderwald
5 * Copyright 2012 Rafal Harabien
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.
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.
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
24 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
26 EXTERN_C BOOL
PathIsExeW(LPCWSTR lpszPath
);
28 BOOL
CFileVersionInfo::Load(LPCWSTR pwszPath
)
30 ULONG cbInfo
= GetFileVersionInfoSizeW(pwszPath
, NULL
);
33 WARN("GetFileVersionInfoSize %ls failed\n", pwszPath
);
37 m_pInfo
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbInfo
);
40 ERR("HeapAlloc failed bytes %x\n", cbInfo
);
44 if (!GetFileVersionInfoW(pwszPath
, 0, cbInfo
, m_pInfo
))
46 ERR("GetFileVersionInfoW failed\n");
50 LPLANGANDCODEPAGE lpLangCode
;
52 if (!VerQueryValueW(m_pInfo
, L
"\\VarFileInfo\\Translation", (LPVOID
*)&lpLangCode
, &cBytes
) || cBytes
< sizeof(LANGANDCODEPAGE
))
54 ERR("VerQueryValueW failed\n");
58 /* FIXME: find language from current locale / if not available,
60 * for now default to first available language
62 m_wLang
= lpLangCode
->wLang
;
63 m_wCode
= lpLangCode
->wCode
;
64 TRACE("Lang %hx Code %hu\n", m_wLang
, m_wCode
);
69 LPCWSTR
CFileVersionInfo::GetString(LPCWSTR pwszName
)
75 swprintf(wszBuf
, L
"\\StringFileInfo\\%04x%04x\\%s", m_wLang
, m_wCode
, pwszName
);
77 /* Query string in version block */
78 LPCWSTR pwszResult
= NULL
;
80 if (!VerQueryValueW(m_pInfo
, wszBuf
, (LPVOID
*)&pwszResult
, &cBytes
))
83 if (!m_wLang
&& !m_wCode
)
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
))
92 ERR("VerQueryValueW %ls failed\n", pwszName
);
94 TRACE("%ls: %ls\n", pwszName
, pwszResult
);
99 VS_FIXEDFILEINFO
*CFileVersionInfo::GetFixedInfo()
104 VS_FIXEDFILEINFO
*pInfo
;
106 if (!VerQueryValueW(m_pInfo
, L
"\\", (PVOID
*)&pInfo
, &cBytes
))
111 LPCWSTR
CFileVersionInfo::GetLangName()
118 if (!VerLanguageNameW(m_wLang
, m_wszLang
, _countof(m_wszLang
)))
119 ERR("VerLanguageNameW failed\n");
126 SH_FormatInteger(LONGLONG Num
, LPWSTR pwszResult
, UINT cchResultMax
)
128 // Print the number in uniform mode
130 swprintf(wszNumber
, L
"%I64u", Num
);
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
));
137 // Initialize format for printing the number in bytes
139 ZeroMemory(&nf
, sizeof(nf
));
140 nf
.lpDecimalSep
= wszDecimalSep
;
141 nf
.lpThousandSep
= wszThousandSep
;
143 // Get system string for groups separator
144 WCHAR wszGrouping
[12];
145 INT cchGrouping
= GetLocaleInfoW(LOCALE_USER_DEFAULT
,
148 _countof(wszGrouping
));
150 // Convert grouping specs from string to integer
151 for (INT i
= 0; i
< cchGrouping
; i
++)
153 WCHAR wch
= wszGrouping
[i
];
155 if (wch
>= L
'0' && wch
<= L
'9')
156 nf
.Grouping
= nf
.Grouping
* 10 + (wch
- L
'0');
157 else if (wch
!= L
';')
161 if ((nf
.Grouping
% 10) == 0)
167 INT cchResult
= GetNumberFormatW(LOCALE_USER_DEFAULT
,
177 // GetNumberFormatW returns number of characters including UNICODE_NULL
178 return cchResult
- 1;
182 SH_FormatByteSize(LONGLONG cbSize
, LPWSTR pwszResult
, UINT cchResultMax
)
184 /* Write formated bytes count */
185 INT cchWritten
= SH_FormatInteger(cbSize
, pwszResult
, cchResultMax
);
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
;
196 return cchResultMax
- cchRemaining
;
199 /*************************************************************************
201 * SH_FormatFileSizeWithBytes
203 * Format a size in bytes to string.
205 * lpQwSize = Pointer to 64bit large integer to format
206 * pszBuf = Buffer to fill with output string
207 * cchBuf = size of pszBuf in characters
212 SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize
, LPWSTR pwszResult
, UINT cchResultMax
)
214 /* Format bytes in KBs, MBs etc */
215 if (StrFormatByteSizeW(lpQwSize
->QuadPart
, pwszResult
, cchResultMax
) == NULL
)
218 /* If there is less bytes than 1KB, we have nothing to do */
219 if (lpQwSize
->QuadPart
< 1024)
223 UINT cchWritten
= wcslen(pwszResult
);
224 LPWSTR pwszEnd
= pwszResult
+ cchWritten
;
225 size_t cchRemaining
= cchResultMax
- cchWritten
;
226 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
228 /* Write formated bytes count */
229 cchWritten
= SH_FormatByteSize(lpQwSize
->QuadPart
, pwszEnd
, cchRemaining
);
230 pwszEnd
+= cchWritten
;
231 cchRemaining
-= cchWritten
;
233 /* Copy ")" to the buffer */
234 StringCchCopyW(pwszEnd
, cchRemaining
, L
")");
239 /*************************************************************************
241 * SH_CreatePropertySheetPage [Internal]
243 * creates a property sheet page from a resource id
248 SH_CreatePropertySheetPage(WORD wDialogId
, DLGPROC pfnDlgProc
, LPARAM lParam
, LPCWSTR pwszTitle
)
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
;
262 Page
.dwFlags
|= PSP_USETITLE
;
264 return CreatePropertySheetPageW(&Page
);
268 CFileDefExt::InitOpensWithField(HWND hwndDlg
)
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
);
276 if (RegGetValueW(HKEY_CLASSES_ROOT
, pwszExt
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
)
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
)
283 /* Get path from command line */
284 ExpandEnvironmentStringsW(wszPath
, wszBuf
, _countof(wszBuf
));
285 PathRemoveArgs(wszBuf
);
286 PathUnquoteSpacesW(wszBuf
);
287 PathSearchAndQualify(wszBuf
, wszPath
, _countof(wszPath
));
290 if (ExtractIconExW(wszPath
, 0, NULL
, &hIcon
, 1))
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
);
298 rcIcon
.right
= rcIcon
.left
+ GetSystemMetrics(SM_CXSMICON
);
299 rcIcon
.bottom
= rcIcon
.top
+ GetSystemMetrics(SM_CYSMICON
);
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
,
309 SendMessageW(hIconCtrl
, STM_SETICON
, (WPARAM
)hIcon
, 0);
311 ERR("Failed to extract icon\n");
313 if (PathFileExistsW(wszPath
))
315 /* Get file description */
316 CFileVersionInfo VerInfo
;
317 VerInfo
.Load(wszPath
);
318 LPCWSTR pwszDescr
= VerInfo
.GetString(L
"FileDescription");
320 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
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
);
333 WARN("RegGetValueW %ls failed\n", wszBuf
);
335 WARN("RegGetValueW %ls failed\n", pwszExt
);
339 /* Unknown application */
340 LoadStringW(shell32_hInstance
, IDS_UNKNOWN_APP
, wszBuf
, _countof(wszBuf
));
341 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
345 /*************************************************************************
347 * SH_FileGeneralFileType [Internal]
349 * retrieves file extension description from registry and sets it in dialog
351 * TODO: retrieve file extension default icon and load it
352 * find executable name from registry, retrieve description from executable
356 CFileDefExt::InitFileType(HWND hwndDlg
)
358 TRACE("path %s\n", debugstr_w(m_wszPath
));
360 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14005);
361 if (hDlgCtrl
== NULL
)
364 /* Get file information */
366 if (!SHGetFileInfoW(m_wszPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
368 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath
, GetLastError());
369 fi
.szTypeName
[0] = L
'\0';
373 LPCWSTR pwszExt
= PathFindExtensionW(m_wszPath
);
378 if (!fi
.szTypeName
[0])
380 /* The file type is unknown, so default to string "FileExtension File" */
381 size_t cchRemaining
= 0;
382 LPWSTR pwszEnd
= NULL
;
384 StringCchPrintfExW(wszBuf
, _countof(wszBuf
), &pwszEnd
, &cchRemaining
, 0, L
"%s ", pwszExt
+ 1);
385 SendMessageW(hDlgCtrl
, WM_GETTEXT
, (WPARAM
)cchRemaining
, (LPARAM
)pwszEnd
);
387 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
391 /* Update file type */
392 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s (%s)", fi
.szTypeName
, pwszExt
);
393 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
397 /* Update file icon */
399 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
401 ERR("No icon %ls\n", m_wszPath
);
406 /*************************************************************************
408 * CFileDefExt::InitFilePath [Internal]
410 * sets file path string and filename string
415 CFileDefExt::InitFilePath(HWND hwndDlg
)
417 /* Find the filename */
418 WCHAR
*pwszFilename
= PathFindFileNameW(m_wszPath
);
420 if (pwszFilename
> m_wszPath
)
423 WCHAR wszLocation
[MAX_PATH
];
424 StringCchCopyNW(wszLocation
, _countof(wszLocation
), m_wszPath
, pwszFilename
- m_wszPath
);
425 PathRemoveBackslashW(wszLocation
);
427 SetDlgItemTextW(hwndDlg
, 14009, wszLocation
);
431 SetDlgItemTextW(hwndDlg
, 14001, pwszFilename
);
436 /*************************************************************************
438 * CFileDefExt::GetFileTimeString [Internal]
440 * formats a given LPFILETIME struct into readable user format
444 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
449 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
452 size_t cchRemaining
= cchResult
;
453 LPWSTR pwszEnd
= pwszResult
;
454 int cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
456 --cchWritten
; // GetDateFormatW returns count with terminating zero
458 ERR("GetDateFormatW failed\n");
459 cchRemaining
-= cchWritten
;
460 pwszEnd
+= cchWritten
;
462 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
464 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
466 --cchWritten
; // GetTimeFormatW returns count with terminating zero
468 ERR("GetTimeFormatW failed\n");
469 TRACE("result %s\n", debugstr_w(pwszResult
));
473 /*************************************************************************
475 * CFileDefExt::InitFileAttr [Internal]
477 * retrieves file information from file and sets in dialog
482 CFileDefExt::InitFileAttr(HWND hwndDlg
)
484 WCHAR wszBuf
[MAX_PATH
];
486 TRACE("InitFileAttr %ls\n", m_wszPath
);
488 WIN32_FILE_ATTRIBUTE_DATA FileInfo
;
489 if (GetFileAttributesExW(m_wszPath
, GetFileExInfoStandard
, &FileInfo
))
491 /* Update attribute checkboxes */
492 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
493 SendDlgItemMessage(hwndDlg
, 14021, BM_SETCHECK
, BST_CHECKED
, 0);
494 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
495 SendDlgItemMessage(hwndDlg
, 14022, BM_SETCHECK
, BST_CHECKED
, 0);
496 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
)
497 SendDlgItemMessage(hwndDlg
, 14023, BM_SETCHECK
, BST_CHECKED
, 0);
499 /* Update creation time */
500 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, _countof(wszBuf
)))
501 SetDlgItemTextW(hwndDlg
, 14015, wszBuf
);
503 /* For files display last access and last write time */
506 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, _countof(wszBuf
)))
507 SetDlgItemTextW(hwndDlg
, 14019, wszBuf
);
509 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, _countof(wszBuf
)))
510 SetDlgItemTextW(hwndDlg
, 14017, wszBuf
);
512 /* Update size of file */
513 ULARGE_INTEGER FileSize
;
514 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
515 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
516 if (SH_FormatFileSizeWithBytes(&FileSize
, wszBuf
, _countof(wszBuf
)))
517 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
523 /* For directories files have to be counted */
525 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData
)));
527 data
->pwszBuf
= static_cast<LPWSTR
>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * MAX_PATH
));
528 data
->cchBufMax
= MAX_PATH
;
529 data
->hwndDlg
= hwndDlg
;
531 StringCchCopyW(data
->pwszBuf
, MAX_PATH
, m_wszPath
);
533 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc
, data
, NULL
, NULL
);
535 /* Update size field */
536 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
537 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
539 /* Display files and folders count */
540 WCHAR wszFormat
[256];
541 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
542 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
543 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
546 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
547 ShowWindow(GetDlgItem(hwndDlg
, 14028), SW_HIDE
);
552 /*************************************************************************
554 * CFileDefExt::InitGeneralPage [Internal]
556 * sets all file general properties in dialog
560 CFileDefExt::InitGeneralPage(HWND hwndDlg
)
562 /* Set general text properties filename filelocation and icon */
563 InitFilePath(hwndDlg
);
565 /* Set file type and icon */
566 InitFileType(hwndDlg
);
568 /* Set open with application */
571 if (!PathIsExeW(m_wszPath
))
572 InitOpensWithField(hwndDlg
);
575 WCHAR wszBuf
[MAX_PATH
];
576 LoadStringW(shell32_hInstance
, IDS_EXE_DESCRIPTION
, wszBuf
, _countof(wszBuf
));
577 SetDlgItemTextW(hwndDlg
, 14006, wszBuf
);
578 ShowWindow(GetDlgItem(hwndDlg
, 14024), SW_HIDE
);
579 LPCWSTR pwszDescr
= m_VerInfo
.GetString(L
"FileDescription");
581 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
584 StringCbCopyW(wszBuf
, sizeof(wszBuf
), PathFindFileNameW(m_wszPath
));
585 PathRemoveExtension(wszBuf
);
586 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
591 /* Set file created/modfied/accessed time, size and attributes */
592 InitFileAttr(hwndDlg
);
597 /*************************************************************************
599 * CFileDefExt::GeneralPageProc
601 * wnd proc of 'General' property sheet page
606 CFileDefExt::GeneralPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
612 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
614 if (ppsp
== NULL
|| !ppsp
->lParam
)
617 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg
, lParam
, ppsp
->lParam
);
619 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
620 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pFileDefExt
);
621 pFileDefExt
->InitGeneralPage(hwndDlg
);
625 if (LOWORD(wParam
) == 14024) /* Opens With - Change */
627 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
629 oainfo
.pcszFile
= pFileDefExt
->m_wszPath
;
630 oainfo
.pcszClass
= NULL
;
631 oainfo
.oaifInFlags
= OAIF_REGISTER_EXT
|OAIF_FORCE_REGISTRATION
;
632 return SUCCEEDED(SHOpenWithDialog(hwndDlg
, &oainfo
));
634 else if (LOWORD(wParam
) == 14021 || LOWORD(wParam
) == 14022 || LOWORD(wParam
) == 14023) /* checkboxes */
635 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
636 else if (LOWORD(wParam
) == 14001) /* Name */
638 if (HIWORD(wParam
) == EN_CHANGE
)
639 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
644 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
645 if (lppsn
->hdr
.code
== PSN_APPLY
)
647 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
649 /* Update attributes first */
650 DWORD dwAttr
= GetFileAttributesW(pFileDefExt
->m_wszPath
);
653 dwAttr
&= ~(FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_ARCHIVE
);
655 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14021, BM_GETCHECK
, 0, 0))
656 dwAttr
|= FILE_ATTRIBUTE_READONLY
;
657 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14022, BM_GETCHECK
, 0, 0))
658 dwAttr
|= FILE_ATTRIBUTE_HIDDEN
;
659 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14023, BM_GETCHECK
, 0, 0))
660 dwAttr
|= FILE_ATTRIBUTE_ARCHIVE
;
662 if (!SetFileAttributesW(pFileDefExt
->m_wszPath
, dwAttr
))
663 ERR("SetFileAttributesW failed\n");
666 /* Update filename now */
667 WCHAR wszBuf
[MAX_PATH
];
668 StringCchCopyW(wszBuf
, _countof(wszBuf
), pFileDefExt
->m_wszPath
);
669 LPWSTR pwszFilename
= PathFindFileNameW(wszBuf
);
670 UINT cchFilenameMax
= _countof(wszBuf
) - (pwszFilename
- wszBuf
);
671 if (GetDlgItemTextW(hwndDlg
, 14001, pwszFilename
, cchFilenameMax
))
673 if (!MoveFileW(pFileDefExt
->m_wszPath
, wszBuf
))
674 ERR("MoveFileW failed\n");
677 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
689 /*************************************************************************
691 * CFileDefExt::InitVersionPage [Internal]
693 * sets all file version properties in dialog
697 CFileDefExt::InitVersionPage(HWND hwndDlg
)
700 VS_FIXEDFILEINFO
*pInfo
= m_VerInfo
.GetFixedInfo();
703 WCHAR wszVersion
[256];
704 swprintf(wszVersion
, L
"%u.%u.%u.%u", HIWORD(pInfo
->dwFileVersionMS
),
705 LOWORD(pInfo
->dwFileVersionMS
),
706 HIWORD(pInfo
->dwFileVersionLS
),
707 LOWORD(pInfo
->dwFileVersionLS
));
708 TRACE("MS %x LS %x ver %s \n", pInfo
->dwFileVersionMS
, pInfo
->dwFileVersionLS
, debugstr_w(wszVersion
));
709 SetDlgItemTextW(hwndDlg
, 14001, wszVersion
);
713 SetVersionLabel(hwndDlg
, 14003, L
"FileDescription");
714 SetVersionLabel(hwndDlg
, 14005, L
"LegalCopyright");
716 /* Add items to listbox */
717 AddVersionString(hwndDlg
, L
"CompanyName");
718 LPCWSTR pwszLang
= m_VerInfo
.GetLangName();
721 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
722 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)L
"Language");
723 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszLang
);
725 AddVersionString(hwndDlg
, L
"ProductName");
726 AddVersionString(hwndDlg
, L
"InternalName");
727 AddVersionString(hwndDlg
, L
"OriginalFilename");
728 AddVersionString(hwndDlg
, L
"FileVersion");
729 AddVersionString(hwndDlg
, L
"ProductVersion");
731 /* Attach file version to dialog window */
732 SetWindowLongPtr(hwndDlg
, DWL_USER
, (LONG_PTR
)this);
734 /* Select first item */
735 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
736 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
737 LPCWSTR pwszText
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
738 if (pwszText
&& pwszText
!= (LPCWSTR
)LB_ERR
)
739 SetDlgItemTextW(hwndDlg
, 14010, pwszText
);
744 /*************************************************************************
746 * CFileDefExt::SetVersionLabel [Internal]
748 * retrieves a version string and uses it to set label text
752 CFileDefExt::SetVersionLabel(HWND hwndDlg
, DWORD idCtrl
, LPCWSTR pwszName
)
754 if (hwndDlg
== NULL
|| pwszName
== NULL
)
757 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
760 /* file description property */
761 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
762 SetDlgItemTextW(hwndDlg
, idCtrl
, pwszValue
);
769 /*************************************************************************
771 * CFileDefExt::AddVersionString [Internal]
773 * retrieves a version string and adds it to listbox
777 CFileDefExt::AddVersionString(HWND hwndDlg
, LPCWSTR pwszName
)
779 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName
), hwndDlg
);
781 if (hwndDlg
== NULL
|| pwszName
== NULL
)
784 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
787 /* listbox name property */
788 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
789 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
790 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
) -1, (LPARAM
)pwszName
);
791 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszValue
);
798 /*************************************************************************
800 * CFileDefExt::VersionPageProc
802 * wnd proc of 'Version' property sheet page
806 CFileDefExt::VersionPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
812 LPPROPSHEETPAGE ppsp
= (LPPROPSHEETPAGE
)lParam
;
814 if (ppsp
== NULL
|| !ppsp
->lParam
)
817 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg
, lParam
, ppsp
->lParam
);
819 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
820 return pFileDefExt
->InitVersionPage(hwndDlg
);
823 if (LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_SELCHANGE
)
825 HWND hDlgCtrl
= (HWND
)lParam
;
827 LRESULT Index
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
831 LPCWSTR pwszData
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)Index
, (LPARAM
)NULL
);
832 if (pwszData
== NULL
)
835 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl
, debugstr_w(pwszData
));
836 SetDlgItemTextW(hwndDlg
, 14010, pwszData
);
850 CFileDefExt::CFileDefExt():
851 m_bDir(FALSE
), m_cFiles(0), m_cFolders(0)
853 m_wszPath
[0] = L
'\0';
854 m_DirSize
.QuadPart
= 0ull;
857 CFileDefExt::~CFileDefExt()
863 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pDataObj
, HKEY hkeyProgID
)
869 TRACE("%p %p %p %p\n", this, pidlFolder
, pDataObj
, hkeyProgID
);
874 format
.cfFormat
= CF_HDROP
;
876 format
.dwAspect
= DVASPECT_CONTENT
;
878 format
.tymed
= TYMED_HGLOBAL
;
880 hr
= pDataObj
->GetData(&format
, &stgm
);
884 if (!DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, m_wszPath
, _countof(m_wszPath
)))
886 ERR("DragQueryFileW failed\n");
887 ReleaseStgMedium(&stgm
);
891 ReleaseStgMedium(&stgm
);
893 TRACE("File properties %ls\n", m_wszPath
);
894 m_bDir
= PathIsDirectoryW(m_wszPath
) ? TRUE
: FALSE
;
896 m_VerInfo
.Load(m_wszPath
);
902 CFileDefExt::QueryContextMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
909 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
916 CFileDefExt::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
*pwReserved
, LPSTR pszName
, UINT cchMax
)
923 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
925 HPROPSHEETPAGE hPage
;
926 WORD wResId
= m_bDir
? IDD_FOLDER_PROPERTIES
: IDD_FILE_PROPERTIES
;
928 hPage
= SH_CreatePropertySheetPage(wResId
,
933 pfnAddPage(hPage
, lParam
);
935 if (!m_bDir
&& GetFileVersionInfoSizeW(m_wszPath
, NULL
))
937 hPage
= SH_CreatePropertySheetPage(IDD_FILE_VERSION
,
942 pfnAddPage(hPage
, lParam
);
949 CFileDefExt::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
956 CFileDefExt::SetSite(IUnknown
*punk
)
963 CFileDefExt::GetSite(REFIID iid
, void **ppvSite
)
970 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter
)
972 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(lpParameter
);
974 data
->This
->CountFolderAndFiles(data
->hwndDlg
, data
->pwszBuf
, data
->cchBufMax
, &ticks
);
976 //Release the CFileDefExt and data object holds in the copying thread.
977 data
->This
->Release();
978 HeapFree(GetProcessHeap(), 0, data
->pwszBuf
);
979 HeapFree(GetProcessHeap(), 0, data
);
985 CFileDefExt::CountFolderAndFiles(HWND hwndDlg
, LPWSTR pwszBuf
, UINT cchBufMax
, DWORD
*ticks
)
987 /* Find filename position */
988 UINT cchBuf
= wcslen(pwszBuf
);
989 WCHAR
*pwszFilename
= pwszBuf
+ cchBuf
;
990 size_t cchFilenameMax
= cchBufMax
- cchBuf
;
993 *(pwszFilename
++) = '\\';
996 /* Find all files, FIXME: shouldn't be "*"? */
997 StringCchCopyW(pwszFilename
, cchFilenameMax
, L
"*");
999 WIN32_FIND_DATAW wfd
;
1000 HANDLE hFind
= FindFirstFileW(pwszBuf
, &wfd
);
1001 if (hFind
== INVALID_HANDLE_VALUE
)
1003 ERR("FindFirstFileW %ls failed\n", pwszBuf
);
1009 *ticks
= GetTickCount();
1015 if (wfd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1017 /* Don't process "." and ".." items */
1018 if (!wcscmp(wfd
.cFileName
, L
".") || !wcscmp(wfd
.cFileName
, L
".."))
1023 StringCchCopyW(pwszFilename
, cchFilenameMax
, wfd
.cFileName
);
1024 CountFolderAndFiles(hwndDlg
, pwszBuf
, cchBufMax
, ticks
);
1030 ULARGE_INTEGER FileSize
;
1031 FileSize
.u
.LowPart
= wfd
.nFileSizeLow
;
1032 FileSize
.u
.HighPart
= wfd
.nFileSizeHigh
;
1033 m_DirSize
.QuadPart
+= FileSize
.QuadPart
;
1035 if (GetTickCount() - *ticks
> (DWORD
) 300)
1037 /* FIXME Using IsWindow is generally ill advised */
1038 if (IsWindow(hwndDlg
))
1040 WCHAR wszBuf
[MAX_PATH
];
1042 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1043 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1045 /* Display files and folders count */
1046 WCHAR wszFormat
[256];
1047 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1048 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1049 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
1050 *ticks
= GetTickCount();
1055 } while(FindNextFileW(hFind
, &wfd
));
1057 if (root
&& IsWindow(hwndDlg
))
1059 WCHAR wszBuf
[MAX_PATH
];
1061 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1062 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1064 /* Display files and folders count */
1065 WCHAR wszFormat
[256];
1066 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1067 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1068 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);