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 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
);
310 rcIcon
.right
= rcIcon
.left
+ GetSystemMetrics(SM_CXSMICON
);
311 rcIcon
.bottom
= rcIcon
.top
+ GetSystemMetrics(SM_CYSMICON
);
313 MapWindowPoints(NULL
, hwndDlg
, (LPPOINT
)&rcIcon
, 2);
314 GetWindowRect(hDescrCtrl
, &rcDescr
);
315 MapWindowPoints(NULL
, hwndDlg
, (LPPOINT
)&rcDescr
, 2);
316 INT cxOffset
= rcIcon
.right
+ 2 - rcDescr
.left
;
317 SetWindowPos(hDescrCtrl
, NULL
,
318 rcDescr
.left
+ cxOffset
, rcDescr
.top
,
319 rcDescr
.right
- rcDescr
.left
- cxOffset
, rcDescr
.bottom
- rcDescr
.top
,
321 SendMessageW(hIconCtrl
, STM_SETICON
, (WPARAM
)hIcon
, 0);
323 ERR("Failed to extract icon\n");
325 if (PathFileExistsW(wszPath
))
327 /* Get file description */
328 CFileVersionInfo VerInfo
;
329 VerInfo
.Load(wszPath
);
330 LPCWSTR pwszDescr
= VerInfo
.GetString(L
"FileDescription");
332 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
335 /* File has no description - display filename */
336 LPWSTR pwszFilename
= PathFindFileNameW(wszPath
);
337 PathRemoveExtension(pwszFilename
);
338 pwszFilename
[0] = towupper(pwszFilename
[0]);
339 SetDlgItemTextW(hwndDlg
, 14007, pwszFilename
);
345 WARN("RegGetValueW %ls failed\n", wszBuf
);
347 WARN("RegGetValueW %ls failed\n", pwszExt
);
351 /* Unknown application */
352 LoadStringW(shell32_hInstance
, IDS_UNKNOWN_APP
, wszBuf
, _countof(wszBuf
));
353 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
357 /*************************************************************************
359 * SH_FileGeneralFileType [Internal]
361 * retrieves file extension description from registry and sets it in dialog
363 * TODO: retrieve file extension default icon and load it
364 * find executable name from registry, retrieve description from executable
368 CFileDefExt::InitFileType(HWND hwndDlg
)
370 TRACE("path %s\n", debugstr_w(m_wszPath
));
372 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14005);
373 if (hDlgCtrl
== NULL
)
376 /* Get file information */
378 if (!SHGetFileInfoW(m_wszPath
, 0, &fi
, sizeof(fi
), SHGFI_TYPENAME
|SHGFI_ICON
))
380 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath
, GetLastError());
381 fi
.szTypeName
[0] = L
'\0';
385 LPCWSTR pwszExt
= PathFindExtensionW(m_wszPath
);
390 if (!fi
.szTypeName
[0])
392 /* The file type is unknown, so default to string "FileExtension File" */
393 size_t cchRemaining
= 0;
394 LPWSTR pwszEnd
= NULL
;
396 StringCchPrintfExW(wszBuf
, _countof(wszBuf
), &pwszEnd
, &cchRemaining
, 0, L
"%s ", pwszExt
+ 1);
397 SendMessageW(hDlgCtrl
, WM_GETTEXT
, (WPARAM
)cchRemaining
, (LPARAM
)pwszEnd
);
399 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
403 /* Update file type */
404 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s (%s)", fi
.szTypeName
, pwszExt
);
405 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)wszBuf
);
409 /* Update file icon */
411 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)fi
.hIcon
, 0);
413 ERR("No icon %ls\n", m_wszPath
);
418 /*************************************************************************
420 * CFileDefExt::InitFilePath [Internal]
422 * sets file path string and filename string
427 CFileDefExt::InitFilePath(HWND hwndDlg
)
429 /* Find the filename */
430 WCHAR
*pwszFilename
= PathFindFileNameW(m_wszPath
);
432 if (pwszFilename
> m_wszPath
)
435 WCHAR wszLocation
[MAX_PATH
];
436 StringCchCopyNW(wszLocation
, _countof(wszLocation
), m_wszPath
, pwszFilename
- m_wszPath
);
437 PathRemoveBackslashW(wszLocation
);
439 SetDlgItemTextW(hwndDlg
, 14009, wszLocation
);
443 SetDlgItemTextW(hwndDlg
, 14001, pwszFilename
);
448 /*************************************************************************
450 * CFileDefExt::GetFileTimeString [Internal]
452 * formats a given LPFILETIME struct into readable user format
456 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime
, LPWSTR pwszResult
, UINT cchResult
)
461 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
) || !FileTimeToSystemTime(&ft
, &st
))
464 size_t cchRemaining
= cchResult
;
465 LPWSTR pwszEnd
= pwszResult
;
466 int cchWritten
= GetDateFormatW(LOCALE_USER_DEFAULT
, DATE_LONGDATE
, &st
, NULL
, pwszEnd
, cchRemaining
);
468 --cchWritten
; // GetDateFormatW returns count with terminating zero
470 ERR("GetDateFormatW failed\n");
471 cchRemaining
-= cchWritten
;
472 pwszEnd
+= cchWritten
;
474 StringCchCopyExW(pwszEnd
, cchRemaining
, L
", ", &pwszEnd
, &cchRemaining
, 0);
476 cchWritten
= GetTimeFormatW(LOCALE_USER_DEFAULT
, 0, &st
, NULL
, pwszEnd
, cchRemaining
);
478 --cchWritten
; // GetTimeFormatW returns count with terminating zero
480 ERR("GetTimeFormatW failed\n");
481 TRACE("result %s\n", debugstr_w(pwszResult
));
485 /*************************************************************************
487 * CFileDefExt::InitFileAttr [Internal]
489 * retrieves file information from file and sets in dialog
494 CFileDefExt::InitFileAttr(HWND hwndDlg
)
496 WCHAR wszBuf
[MAX_PATH
];
498 TRACE("InitFileAttr %ls\n", m_wszPath
);
500 WIN32_FILE_ATTRIBUTE_DATA FileInfo
;
501 if (GetFileAttributesExW(m_wszPath
, GetFileExInfoStandard
, &FileInfo
))
503 /* Update attribute checkboxes */
504 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
505 SendDlgItemMessage(hwndDlg
, 14021, BM_SETCHECK
, BST_CHECKED
, 0);
506 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
507 SendDlgItemMessage(hwndDlg
, 14022, BM_SETCHECK
, BST_CHECKED
, 0);
508 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
)
509 SendDlgItemMessage(hwndDlg
, 14023, BM_SETCHECK
, BST_CHECKED
, 0);
511 /* Update creation time */
512 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, _countof(wszBuf
)))
513 SetDlgItemTextW(hwndDlg
, 14015, wszBuf
);
515 /* For files display last access and last write time */
518 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, _countof(wszBuf
)))
519 SetDlgItemTextW(hwndDlg
, 14019, wszBuf
);
521 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, _countof(wszBuf
)))
522 SetDlgItemTextW(hwndDlg
, 14017, wszBuf
);
524 /* Update size of file */
525 ULARGE_INTEGER FileSize
;
526 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
527 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
528 if (SH_FormatFileSizeWithBytes(&FileSize
, wszBuf
, _countof(wszBuf
)))
529 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
535 /* For directories files have to be counted */
537 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData
)));
539 data
->pwszBuf
= static_cast<LPWSTR
>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * MAX_PATH
));
540 data
->cchBufMax
= MAX_PATH
;
541 data
->hwndDlg
= hwndDlg
;
543 StringCchCopyW(data
->pwszBuf
, MAX_PATH
, m_wszPath
);
545 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc
, data
, NULL
, NULL
);
547 /* Update size field */
548 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
549 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
551 /* Display files and folders count */
552 WCHAR wszFormat
[256];
553 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
554 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
555 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
558 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
559 ShowWindow(GetDlgItem(hwndDlg
, 14028), SW_HIDE
);
564 /*************************************************************************
566 * CFileDefExt::InitGeneralPage [Internal]
568 * sets all file general properties in dialog
572 CFileDefExt::InitGeneralPage(HWND hwndDlg
)
574 /* Set general text properties filename filelocation and icon */
575 InitFilePath(hwndDlg
);
577 /* Set file type and icon */
578 InitFileType(hwndDlg
);
580 /* Set open with application */
583 if (!PathIsExeW(m_wszPath
))
584 InitOpensWithField(hwndDlg
);
587 WCHAR wszBuf
[MAX_PATH
];
588 LoadStringW(shell32_hInstance
, IDS_EXE_DESCRIPTION
, wszBuf
, _countof(wszBuf
));
589 SetDlgItemTextW(hwndDlg
, 14006, wszBuf
);
590 ShowWindow(GetDlgItem(hwndDlg
, 14024), SW_HIDE
);
591 LPCWSTR pwszDescr
= m_VerInfo
.GetString(L
"FileDescription");
593 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
596 StringCbCopyW(wszBuf
, sizeof(wszBuf
), PathFindFileNameW(m_wszPath
));
597 PathRemoveExtension(wszBuf
);
598 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
603 /* Set file created/modfied/accessed time, size and attributes */
604 InitFileAttr(hwndDlg
);
609 /*************************************************************************
611 * CFileDefExt::GeneralPageProc
613 * wnd proc of 'General' property sheet page
618 CFileDefExt::GeneralPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
624 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
626 if (ppsp
== NULL
|| !ppsp
->lParam
)
629 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg
, lParam
, ppsp
->lParam
);
631 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
632 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pFileDefExt
);
633 pFileDefExt
->InitGeneralPage(hwndDlg
);
637 if (LOWORD(wParam
) == 14024) /* Opens With - Change */
639 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
641 oainfo
.pcszFile
= pFileDefExt
->m_wszPath
;
642 oainfo
.pcszClass
= NULL
;
643 oainfo
.oaifInFlags
= OAIF_REGISTER_EXT
|OAIF_FORCE_REGISTRATION
;
644 return SUCCEEDED(SHOpenWithDialog(hwndDlg
, &oainfo
));
646 else if (LOWORD(wParam
) == 14021 || LOWORD(wParam
) == 14022 || LOWORD(wParam
) == 14023) /* checkboxes */
647 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
648 else if (LOWORD(wParam
) == 14001) /* Name */
650 if (HIWORD(wParam
) == EN_CHANGE
)
651 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
656 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
657 if (lppsn
->hdr
.code
== PSN_APPLY
)
659 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
661 /* Update attributes first */
662 DWORD dwAttr
= GetFileAttributesW(pFileDefExt
->m_wszPath
);
665 dwAttr
&= ~(FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_ARCHIVE
);
667 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14021, BM_GETCHECK
, 0, 0))
668 dwAttr
|= FILE_ATTRIBUTE_READONLY
;
669 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14022, BM_GETCHECK
, 0, 0))
670 dwAttr
|= FILE_ATTRIBUTE_HIDDEN
;
671 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14023, BM_GETCHECK
, 0, 0))
672 dwAttr
|= FILE_ATTRIBUTE_ARCHIVE
;
674 if (!SetFileAttributesW(pFileDefExt
->m_wszPath
, dwAttr
))
675 ERR("SetFileAttributesW failed\n");
678 /* Update filename now */
679 WCHAR wszBuf
[MAX_PATH
];
680 StringCchCopyW(wszBuf
, _countof(wszBuf
), pFileDefExt
->m_wszPath
);
681 LPWSTR pwszFilename
= PathFindFileNameW(wszBuf
);
682 UINT cchFilenameMax
= _countof(wszBuf
) - (pwszFilename
- wszBuf
);
683 if (GetDlgItemTextW(hwndDlg
, 14001, pwszFilename
, cchFilenameMax
))
685 if (!MoveFileW(pFileDefExt
->m_wszPath
, wszBuf
))
686 ERR("MoveFileW failed\n");
689 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
701 /*************************************************************************
703 * CFileDefExt::InitVersionPage [Internal]
705 * sets all file version properties in dialog
709 CFileDefExt::InitVersionPage(HWND hwndDlg
)
712 VS_FIXEDFILEINFO
*pInfo
= m_VerInfo
.GetFixedInfo();
715 WCHAR wszVersion
[256];
716 swprintf(wszVersion
, L
"%u.%u.%u.%u", HIWORD(pInfo
->dwFileVersionMS
),
717 LOWORD(pInfo
->dwFileVersionMS
),
718 HIWORD(pInfo
->dwFileVersionLS
),
719 LOWORD(pInfo
->dwFileVersionLS
));
720 TRACE("MS %x LS %x ver %s \n", pInfo
->dwFileVersionMS
, pInfo
->dwFileVersionLS
, debugstr_w(wszVersion
));
721 SetDlgItemTextW(hwndDlg
, 14001, wszVersion
);
725 SetVersionLabel(hwndDlg
, 14003, L
"FileDescription");
726 SetVersionLabel(hwndDlg
, 14005, L
"LegalCopyright");
728 /* Add items to listbox */
729 AddVersionString(hwndDlg
, L
"CompanyName");
730 LPCWSTR pwszLang
= m_VerInfo
.GetLangName();
733 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
734 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)L
"Language");
735 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszLang
);
737 AddVersionString(hwndDlg
, L
"ProductName");
738 AddVersionString(hwndDlg
, L
"InternalName");
739 AddVersionString(hwndDlg
, L
"OriginalFilename");
740 AddVersionString(hwndDlg
, L
"FileVersion");
741 AddVersionString(hwndDlg
, L
"ProductVersion");
743 /* Attach file version to dialog window */
744 SetWindowLongPtr(hwndDlg
, DWL_USER
, (LONG_PTR
)this);
746 /* Select first item */
747 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
748 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
749 LPCWSTR pwszText
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
750 if (pwszText
&& pwszText
!= (LPCWSTR
)LB_ERR
)
751 SetDlgItemTextW(hwndDlg
, 14010, pwszText
);
756 /*************************************************************************
758 * CFileDefExt::SetVersionLabel [Internal]
760 * retrieves a version string and uses it to set label text
764 CFileDefExt::SetVersionLabel(HWND hwndDlg
, DWORD idCtrl
, LPCWSTR pwszName
)
766 if (hwndDlg
== NULL
|| pwszName
== NULL
)
769 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
772 /* file description property */
773 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
774 SetDlgItemTextW(hwndDlg
, idCtrl
, pwszValue
);
781 /*************************************************************************
783 * CFileDefExt::AddVersionString [Internal]
785 * retrieves a version string and adds it to listbox
789 CFileDefExt::AddVersionString(HWND hwndDlg
, LPCWSTR pwszName
)
791 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName
), hwndDlg
);
793 if (hwndDlg
== NULL
|| pwszName
== NULL
)
796 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
799 /* listbox name property */
800 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
801 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
802 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
) -1, (LPARAM
)pwszName
);
803 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszValue
);
810 /*************************************************************************
812 * CFileDefExt::VersionPageProc
814 * wnd proc of 'Version' property sheet page
818 CFileDefExt::VersionPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
824 LPPROPSHEETPAGE ppsp
= (LPPROPSHEETPAGE
)lParam
;
826 if (ppsp
== NULL
|| !ppsp
->lParam
)
829 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg
, lParam
, ppsp
->lParam
);
831 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
832 return pFileDefExt
->InitVersionPage(hwndDlg
);
835 if (LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_SELCHANGE
)
837 HWND hDlgCtrl
= (HWND
)lParam
;
839 LRESULT Index
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
843 LPCWSTR pwszData
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)Index
, (LPARAM
)NULL
);
844 if (pwszData
== NULL
)
847 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl
, debugstr_w(pwszData
));
848 SetDlgItemTextW(hwndDlg
, 14010, pwszData
);
862 CFileDefExt::CFileDefExt():
863 m_bDir(FALSE
), m_cFiles(0), m_cFolders(0)
865 m_wszPath
[0] = L
'\0';
866 m_DirSize
.QuadPart
= 0ull;
869 CFileDefExt::~CFileDefExt()
875 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pDataObj
, HKEY hkeyProgID
)
881 TRACE("%p %p %p %p\n", this, pidlFolder
, pDataObj
, hkeyProgID
);
886 format
.cfFormat
= CF_HDROP
;
888 format
.dwAspect
= DVASPECT_CONTENT
;
890 format
.tymed
= TYMED_HGLOBAL
;
892 hr
= pDataObj
->GetData(&format
, &stgm
);
896 if (!DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, m_wszPath
, _countof(m_wszPath
)))
898 ERR("DragQueryFileW failed\n");
899 ReleaseStgMedium(&stgm
);
903 ReleaseStgMedium(&stgm
);
905 TRACE("File properties %ls\n", m_wszPath
);
906 m_bDir
= PathIsDirectoryW(m_wszPath
) ? TRUE
: FALSE
;
908 m_VerInfo
.Load(m_wszPath
);
914 CFileDefExt::QueryContextMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
921 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
928 CFileDefExt::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
*pwReserved
, LPSTR pszName
, UINT cchMax
)
935 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
937 HPROPSHEETPAGE hPage
;
938 WORD wResId
= m_bDir
? IDD_FOLDER_PROPERTIES
: IDD_FILE_PROPERTIES
;
940 hPage
= SH_CreatePropertySheetPage(wResId
,
945 pfnAddPage(hPage
, lParam
);
947 if (!m_bDir
&& GetFileVersionInfoSizeW(m_wszPath
, NULL
))
949 hPage
= SH_CreatePropertySheetPage(IDD_FILE_VERSION
,
954 pfnAddPage(hPage
, lParam
);
961 CFileDefExt::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
968 CFileDefExt::SetSite(IUnknown
*punk
)
975 CFileDefExt::GetSite(REFIID iid
, void **ppvSite
)
982 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter
)
984 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(lpParameter
);
986 data
->This
->CountFolderAndFiles(data
->hwndDlg
, data
->pwszBuf
, data
->cchBufMax
, &ticks
);
988 //Release the CFileDefExt and data object holds in the copying thread.
989 data
->This
->Release();
990 HeapFree(GetProcessHeap(), 0, data
->pwszBuf
);
991 HeapFree(GetProcessHeap(), 0, data
);
997 CFileDefExt::CountFolderAndFiles(HWND hwndDlg
, LPWSTR pwszBuf
, UINT cchBufMax
, DWORD
*ticks
)
999 /* Find filename position */
1000 UINT cchBuf
= wcslen(pwszBuf
);
1001 WCHAR
*pwszFilename
= pwszBuf
+ cchBuf
;
1002 size_t cchFilenameMax
= cchBufMax
- cchBuf
;
1003 if (!cchFilenameMax
)
1005 *(pwszFilename
++) = '\\';
1008 /* Find all files, FIXME: shouldn't be "*"? */
1009 StringCchCopyW(pwszFilename
, cchFilenameMax
, L
"*");
1011 WIN32_FIND_DATAW wfd
;
1012 HANDLE hFind
= FindFirstFileW(pwszBuf
, &wfd
);
1013 if (hFind
== INVALID_HANDLE_VALUE
)
1015 ERR("FindFirstFileW %ls failed\n", pwszBuf
);
1021 *ticks
= GetTickCount();
1027 if (wfd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1029 /* Don't process "." and ".." items */
1030 if (!wcscmp(wfd
.cFileName
, L
".") || !wcscmp(wfd
.cFileName
, L
".."))
1035 StringCchCopyW(pwszFilename
, cchFilenameMax
, wfd
.cFileName
);
1036 CountFolderAndFiles(hwndDlg
, pwszBuf
, cchBufMax
, ticks
);
1042 ULARGE_INTEGER FileSize
;
1043 FileSize
.u
.LowPart
= wfd
.nFileSizeLow
;
1044 FileSize
.u
.HighPart
= wfd
.nFileSizeHigh
;
1045 m_DirSize
.QuadPart
+= FileSize
.QuadPart
;
1047 if (GetTickCount() - *ticks
> (DWORD
) 300)
1049 /* FIXME Using IsWindow is generally ill advised */
1050 if (IsWindow(hwndDlg
))
1052 WCHAR wszBuf
[MAX_PATH
];
1054 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1055 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1057 /* Display files and folders count */
1058 WCHAR wszFormat
[256];
1059 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1060 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1061 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
1062 *ticks
= GetTickCount();
1067 } while(FindNextFileW(hFind
, &wfd
));
1069 if (root
&& IsWindow(hwndDlg
))
1071 WCHAR wszBuf
[MAX_PATH
];
1073 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1074 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1076 /* Display files and folders count */
1077 WCHAR wszFormat
[256];
1078 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1079 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1080 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);