2 * Shell Library Functions
4 * Copyright 2005 Johannes Anderwald
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define NONAMELESSUNION
25 #include "wine/port.h"
36 #include "wine/debug.h"
41 #include "shell32_main.h"
43 #include "undocshell.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
48 #define MAX_PROPERTY_SHEET_PAGE 32
50 typedef struct _LANGANDCODEPAGE_
54 } LANGANDCODEPAGE
, *LPLANGANDCODEPAGE
;
56 /*************************************************************************
58 * SH_CreatePropertySheetPage [Internal]
60 * creates a property sheet page from an resource name
64 SH_CreatePropertySheetPage(LPSTR resname
, DLGPROC dlgproc
, LPARAM lParam
, LPWSTR szTitle
)
71 return (HPROPSHEETPAGE
)0;
73 hRes
= FindResourceA(shell32_hInstance
, resname
, (LPSTR
)RT_DIALOG
);
77 ERR("failed to find resource name\n");
78 return (HPROPSHEETPAGE
)0;
80 lpsztemplate
= LoadResource(shell32_hInstance
, hRes
);
81 if (lpsztemplate
== NULL
)
82 return (HPROPSHEETPAGE
)0;
84 memset(&ppage
, 0x0, sizeof(PROPSHEETPAGEW
));
85 ppage
.dwSize
= sizeof(PROPSHEETPAGEW
);
86 ppage
.dwFlags
= PSP_DLGINDIRECT
;
87 ppage
.u
.pResource
= lpsztemplate
;
88 ppage
.pfnDlgProc
= dlgproc
;
89 ppage
.lParam
= lParam
;
90 ppage
.pszTitle
= szTitle
;
93 ppage
.dwFlags
|= PSP_USETITLE
;
95 return CreatePropertySheetPageW(&ppage
);
104 /*************************************************************************
106 * SH_FileGeneralFileType [Internal]
108 * retrieves file extension description from registry and sets it in dialog
110 * TODO: retrieve file extension default icon and load it
111 * find executable name from registry, retrieve description from executable
115 SH_FileGeneralSetFileType(HWND hwndDlg
, WCHAR
* filext
)
117 WCHAR name
[MAX_PATH
];
118 WCHAR value
[MAX_PATH
];
119 DWORD lname
= MAX_PATH
;
120 DWORD lvalue
= MAX_PATH
;
126 TRACE("fileext %s\n", debugstr_w(filext
));
131 hDlgCtrl
= GetDlgItem(hwndDlg
, 14005);
133 if (hDlgCtrl
== NULL
)
136 if (RegOpenKeyW(HKEY_CLASSES_ROOT
, filext
, &hKey
) != ERROR_SUCCESS
)
138 /* the fileextension is unknown, so default to string "FileExtension File" */
139 SendMessageW(hDlgCtrl
, WM_GETTEXT
, (WPARAM
)MAX_PATH
, (LPARAM
)value
);
140 sprintfW(name
, value
, &filext
[1]);
141 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)name
);
144 result
= RegEnumValueW(hKey
, 0, name
, &lname
, NULL
, NULL
, (LPBYTE
)value
, &lvalue
);
147 if (result
!= ERROR_SUCCESS
)
149 if (RegOpenKeyW(HKEY_CLASSES_ROOT
, value
, &hKey
) == ERROR_SUCCESS
)
151 if (RegLoadMUIStringW(hKey
, L
"FriendlyTypeName", value
, MAX_PATH
, NULL
, 0, NULL
) != ERROR_SUCCESS
)
153 lvalue
= lname
= MAX_PATH
;
154 result
= RegEnumValueW(hKey
,0, name
, &lname
, NULL
, NULL
, (LPBYTE
)value
, &lvalue
);
157 if (RegGetValueW(hKey
, L
"DefaultIcon", NULL
, RRF_RT_REG_SZ
, NULL
, name
, &lname
) == ERROR_SUCCESS
)
160 WCHAR szBuffer
[MAX_PATH
];
164 LPVOID pResource
= NULL
;
167 Offset
= wcsrchr(name
, L
',');
170 IconIndex
= _wtoi(Offset
+ 2);
172 name
[MAX_PATH
-1] = L
'\0';
173 if (ExpandEnvironmentStringsW(name
, szBuffer
, MAX_PATH
))
175 szBuffer
[MAX_PATH
-1] = L
'\0';
176 hLibrary
= LoadLibraryExW(szBuffer
, NULL
, LOAD_LIBRARY_AS_DATAFILE
);
179 hResource
= FindResourceW(hLibrary
, MAKEINTRESOURCEW(IconIndex
), (LPCWSTR
)RT_ICON
);
182 hGlobal
= LoadResource(shell32_hInstance
, hResource
);
185 pResource
= LockResource(hGlobal
);
186 if (pResource
!= NULL
)
188 hIcon
= CreateIconFromResource(pResource
, SizeofResource(shell32_hInstance
, hResource
), TRUE
, 0x00030000);
189 TRACE("hIcon %p,- szBuffer %s IconIndex %u error %u icon %p hResource %p pResource %p\n", hIcon
, debugstr_w(szBuffer
), IconIndex
, MAKEINTRESOURCEW(IconIndex
), hResource
, pResource
);
190 SendDlgItemMessageW(hwndDlg
, 14000, STM_SETICON
, (WPARAM
)hIcon
, 0);
194 FreeLibrary(hLibrary
);
202 /* file extension type */
203 value
[MAX_PATH
-1] = L
'\0';
204 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)value
);
207 /*************************************************************************
209 * SHFileGeneralGetFileTimeString [Internal]
211 * formats a given LPFILETIME struct into readable user format
215 SHFileGeneralGetFileTimeString(LPFILETIME lpFileTime
, WCHAR
* lpResult
)
220 static const WCHAR wFormat
[] = {'%','0','2','d','/','%','0','2','d','/','%','0','4','d',' ',' ','%','0','2','d',':','%','0','2','u',0};
222 if (lpFileTime
== NULL
|| lpResult
== NULL
)
225 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
))
228 FileTimeToSystemTime(&ft
, &dt
);
232 sprintfW (lpResult
, wFormat
, dt
.wDay
, dt
.wMonth
, wYear
, dt
.wHour
, dt
.wMinute
);
234 TRACE("result %s\n",debugstr_w(lpResult
));
238 /*************************************************************************
240 * SH_FileGeneralSetText [Internal]
242 * sets file path string and filename string
247 SH_FileGeneralSetText(HWND hwndDlg
, WCHAR
* lpstr
)
252 WCHAR buff
[MAX_PATH
];
258 lpdir
= strrchrW(lpstr
, '\\'); /* find the last occurence of '\\' */
260 plength
= strlenW(lpstr
);
261 flength
= strlenW(lpdir
);
265 /* location text field */
266 strncpyW(buff
, lpstr
, plength
- flength
);
267 buff
[plength
- flength
] = UNICODE_NULL
;
268 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
269 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
274 /* text filename field */
275 strncpyW(buff
, &lpdir
[1], flength
);
276 hDlgCtrl
= GetDlgItem(hwndDlg
, 14001);
277 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
283 /*************************************************************************
285 * SH_FileGeneralSetFileSizeTime [Internal]
287 * retrieves file information from file and sets in dialog
292 SH_FileGeneralSetFileSizeTime(HWND hwndDlg
, WCHAR
* lpfilename
, PULARGE_INTEGER lpfilesize
)
296 FILETIME create_time
;
297 FILETIME accessed_time
;
299 WCHAR resultstr
[MAX_PATH
];
301 LARGE_INTEGER file_size
;
303 if (lpfilename
== NULL
)
306 hFile
= CreateFileW(lpfilename
,
308 FILE_SHARE_READ
,NULL
,
310 FILE_ATTRIBUTE_NORMAL
,
313 if (hFile
== INVALID_HANDLE_VALUE
)
315 WARN("failed to open file %s\n", debugstr_w(lpfilename
));
319 result
= GetFileTime(hFile
, &create_time
, &accessed_time
, &write_time
);
323 WARN("GetFileTime failed\n");
326 if (SHFileGeneralGetFileTimeString(&create_time
,resultstr
))
328 hDlgCtrl
= GetDlgItem(hwndDlg
, 14015);
329 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
332 if (SHFileGeneralGetFileTimeString(&accessed_time
, resultstr
))
334 hDlgCtrl
= GetDlgItem(hwndDlg
, 14017);
335 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
338 if (SHFileGeneralGetFileTimeString(&write_time
, resultstr
))
340 hDlgCtrl
= GetDlgItem(hwndDlg
, 14019);
341 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
344 if (!GetFileSizeEx(hFile
, &file_size
))
346 WARN("GetFileSize failed\n");
351 if (!StrFormatByteSizeW(file_size
.QuadPart
, resultstr
, sizeof(resultstr
) / sizeof(WCHAR
)))
353 hDlgCtrl
= GetDlgItem(hwndDlg
, 14011);
354 TRACE("result size %u resultstr %s\n", file_size
.QuadPart
, debugstr_w(resultstr
));
355 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
358 lpfilesize
->QuadPart
= (ULONGLONG
)file_size
.QuadPart
;
363 /*************************************************************************
365 * SH_SetFileVersionText [Internal]
371 SH_FileVersionQuerySetText(HWND hwndDlg
, DWORD dlgId
, LPVOID pInfo
, WCHAR
* text
, WCHAR
** resptr
)
376 if(hwndDlg
== NULL
|| resptr
== NULL
|| text
== NULL
)
379 if(VerQueryValueW(pInfo
, text
, (LPVOID
*)resptr
, &reslen
))
381 /* file description property */
382 hDlgCtrl
= GetDlgItem(hwndDlg
, dlgId
);
383 TRACE("%s :: %s\n",debugstr_w(text
), debugstr_w(*resptr
));
384 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)*resptr
);
390 /*************************************************************************
392 * SH_FileVersionQuerySetListText [Internal]
394 * retrieves a version string and adds it to listbox
400 SH_FileVersionQuerySetListText(HWND hwndDlg
, LPVOID pInfo
, const WCHAR
* text
, WCHAR
**resptr
, WORD lang
, WORD code
)
405 static const WCHAR wFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n',
406 'f','o','\\','%','0','4','x','%','0','4','x','\\','%','s',0 };
409 TRACE("text %s, resptr %p hwndDlg %p\n",debugstr_w(text
), resptr
, hwndDlg
);
411 if(hwndDlg
== NULL
|| resptr
== NULL
|| text
== NULL
)
414 sprintfW(buff
, wFormat
, lang
, code
, text
);
415 if(VerQueryValueW(pInfo
, buff
, (LPVOID
*)resptr
, &reslen
))
417 /* listbox name property */
418 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
419 TRACE("%s :: %s\n",debugstr_w(text
), debugstr_w(*resptr
));
420 index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)text
);
421 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)(WCHAR
*)*resptr
);
427 /*************************************************************************
429 * SH_FileVersionInitialize [Internal]
431 * sets all file version properties in dialog
434 SH_FileVersionInitialize(HWND hwndDlg
, WCHAR
* lpfilename
)
445 LPLANGANDCODEPAGE lplangcode
;
447 static const WCHAR wVersionFormat
[] = { '%','d','.','%','d','.','%','d','.','%','d',0 };
448 static const WCHAR wFileDescriptionFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
449 '\\','%','0','4','x','%','0','4','x','\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
450 static const WCHAR wLegalCopyrightFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
451 '\\','%','0','4','x','%','0','4','x','\\','L','e','g','a','l','C','o','p','y','r','i','g','h','t',0 };
452 static const WCHAR wTranslation
[] = { 'V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0 };
453 static const WCHAR wCompanyName
[] = { 'C','o','m','p','a','n','y','N','a','m','e',0 };
454 static const WCHAR wFileVersion
[] = { 'F','i','l','e','V','e','r','s','i','o','n',0 };
455 static const WCHAR wInternalName
[] = { 'I','n','t','e','r','n','a','l','N','a','m','e',0 };
456 static const WCHAR wOriginalFilename
[] = { 'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0 };
457 static const WCHAR wProductName
[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
458 static const WCHAR wProductVersion
[] = { 'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0 };
459 static const WCHAR wSlash
[] = { '\\',0 };
465 if(!(versize
= GetFileVersionInfoSizeW(lpfilename
, &handle
)))
467 WARN("GetFileVersionInfoSize failed\n");
471 if(!(pBuf
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, versize
)))
473 WARN("HeapAlloc failed bytes %x\n",versize
);
477 if(!GetFileVersionInfoW(lpfilename
, handle
, versize
, pBuf
))
479 HeapFree(GetProcessHeap(), 0, pBuf
);
482 if(VerQueryValueW(pBuf
, wSlash
, &info
, &infolen
))
484 VS_FIXEDFILEINFO
* inf
= (VS_FIXEDFILEINFO
*)info
;
485 sprintfW(buff
, wVersionFormat
, HIWORD(inf
->dwFileVersionMS
),
486 LOWORD(inf
->dwFileVersionMS
),
487 HIWORD(inf
->dwFileVersionLS
),
488 LOWORD(inf
->dwFileVersionLS
));
490 hDlgCtrl
= GetDlgItem(hwndDlg
, 14001);
491 TRACE("MS %x LS %x res %s \n",inf
->dwFileVersionMS
, inf
->dwFileVersionLS
, debugstr_w(buff
));
492 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
494 if(VerQueryValueW(pBuf
, wTranslation
, (LPVOID
*)&lplangcode
, &infolen
))
496 /* FIXME find language from current locale / if not available,
498 * for now default to first available language
500 lang
= lplangcode
->lang
;
501 code
= lplangcode
->code
;
504 sprintfW(buff
, wFileDescriptionFormat
, lang
, code
);
505 SH_FileVersionQuerySetText(hwndDlg
, 14003, pBuf
, buff
, &str
);
507 sprintfW(buff
, wLegalCopyrightFormat
, lang
, code
);
508 SH_FileVersionQuerySetText(hwndDlg
, 14005, pBuf
, buff
, &str
);
510 /* listbox properties */
511 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wCompanyName
, &str
, lang
, code
);
512 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wFileVersion
, &str
, lang
, code
);
513 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wInternalName
, &str
, lang
, code
);
515 /* FIXME insert language identifier */
517 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wOriginalFilename
, &str
, lang
, code
);
518 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wProductName
, &str
, lang
, code
);
519 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wProductVersion
, &str
, lang
, code
);
520 SetWindowLong(hwndDlg
, DWL_USER
, (LONG
)pBuf
);
522 /* select first item */
523 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
524 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
525 str
= (WCHAR
*)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
526 hDlgCtrl
= GetDlgItem(hwndDlg
, 14010);
527 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)str
);
531 /*************************************************************************
533 * SH_FileVersionDlgProc
535 * wnd proc of 'Version' property sheet page
539 SH_FileVersionDlgProc(
546 LPPROPSHEETPAGE ppsp
;
552 ppsp
= (LPPROPSHEETPAGE
)lParam
;
556 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n",hwndDlg
, lParam
, ppsp
->lParam
);
558 lpstr
= (WCHAR
*)ppsp
->lParam
;
563 return SH_FileVersionInitialize(hwndDlg
, lpstr
);
567 if(LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_DBLCLK
)
573 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
574 lresult
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
575 if(lresult
== LB_ERR
)
579 str
= (WCHAR
*)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)lresult
, (LPARAM
)NULL
);
585 hDlgCtrl
= GetDlgItem(hwndDlg
, 14010);
586 TRACE("hDlgCtrl %x string %s \n",hDlgCtrl
, debugstr_w(str
));
587 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)str
);
593 buf
= (LPVOID
)GetWindowLong(hwndDlg
, DWL_USER
);
594 HeapFree(GetProcessHeap(), 0, buf
);
603 /*************************************************************************
605 * SH_FileGeneralDlgProc
607 * wnd proc of 'General' property sheet page
613 SH_FileGeneralDlgProc(
620 LPPROPSHEETPAGEW ppsp
;
625 ppsp
= (LPPROPSHEETPAGEW
)lParam
;
628 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n",hwndDlg
, lParam
, ppsp
->lParam
);
630 lpstr
= (WCHAR
*)ppsp
->lParam
;
634 ERR("no filename\n");
637 /* set general text properties filename filelocation and icon */
638 SH_FileGeneralSetText(hwndDlg
, lpstr
);
639 /* enumerate file extension from registry and application which opens it*/
640 SH_FileGeneralSetFileType(hwndDlg
, strrchrW(lpstr
, '.'));
641 /* set file time create/modfied/accessed */
642 SH_FileGeneralSetFileSizeTime(hwndDlg
, lpstr
, NULL
);
651 /*************************************************************************
653 * SH_ShowPropertiesDialog
655 * called from ShellExecuteExW32
657 * lpf contains (quoted) path of folder/file
659 * TODO: provide button change application type if file has registered type
660 * make filename field editable and apply changes to filename on close
664 SH_ShowPropertiesDialog(PCWSTR lpf
)
666 PROPSHEETHEADERW pinfo
;
667 HPROPSHEETPAGE hppages
[MAX_PROPERTY_SHEET_PAGE
];
668 HPROPSHEETPAGE hpage
;
669 WCHAR wFileName
[MAX_PATH
];
674 TRACE("SH_ShowPropertiesDialog entered filename %s\n", debugstr_w(lpf
));
682 memset(hppages
, 0x0, sizeof(HPROPSHEETPAGE
) * MAX_PROPERTY_SHEET_PAGE
);
685 /* remove quotes from lpf */
686 LPCWSTR src
= lpf
+ 1;
687 LPWSTR dst
= wFileName
;
689 while(*src
&& *src
!='"')
696 strcpyW(wFileName
, lpf
);
699 if (PathIsDirectoryW(wFileName
) || strlenW(wFileName
) == 3)
701 FIXME("directory / drive resources are missing\n");
704 hpage
= SH_CreatePropertySheetPage("SHELL_FILE_GENERAL_DLG", SH_FileGeneralDlgProc
, (LPARAM
)wFileName
, NULL
);
709 hppages
[num_pages
] = hpage
;
711 if ( GetFileVersionInfoSizeW(lpf
, &dwHandle
) )
713 if ( (hpage
= SH_CreatePropertySheetPage("SHELL_FILE_VERSION_DLG",SH_FileVersionDlgProc
, (LPARAM
)wFileName
, NULL
))!= NULL
)
715 hppages
[num_pages
] = hpage
;
720 pFileName
= wcsrchr(wFileName
, '\\');
722 pFileName
= wFileName
;
727 memset(&pinfo
, 0x0, sizeof(PROPSHEETHEADERW
));
728 pinfo
.dwSize
= sizeof(PROPSHEETHEADERW
);
729 pinfo
.dwFlags
= PSH_NOCONTEXTHELP
| PSH_PROPTITLE
;
730 pinfo
.nPages
= num_pages
;
731 pinfo
.u3
.phpage
= hppages
;
732 pinfo
.pszCaption
= pFileName
;
733 return (PropertySheetW(&pinfo
) != -1);