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
, 0);
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)
222 /* Concatenate " (" */
223 UINT cchWritten
= wcslen(pwszResult
);
224 LPWSTR pwszEnd
= pwszResult
+ cchWritten
;
225 size_t cchRemaining
= cchResultMax
- cchWritten
;
226 StringCchCopyExW(pwszEnd
, cchRemaining
, L
" (", &pwszEnd
, &cchRemaining
, 0);
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
)
485 WIN32_FIND_DATAW FileInfo
; // WIN32_FILE_ATTRIBUTE_DATA
486 WCHAR wszBuf
[MAX_PATH
];
488 TRACE("InitFileAttr %ls\n", m_wszPath
);
491 * There are situations where GetFileAttributes(Ex) can fail even if the
492 * specified path represents a file. This happens when e.g. the file is a
493 * locked system file, such as C:\pagefile.sys . In this case, the function
494 * returns INVALID_FILE_ATTRIBUTES and GetLastError returns ERROR_SHARING_VIOLATION.
495 * (this would allow us to distinguish between this failure and a failure
496 * due to the fact that the path actually refers to a directory).
498 * Because we really want to retrieve the file attributes/size/date&time,
499 * we do the following trick:
500 * - First we call GetFileAttributesEx. If it succeeds we know we have
501 * a file or a directory, and we have retrieved its attributes.
502 * - If GetFileAttributesEx fails, we call FindFirstFile on the full path.
503 * While we could have called FindFirstFile at first and skip GetFileAttributesEx
504 * altogether, we do it after GetFileAttributesEx because it performs more
505 * work to retrieve the file attributes. However it actually works even
506 * for locked system files.
507 * - If FindFirstFile succeeds we have retrieved its attributes.
508 * - Otherwise (FindFirstFile has failed), we do not retrieve anything.
510 * The following code also relies on the fact that the first 6 members
511 * of WIN32_FIND_DATA are *exactly* the same as the WIN32_FILE_ATTRIBUTE_DATA
512 * structure. Therefore it is safe to use a single WIN32_FIND_DATA
513 * structure for both the GetFileAttributesEx and FindFirstFile calls.
516 Success
= GetFileAttributesExW(m_wszPath
,
517 GetFileExInfoStandard
,
518 (LPWIN32_FILE_ATTRIBUTE_DATA
)&FileInfo
);
521 HANDLE hFind
= FindFirstFileW(m_wszPath
, &FileInfo
);
522 Success
= (hFind
!= INVALID_HANDLE_VALUE
);
529 /* Update attribute checkboxes */
530 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
531 SendDlgItemMessage(hwndDlg
, 14021, BM_SETCHECK
, BST_CHECKED
, 0);
532 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
)
533 SendDlgItemMessage(hwndDlg
, 14022, BM_SETCHECK
, BST_CHECKED
, 0);
534 if (FileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
)
535 SendDlgItemMessage(hwndDlg
, 14023, BM_SETCHECK
, BST_CHECKED
, 0);
537 /* Update creation time */
538 if (GetFileTimeString(&FileInfo
.ftCreationTime
, wszBuf
, _countof(wszBuf
)))
539 SetDlgItemTextW(hwndDlg
, 14015, wszBuf
);
541 /* For files display last access and last write time */
544 if (GetFileTimeString(&FileInfo
.ftLastAccessTime
, wszBuf
, _countof(wszBuf
)))
545 SetDlgItemTextW(hwndDlg
, 14019, wszBuf
);
547 if (GetFileTimeString(&FileInfo
.ftLastWriteTime
, wszBuf
, _countof(wszBuf
)))
548 SetDlgItemTextW(hwndDlg
, 14017, wszBuf
);
550 /* Update size of file */
551 ULARGE_INTEGER FileSize
;
552 FileSize
.u
.LowPart
= FileInfo
.nFileSizeLow
;
553 FileSize
.u
.HighPart
= FileInfo
.nFileSizeHigh
;
554 if (SH_FormatFileSizeWithBytes(&FileSize
, wszBuf
, _countof(wszBuf
)))
555 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
561 /* For directories files have to be counted */
563 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData
)));
565 data
->pwszBuf
= static_cast<LPWSTR
>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
) * MAX_PATH
));
566 data
->cchBufMax
= MAX_PATH
;
567 data
->hwndDlg
= hwndDlg
;
569 StringCchCopyW(data
->pwszBuf
, MAX_PATH
, m_wszPath
);
571 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc
, data
, NULL
, NULL
);
573 /* Update size field */
574 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
575 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
577 /* Display files and folders count */
578 WCHAR wszFormat
[256];
579 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
580 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
581 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
584 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
585 ShowWindow(GetDlgItem(hwndDlg
, 14028), SW_HIDE
);
590 /*************************************************************************
592 * CFileDefExt::InitGeneralPage [Internal]
594 * sets all file general properties in dialog
598 CFileDefExt::InitGeneralPage(HWND hwndDlg
)
600 /* Set general text properties filename filelocation and icon */
601 InitFilePath(hwndDlg
);
603 /* Set file type and icon */
604 InitFileType(hwndDlg
);
606 /* Set open with application */
609 if (!PathIsExeW(m_wszPath
))
610 InitOpensWithField(hwndDlg
);
613 WCHAR wszBuf
[MAX_PATH
];
614 LoadStringW(shell32_hInstance
, IDS_EXE_DESCRIPTION
, wszBuf
, _countof(wszBuf
));
615 SetDlgItemTextW(hwndDlg
, 14006, wszBuf
);
616 ShowWindow(GetDlgItem(hwndDlg
, 14024), SW_HIDE
);
617 LPCWSTR pwszDescr
= m_VerInfo
.GetString(L
"FileDescription");
619 SetDlgItemTextW(hwndDlg
, 14007, pwszDescr
);
622 StringCbCopyW(wszBuf
, sizeof(wszBuf
), PathFindFileNameW(m_wszPath
));
623 PathRemoveExtension(wszBuf
);
624 SetDlgItemTextW(hwndDlg
, 14007, wszBuf
);
629 /* Set file created/modfied/accessed time, size and attributes */
630 InitFileAttr(hwndDlg
);
635 /*************************************************************************
637 * CFileDefExt::GeneralPageProc
639 * wnd proc of 'General' property sheet page
644 CFileDefExt::GeneralPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
650 LPPROPSHEETPAGEW ppsp
= (LPPROPSHEETPAGEW
)lParam
;
652 if (ppsp
== NULL
|| !ppsp
->lParam
)
655 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg
, lParam
, ppsp
->lParam
);
657 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
658 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pFileDefExt
);
659 pFileDefExt
->InitGeneralPage(hwndDlg
);
663 if (LOWORD(wParam
) == 14024) /* Opens With - Change */
665 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
667 oainfo
.pcszFile
= pFileDefExt
->m_wszPath
;
668 oainfo
.pcszClass
= NULL
;
669 oainfo
.oaifInFlags
= OAIF_REGISTER_EXT
|OAIF_FORCE_REGISTRATION
;
670 return SUCCEEDED(SHOpenWithDialog(hwndDlg
, &oainfo
));
672 else if (LOWORD(wParam
) == 14021 || LOWORD(wParam
) == 14022 || LOWORD(wParam
) == 14023) /* checkboxes */
673 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
674 else if (LOWORD(wParam
) == 14001) /* Name */
676 if (HIWORD(wParam
) == EN_CHANGE
)
677 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
682 LPPSHNOTIFY lppsn
= (LPPSHNOTIFY
)lParam
;
683 if (lppsn
->hdr
.code
== PSN_APPLY
)
685 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
687 /* Update attributes first */
688 DWORD dwAttr
= GetFileAttributesW(pFileDefExt
->m_wszPath
);
691 dwAttr
&= ~(FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_ARCHIVE
);
693 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14021, BM_GETCHECK
, 0, 0))
694 dwAttr
|= FILE_ATTRIBUTE_READONLY
;
695 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14022, BM_GETCHECK
, 0, 0))
696 dwAttr
|= FILE_ATTRIBUTE_HIDDEN
;
697 if (BST_CHECKED
== SendDlgItemMessageW(hwndDlg
, 14023, BM_GETCHECK
, 0, 0))
698 dwAttr
|= FILE_ATTRIBUTE_ARCHIVE
;
700 if (!SetFileAttributesW(pFileDefExt
->m_wszPath
, dwAttr
))
701 ERR("SetFileAttributesW failed\n");
704 /* Update filename now */
705 WCHAR wszBuf
[MAX_PATH
];
706 StringCchCopyW(wszBuf
, _countof(wszBuf
), pFileDefExt
->m_wszPath
);
707 LPWSTR pwszFilename
= PathFindFileNameW(wszBuf
);
708 UINT cchFilenameMax
= _countof(wszBuf
) - (pwszFilename
- wszBuf
);
709 if (GetDlgItemTextW(hwndDlg
, 14001, pwszFilename
, cchFilenameMax
))
711 if (!MoveFileW(pFileDefExt
->m_wszPath
, wszBuf
))
712 ERR("MoveFileW failed\n");
715 SetWindowLongPtr(hwndDlg
, DWL_MSGRESULT
, PSNRET_NOERROR
);
727 /*************************************************************************
729 * CFileDefExt::InitVersionPage [Internal]
731 * sets all file version properties in dialog
735 CFileDefExt::InitVersionPage(HWND hwndDlg
)
738 VS_FIXEDFILEINFO
*pInfo
= m_VerInfo
.GetFixedInfo();
741 WCHAR wszVersion
[256];
742 swprintf(wszVersion
, L
"%u.%u.%u.%u", HIWORD(pInfo
->dwFileVersionMS
),
743 LOWORD(pInfo
->dwFileVersionMS
),
744 HIWORD(pInfo
->dwFileVersionLS
),
745 LOWORD(pInfo
->dwFileVersionLS
));
746 TRACE("MS %x LS %x ver %s \n", pInfo
->dwFileVersionMS
, pInfo
->dwFileVersionLS
, debugstr_w(wszVersion
));
747 SetDlgItemTextW(hwndDlg
, 14001, wszVersion
);
751 SetVersionLabel(hwndDlg
, 14003, L
"FileDescription");
752 SetVersionLabel(hwndDlg
, 14005, L
"LegalCopyright");
754 /* Add items to listbox */
755 AddVersionString(hwndDlg
, L
"CompanyName");
756 LPCWSTR pwszLang
= m_VerInfo
.GetLangName();
759 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
760 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)L
"Language");
761 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszLang
);
763 AddVersionString(hwndDlg
, L
"ProductName");
764 AddVersionString(hwndDlg
, L
"InternalName");
765 AddVersionString(hwndDlg
, L
"OriginalFilename");
766 AddVersionString(hwndDlg
, L
"FileVersion");
767 AddVersionString(hwndDlg
, L
"ProductVersion");
769 /* Attach file version to dialog window */
770 SetWindowLongPtr(hwndDlg
, DWL_USER
, (LONG_PTR
)this);
772 /* Select first item */
773 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
774 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
775 LPCWSTR pwszText
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
776 if (pwszText
&& pwszText
!= (LPCWSTR
)LB_ERR
)
777 SetDlgItemTextW(hwndDlg
, 14010, pwszText
);
782 /*************************************************************************
784 * CFileDefExt::SetVersionLabel [Internal]
786 * retrieves a version string and uses it to set label text
790 CFileDefExt::SetVersionLabel(HWND hwndDlg
, DWORD idCtrl
, LPCWSTR pwszName
)
792 if (hwndDlg
== NULL
|| pwszName
== NULL
)
795 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
798 /* file description property */
799 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
800 SetDlgItemTextW(hwndDlg
, idCtrl
, pwszValue
);
807 /*************************************************************************
809 * CFileDefExt::AddVersionString [Internal]
811 * retrieves a version string and adds it to listbox
815 CFileDefExt::AddVersionString(HWND hwndDlg
, LPCWSTR pwszName
)
817 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName
), hwndDlg
);
819 if (hwndDlg
== NULL
|| pwszName
== NULL
)
822 LPCWSTR pwszValue
= m_VerInfo
.GetString(pwszName
);
825 /* listbox name property */
826 HWND hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
827 TRACE("%s :: %s\n", debugstr_w(pwszName
), debugstr_w(pwszValue
));
828 UINT Index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
) -1, (LPARAM
)pwszName
);
829 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)Index
, (LPARAM
)(WCHAR
*)pwszValue
);
836 /*************************************************************************
838 * CFileDefExt::VersionPageProc
840 * wnd proc of 'Version' property sheet page
844 CFileDefExt::VersionPageProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
850 LPPROPSHEETPAGE ppsp
= (LPPROPSHEETPAGE
)lParam
;
852 if (ppsp
== NULL
|| !ppsp
->lParam
)
855 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg
, lParam
, ppsp
->lParam
);
857 CFileDefExt
*pFileDefExt
= reinterpret_cast<CFileDefExt
*>(ppsp
->lParam
);
858 return pFileDefExt
->InitVersionPage(hwndDlg
);
861 if (LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_SELCHANGE
)
863 HWND hDlgCtrl
= (HWND
)lParam
;
865 LRESULT Index
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
869 LPCWSTR pwszData
= (LPCWSTR
)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)Index
, (LPARAM
)NULL
);
870 if (pwszData
== NULL
)
873 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl
, debugstr_w(pwszData
));
874 SetDlgItemTextW(hwndDlg
, 14010, pwszData
);
888 CFileDefExt::CFileDefExt():
889 m_bDir(FALSE
), m_cFiles(0), m_cFolders(0)
891 m_wszPath
[0] = L
'\0';
892 m_DirSize
.QuadPart
= 0ull;
895 CFileDefExt::~CFileDefExt()
901 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder
, IDataObject
*pDataObj
, HKEY hkeyProgID
)
907 TRACE("%p %p %p %p\n", this, pidlFolder
, pDataObj
, hkeyProgID
);
912 format
.cfFormat
= CF_HDROP
;
914 format
.dwAspect
= DVASPECT_CONTENT
;
916 format
.tymed
= TYMED_HGLOBAL
;
918 hr
= pDataObj
->GetData(&format
, &stgm
);
922 if (!DragQueryFileW((HDROP
)stgm
.hGlobal
, 0, m_wszPath
, _countof(m_wszPath
)))
924 ERR("DragQueryFileW failed\n");
925 ReleaseStgMedium(&stgm
);
929 ReleaseStgMedium(&stgm
);
931 TRACE("File properties %ls\n", m_wszPath
);
932 m_bDir
= PathIsDirectoryW(m_wszPath
) ? TRUE
: FALSE
;
934 m_VerInfo
.Load(m_wszPath
);
940 CFileDefExt::QueryContextMenu(HMENU hmenu
, UINT indexMenu
, UINT idCmdFirst
, UINT idCmdLast
, UINT uFlags
)
947 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
954 CFileDefExt::GetCommandString(UINT_PTR idCmd
, UINT uType
, UINT
*pwReserved
, LPSTR pszName
, UINT cchMax
)
961 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
963 HPROPSHEETPAGE hPage
;
964 WORD wResId
= m_bDir
? IDD_FOLDER_PROPERTIES
: IDD_FILE_PROPERTIES
;
966 hPage
= SH_CreatePropertySheetPage(wResId
,
971 pfnAddPage(hPage
, lParam
);
973 if (!m_bDir
&& GetFileVersionInfoSizeW(m_wszPath
, NULL
))
975 hPage
= SH_CreatePropertySheetPage(IDD_FILE_VERSION
,
980 pfnAddPage(hPage
, lParam
);
987 CFileDefExt::ReplacePage(UINT uPageID
, LPFNADDPROPSHEETPAGE pfnReplacePage
, LPARAM lParam
)
994 CFileDefExt::SetSite(IUnknown
*punk
)
1001 CFileDefExt::GetSite(REFIID iid
, void **ppvSite
)
1008 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter
)
1010 _CountFolderAndFilesData
*data
= static_cast<_CountFolderAndFilesData
*>(lpParameter
);
1012 data
->This
->CountFolderAndFiles(data
->hwndDlg
, data
->pwszBuf
, data
->cchBufMax
, &ticks
);
1014 //Release the CFileDefExt and data object holds in the copying thread.
1015 data
->This
->Release();
1016 HeapFree(GetProcessHeap(), 0, data
->pwszBuf
);
1017 HeapFree(GetProcessHeap(), 0, data
);
1023 CFileDefExt::CountFolderAndFiles(HWND hwndDlg
, LPWSTR pwszBuf
, UINT cchBufMax
, DWORD
*ticks
)
1025 /* Find filename position */
1026 UINT cchBuf
= wcslen(pwszBuf
);
1027 WCHAR
*pwszFilename
= pwszBuf
+ cchBuf
;
1028 size_t cchFilenameMax
= cchBufMax
- cchBuf
;
1029 if (!cchFilenameMax
)
1031 *(pwszFilename
++) = '\\';
1034 /* Find all files, FIXME: shouldn't be "*"? */
1035 StringCchCopyW(pwszFilename
, cchFilenameMax
, L
"*");
1037 WIN32_FIND_DATAW wfd
;
1038 HANDLE hFind
= FindFirstFileW(pwszBuf
, &wfd
);
1039 if (hFind
== INVALID_HANDLE_VALUE
)
1041 ERR("FindFirstFileW %ls failed\n", pwszBuf
);
1047 *ticks
= GetTickCount();
1053 if (wfd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1055 /* Don't process "." and ".." items */
1056 if (!wcscmp(wfd
.cFileName
, L
".") || !wcscmp(wfd
.cFileName
, L
".."))
1061 StringCchCopyW(pwszFilename
, cchFilenameMax
, wfd
.cFileName
);
1062 CountFolderAndFiles(hwndDlg
, pwszBuf
, cchBufMax
, ticks
);
1068 ULARGE_INTEGER FileSize
;
1069 FileSize
.u
.LowPart
= wfd
.nFileSizeLow
;
1070 FileSize
.u
.HighPart
= wfd
.nFileSizeHigh
;
1071 m_DirSize
.QuadPart
+= FileSize
.QuadPart
;
1073 if (GetTickCount() - *ticks
> (DWORD
) 300)
1075 /* FIXME Using IsWindow is generally ill advised */
1076 if (IsWindow(hwndDlg
))
1078 WCHAR wszBuf
[MAX_PATH
];
1080 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1081 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1083 /* Display files and folders count */
1084 WCHAR wszFormat
[256];
1085 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1086 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1087 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);
1088 *ticks
= GetTickCount();
1093 } while(FindNextFileW(hFind
, &wfd
));
1095 if (root
&& IsWindow(hwndDlg
))
1097 WCHAR wszBuf
[MAX_PATH
];
1099 if (SH_FormatFileSizeWithBytes(&m_DirSize
, wszBuf
, _countof(wszBuf
)))
1100 SetDlgItemTextW(hwndDlg
, 14011, wszBuf
);
1102 /* Display files and folders count */
1103 WCHAR wszFormat
[256];
1104 LoadStringW(shell32_hInstance
, IDS_FILE_FOLDER
, wszFormat
, _countof(wszFormat
));
1105 StringCchPrintfW(wszBuf
, _countof(wszBuf
), wszFormat
, m_cFiles
, m_cFolders
);
1106 SetDlgItemTextW(hwndDlg
, 14027, wszBuf
);