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 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 an resource name
248 SH_CreatePropertySheetPage(WORD wDialogId
, DLGPROC pfnDlgProc
, LPARAM lParam
, LPCWSTR pwszTitle
)
250 HRSRC hRes
= FindResourceW(shell32_hInstance
, MAKEINTRESOURCEW(wDialogId
), (LPWSTR
)RT_DIALOG
);
253 ERR("failed to find resource id\n");
257 LPVOID pTemplate
= LoadResource(shell32_hInstance
, hRes
);
258 if (pTemplate
== NULL
)
260 ERR("failed to load resource\n");
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
;
274 Page
.dwFlags
|= PSP_USETITLE
;
276 return CreatePropertySheetPageW(&Page
);
280 CFileDefExt::InitOpensWithField(HWND hwndDlg
)
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
);
288 if (RegGetValueW(HKEY_CLASSES_ROOT
, pwszExt
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
)
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
)
295 /* Get path from command line */
296 ExpandEnvironmentStringsW(wszPath
, wszBuf
, _countof(wszBuf
));
297 PathRemoveArgs(wszBuf
);
298 PathUnquoteSpacesW(wszBuf
);
299 PathSearchAndQualify(wszBuf
, wszPath
, _countof(wszPath
));
302 if (ExtractIconExW(wszPath
, 0, NULL
, &hIcon
, 1))
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
,
319 SendMessageW(hIconCtrl
, STM_SETICON
, (WPARAM
)hIcon
, 0);
321 ERR("Failed to extract icon\n");
323 if (PathFileExistsW(wszPath
))
325 /* Get file description */
326 CFileVersionInfo VerInfo
;
327 VerInfo
.Load(wszPath
);
328 LPCWSTR pwszDescr
= VerInfo
.GetString(L
"FileDescription");
330 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
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
);
343 WARN("RegGetValueW %ls failed\n", wszBuf
);
345 WARN("RegGetValueW %ls failed\n", pwszExt
);
349 /* Unknown application */
350 LoadStringW(shell32_hInstance
, IDS_UNKNOWN_APP
, wszBuf
, _countof(wszBuf
));
351 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
355 /*************************************************************************
357 * SH_FileGeneralFileType [Internal]
359 * retrieves file extension description from registry and sets it in dialog
361 * TODO: retrieve file extension default icon and load it
362 * find executable name from registry, retrieve description from executable
366 CFileDefExt::InitFileType(HWND hwndDlg
)
368 TRACE("path %s\n", debugstr_w(m_wszPath
));
370 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14005);
371 if (hDlgCtrl
== NULL
)
374 /* Get file information */
376 if (!SHGetFileInfoW(m_wszPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
378 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath
, GetLastError());
379 fi
.szTypeName
[0] = L
'\0';
383 LPCWSTR pwszExt
= PathFindExtensionW(m_wszPath
);
388 if (!fi
.szTypeName
[0])
390 /* The file type is unknown, so default to string "FileExtension File" */
391 size_t cchRemaining
= 0;
392 LPWSTR pwszEnd
= NULL
;
394 StringCchPrintfExW(wszBuf
, _countof(wszBuf
), &pwszEnd
, &cchRemaining
, 0, L
"%s ", pwszExt
+ 1);
395 SendMessageW(hDlgCtrl
, WM_GETTEXT
, (WPARAM
)cchRemaining
, (LPARAM
)pwszEnd
);
397 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
401 /* Update file type */
402 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s (%s)", fi
.szTypeName
, pwszExt
);
403 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
407 /* Update file icon */
409 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
411 ERR("No icon %ls\n", m_wszPath
);
416 /*************************************************************************
418 * CFileDefExt::InitFilePath [Internal]
420 * sets file path string and filename string
425 CFileDefExt::InitFilePath(HWND hwndDlg
)
427 /* Find the filename */
428 WCHAR
*pwszFilename
= PathFindFileNameW(m_wszPath
);
430 if (pwszFilename
> m_wszPath
)
433 WCHAR wszLocation
[MAX_PATH
];
434 StringCchCopyNW(wszLocation
, _countof(wszLocation
), m_wszPath
, pwszFilename
- m_wszPath
);
435 PathRemoveBackslashW(wszLocation
);
437 SetDlgItemTextW(hwndDlg
, 14009, wszLocation
);
441 SetDlgItemTextW(hwndDlg
, 14001, pwszFilename
);
446 /*************************************************************************
448 * CFileDefExt::GetFileTimeString [Internal]
450 * formats a given LPFILETIME struct into readable user format
454 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
459 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
462 size_t cchRemaining
= cchResult
;
463 LPWSTR pwszEnd
= pwszResult
;
464 int cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
466 --cchWritten
; // GetDateFormatW returns count with terminating zero
468 ERR("GetDateFormatW failed\n");
469 cchRemaining
-= cchWritten
;
470 pwszEnd
+= cchWritten
;
472 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
474 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
476 --cchWritten
; // GetTimeFormatW returns count with terminating zero
478 ERR("GetTimeFormatW failed\n");
479 TRACE("result %s\n", debugstr_w(pwszResult
));
483 /*************************************************************************
485 * CFileDefExt::InitFileAttr [Internal]
487 * retrieves file information from file and sets in dialog
492 CFileDefExt::InitFileAttr(HWND hwndDlg
)
494 WCHAR wszBuf
[MAX_PATH
];
496 TRACE("InitFileAttr %ls\n", m_wszPath
);
498 WIN32_FILE_ATTRIBUTE_DATA FileInfo
;
499 if (GetFileAttributesExW(m_wszPath
, GetFileExInfoStandard
, &FileInfo
))
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);
509 /* Update creation time */
510 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, _countof(wszBuf
)))
511 SetDlgItemTextW(hwndDlg
, 14015, wszBuf
);
513 /* For files display last access and last write time */
516 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, _countof(wszBuf
)))
517 SetDlgItemTextW(hwndDlg
, 14019, wszBuf
);
519 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, _countof(wszBuf
)))
520 SetDlgItemTextW(hwndDlg
, 14017, wszBuf
);
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
);
533 /* For directories files have to be counted */
535 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData
)));
537 data
->pwszBuf
= static_cast<LPWSTR
>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * MAX_PATH
));
538 data
->cchBufMax
= MAX_PATH
;
539 data
->hwndDlg
= hwndDlg
;
541 StringCchCopyW(data
->pwszBuf
, MAX_PATH
, m_wszPath
);
543 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc
, data
, NULL
, NULL
);
545 /* Update size field */
546 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
547 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
549 /* Display files and folders count */
550 WCHAR wszFormat
[256];
551 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
552 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
553 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
556 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
557 ShowWindow(GetDlgItem(hwndDlg
, 14028), SW_HIDE
);
562 /*************************************************************************
564 * CFileDefExt::InitGeneralPage [Internal]
566 * sets all file general properties in dialog
570 CFileDefExt::InitGeneralPage(HWND hwndDlg
)
572 /* Set general text properties filename filelocation and icon */
573 InitFilePath(hwndDlg
);
575 /* Set file type and icon */
576 InitFileType(hwndDlg
);
578 /* Set open with application */
581 if (!PathIsExeW(m_wszPath
))
582 InitOpensWithField(hwndDlg
);
585 WCHAR wszBuf
[MAX_PATH
];
586 LoadStringW(shell32_hInstance
, IDS_EXE_DESCRIPTION
, wszBuf
, _countof(wszBuf
));
587 SetDlgItemTextW(hwndDlg
, 14006, wszBuf
);
588 ShowWindow(GetDlgItem(hwndDlg
, 14024), SW_HIDE
);
589 LPCWSTR pwszDescr
= m_VerInfo
.GetString(L
"FileDescription");
591 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
594 StringCbCopyW(wszBuf
, sizeof(wszBuf
), PathFindFileNameW(m_wszPath
));
595 PathRemoveExtension(wszBuf
);
596 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
601 /* Set file created/modfied/accessed time, size and attributes */
602 InitFileAttr(hwndDlg
);
607 /*************************************************************************
609 * CFileDefExt::GeneralPageProc
611 * wnd proc of 'General' property sheet page
616 CFileDefExt::GeneralPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
622 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
624 if (ppsp
== NULL
|| !ppsp
->lParam
)
627 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg
, lParam
, ppsp
->lParam
);
629 CFileDefExt
*pFileDefExt
= (CFileDefExt
*)ppsp
->lParam
;
630 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pFileDefExt
);
631 pFileDefExt
->InitGeneralPage(hwndDlg
);
635 if (LOWORD(wParam
) == 14024) /* Opens With - Change */
637 CFileDefExt
*pFileDefExt
= (CFileDefExt
*)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
639 oainfo
.pcszFile
= pFileDefExt
->m_wszPath
;
640 oainfo
.pcszClass
= NULL
;
641 oainfo
.oaifInFlags
= OAIF_REGISTER_EXT
|OAIF_FORCE_REGISTRATION
;
642 return SUCCEEDED(SHOpenWithDialog(hwndDlg
, &oainfo
));
644 else if (LOWORD(wParam
) == 14021 || LOWORD(wParam
) == 14022 || LOWORD(wParam
) == 14023) /* checkboxes */
645 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
646 else if (LOWORD(wParam
) == 14001) /* Name */
648 if (HIWORD(wParam
) == EN_CHANGE
)
649 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
654 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
655 if (lppsn
->hdr
.code
== PSN_APPLY
)
657 CFileDefExt
*pFileDefExt
= (CFileDefExt
*)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
659 /* Update attributes first */
660 DWORD dwAttr
= GetFileAttributesW(pFileDefExt
->m_wszPath
);
663 dwAttr
&= ~(FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_ARCHIVE
);
665 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14021, BM_GETCHECK
, 0, 0))
666 dwAttr
|= FILE_ATTRIBUTE_READONLY
;
667 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14022, BM_GETCHECK
, 0, 0))
668 dwAttr
|= FILE_ATTRIBUTE_HIDDEN
;
669 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14023, BM_GETCHECK
, 0, 0))
670 dwAttr
|= FILE_ATTRIBUTE_ARCHIVE
;
672 if (!SetFileAttributesW(pFileDefExt
->m_wszPath
, dwAttr
))
673 ERR("SetFileAttributesW failed\n");
676 /* Update filename now */
677 WCHAR wszBuf
[MAX_PATH
];
678 StringCchCopyW(wszBuf
, _countof(wszBuf
), pFileDefExt
->m_wszPath
);
679 LPWSTR pwszFilename
= PathFindFileNameW(wszBuf
);
680 UINT cchFilenameMax
= _countof(wszBuf
) - (pwszFilename
- wszBuf
);
681 if (GetDlgItemTextW(hwndDlg
, 14001, pwszFilename
, cchFilenameMax
))
683 if (!MoveFileW(pFileDefExt
->m_wszPath
, wszBuf
))
684 ERR("MoveFileW failed\n");
687 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
699 /*************************************************************************
701 * CFileDefExt::InitVersionPage [Internal]
703 * sets all file version properties in dialog
707 CFileDefExt::InitVersionPage(HWND hwndDlg
)
710 VS_FIXEDFILEINFO
*pInfo
= m_VerInfo
.GetFixedInfo();
713 WCHAR wszVersion
[256];
714 swprintf(wszVersion
, L
"%u.%u.%u.%u", HIWORD(pInfo
->dwFileVersionMS
),
715 LOWORD(pInfo
->dwFileVersionMS
),
716 HIWORD(pInfo
->dwFileVersionLS
),
717 LOWORD(pInfo
->dwFileVersionLS
));
718 TRACE("MS %x LS %x ver %s \n", pInfo
->dwFileVersionMS
, pInfo
->dwFileVersionLS
, debugstr_w(wszVersion
));
719 SetDlgItemTextW(hwndDlg
, 14001, wszVersion
);
723 SetVersionLabel(hwndDlg
, 14003, L
"FileDescription");
724 SetVersionLabel(hwndDlg
, 14005, L
"LegalCopyright");
726 /* Add items to listbox */
727 AddVersionString(hwndDlg
, L
"CompanyName");
728 LPCWSTR pwszLang
= m_VerInfo
.GetLangName();
731 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
732 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)L
"Language");
733 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszLang
);
735 AddVersionString(hwndDlg
, L
"ProductName");
736 AddVersionString(hwndDlg
, L
"InternalName");
737 AddVersionString(hwndDlg
, L
"OriginalFilename");
738 AddVersionString(hwndDlg
, L
"FileVersion");
739 AddVersionString(hwndDlg
, L
"ProductVersion");
741 /* Attach file version to dialog window */
742 SetWindowLongPtr(hwndDlg
, DWL_USER
, (LONG_PTR
)this);
744 /* Select first item */
745 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
746 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
747 LPCWSTR pwszText
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
748 if (pwszText
&& pwszText
!= (LPCWSTR
)LB_ERR
)
749 SetDlgItemTextW(hwndDlg
, 14010, pwszText
);
754 /*************************************************************************
756 * CFileDefExt::SetVersionLabel [Internal]
758 * retrieves a version string and uses it to set label text
762 CFileDefExt::SetVersionLabel(HWND hwndDlg
, DWORD idCtrl
, LPCWSTR pwszName
)
764 if (hwndDlg
== NULL
|| pwszName
== NULL
)
767 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
770 /* file description property */
771 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
772 SetDlgItemTextW(hwndDlg
, idCtrl
, pwszValue
);
779 /*************************************************************************
781 * CFileDefExt::AddVersionString [Internal]
783 * retrieves a version string and adds it to listbox
787 CFileDefExt::AddVersionString(HWND hwndDlg
, LPCWSTR pwszName
)
789 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName
), hwndDlg
);
791 if (hwndDlg
== NULL
|| pwszName
== NULL
)
794 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
797 /* listbox name property */
798 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
799 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
800 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
) -1, (LPARAM
)pwszName
);
801 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszValue
);
808 /*************************************************************************
810 * CFileDefExt::VersionPageProc
812 * wnd proc of 'Version' property sheet page
816 CFileDefExt::VersionPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
822 LPPROPSHEETPAGE ppsp
= (LPPROPSHEETPAGE
)lParam
;
824 if (ppsp
== NULL
|| !ppsp
->lParam
)
827 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg
, lParam
, ppsp
->lParam
);
829 CFileDefExt
*pFileDefExt
= (CFileDefExt
*)ppsp
->lParam
;
830 return pFileDefExt
->InitVersionPage(hwndDlg
);
833 if (LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_SELCHANGE
)
835 HWND hDlgCtrl
= (HWND
)lParam
;
837 LRESULT Index
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
841 LPCWSTR pwszData
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)Index
, (LPARAM
)NULL
);
842 if (pwszData
== NULL
)
845 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl
, debugstr_w(pwszData
));
846 SetDlgItemTextW(hwndDlg
, 14010, pwszData
);
860 CFileDefExt::CFileDefExt():
861 m_bDir(FALSE
), m_cFiles(0), m_cFolders(0)
863 m_wszPath
[0] = L
'\0';
864 m_DirSize
.QuadPart
= 0ull;
867 CFileDefExt::~CFileDefExt()
873 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pDataObj
, HKEY hkeyProgID
)
879 TRACE("%p %p %p %p\n", this, pidlFolder
, pDataObj
, hkeyProgID
);
884 format
.cfFormat
= CF_HDROP
;
886 format
.dwAspect
= DVASPECT_CONTENT
;
888 format
.tymed
= TYMED_HGLOBAL
;
890 hr
= pDataObj
->GetData(&format
, &stgm
);
894 if (!DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, m_wszPath
, _countof(m_wszPath
)))
896 ERR("DragQueryFileW failed\n");
897 ReleaseStgMedium(&stgm
);
901 ReleaseStgMedium(&stgm
);
903 TRACE("File properties %ls\n", m_wszPath
);
904 m_bDir
= PathIsDirectoryW(m_wszPath
) ? TRUE
: FALSE
;
906 m_VerInfo
.Load(m_wszPath
);
912 CFileDefExt::QueryContextMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
919 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
926 CFileDefExt::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
*pwReserved
, LPSTR pszName
, UINT cchMax
)
933 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
935 HPROPSHEETPAGE hPage
;
936 WORD wResId
= m_bDir
? IDD_FOLDER_PROPERTIES
: IDD_FILE_PROPERTIES
;
938 hPage
= SH_CreatePropertySheetPage(wResId
,
943 pfnAddPage(hPage
, lParam
);
945 if (!m_bDir
&& GetFileVersionInfoSizeW(m_wszPath
, NULL
))
947 hPage
= SH_CreatePropertySheetPage(IDD_FILE_VERSION
,
952 pfnAddPage(hPage
, lParam
);
959 CFileDefExt::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
966 CFileDefExt::SetSite(IUnknown
*punk
)
973 CFileDefExt::GetSite(REFIID iid
, void **ppvSite
)
980 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter
)
982 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(lpParameter
);
984 data
->This
->CountFolderAndFiles(data
->hwndDlg
, data
->pwszBuf
, data
->cchBufMax
, &ticks
);
986 //Release the CFileDefExt and data object holds in the copying thread.
987 data
->This
->Release();
988 HeapFree(GetProcessHeap(), 0, data
->pwszBuf
);
989 HeapFree(GetProcessHeap(), 0, data
);
995 CFileDefExt::CountFolderAndFiles(HWND hwndDlg
, LPWSTR pwszBuf
, UINT cchBufMax
, DWORD
*ticks
)
997 /* Find filename position */
998 UINT cchBuf
= wcslen(pwszBuf
);
999 WCHAR
*pwszFilename
= pwszBuf
+ cchBuf
;
1000 size_t cchFilenameMax
= cchBufMax
- cchBuf
;
1001 if (!cchFilenameMax
)
1003 *(pwszFilename
++) = '\\';
1006 /* Find all files, FIXME: shouldn't be "*"? */
1007 StringCchCopyW(pwszFilename
, cchFilenameMax
, L
"*");
1009 WIN32_FIND_DATAW wfd
;
1010 HANDLE hFind
= FindFirstFileW(pwszBuf
, &wfd
);
1011 if (hFind
== INVALID_HANDLE_VALUE
)
1013 ERR("FindFirstFileW %ls failed\n", pwszBuf
);
1019 *ticks
= GetTickCount();
1025 if (wfd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1027 /* Don't process "." and ".." items */
1028 if (!wcscmp(wfd
.cFileName
, L
".") || !wcscmp(wfd
.cFileName
, L
".."))
1033 StringCchCopyW(pwszFilename
, cchFilenameMax
, wfd
.cFileName
);
1034 CountFolderAndFiles(hwndDlg
, pwszBuf
, cchBufMax
, ticks
);
1040 ULARGE_INTEGER FileSize
;
1041 FileSize
.u
.LowPart
= wfd
.nFileSizeLow
;
1042 FileSize
.u
.HighPart
= wfd
.nFileSizeHigh
;
1043 m_DirSize
.QuadPart
+= FileSize
.QuadPart
;
1045 if (GetTickCount() - *ticks
> (DWORD
) 300)
1047 /* FIXME Using IsWindow is generally ill advised */
1048 if (IsWindow(hwndDlg
))
1050 WCHAR wszBuf
[MAX_PATH
];
1052 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1053 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1055 /* Display files and folders count */
1056 WCHAR wszFormat
[256];
1057 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1058 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1059 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
1060 *ticks
= GetTickCount();
1065 } while(FindNextFileW(hFind
, &wfd
));
1067 if (root
&& IsWindow(hwndDlg
))
1069 WCHAR wszBuf
[MAX_PATH
];
1071 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1072 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1074 /* Display files and folders count */
1075 WCHAR wszFormat
[256];
1076 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1077 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1078 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);