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 lvalue
= lname
= MAX_PATH
;
152 result
= RegEnumValueW(hKey
,0, name
, &lname
, NULL
, NULL
, (LPBYTE
)value
, &lvalue
);
156 /* file extension type */
157 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)value
);
160 /*************************************************************************
162 * SHFileGeneralGetFileTimeString [Internal]
164 * formats a given LPFILETIME struct into readable user format
168 SHFileGeneralGetFileTimeString(LPFILETIME lpFileTime
, WCHAR
* lpResult
)
173 static const WCHAR wFormat
[] = {'%','0','2','d','/','%','0','2','d','/','%','0','4','d',' ',' ','%','0','2','d',':','%','0','2','u',0};
175 if (lpFileTime
== NULL
|| lpResult
== NULL
)
178 if (!FileTimeToLocalFileTime(lpFileTime
, &ft
))
181 FileTimeToSystemTime(&ft
, &dt
);
185 sprintfW (lpResult
, wFormat
, dt
.wDay
, dt
.wMonth
, wYear
, dt
.wHour
, dt
.wMinute
);
187 TRACE("result %s\n",debugstr_w(lpResult
));
191 /*************************************************************************
193 * SH_FileGeneralSetText [Internal]
195 * sets file path string and filename string
200 SH_FileGeneralSetText(HWND hwndDlg
, WCHAR
* lpstr
)
205 WCHAR buff
[MAX_PATH
];
211 lpdir
= strrchrW(lpstr
, '\\'); /* find the last occurence of '\\' */
213 plength
= strlenW(lpstr
);
214 flength
= strlenW(lpdir
);
218 /* location text field */
219 strncpyW(buff
, lpstr
, plength
- flength
);
220 buff
[plength
- flength
] = UNICODE_NULL
;
221 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
222 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
227 /* text filename field */
228 strncpyW(buff
, &lpdir
[1], flength
);
229 hDlgCtrl
= GetDlgItem(hwndDlg
, 14001);
230 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
236 /*************************************************************************
238 * SH_FileGeneralSetFileSizeTime [Internal]
240 * retrieves file information from file and sets in dialog
245 SH_FileGeneralSetFileSizeTime(HWND hwndDlg
, WCHAR
* lpfilename
, PULARGE_INTEGER lpfilesize
)
249 FILETIME create_time
;
250 FILETIME accessed_time
;
252 WCHAR resultstr
[MAX_PATH
];
254 LARGE_INTEGER file_size
;
256 if (lpfilename
== NULL
)
259 hFile
= CreateFileW(lpfilename
,
261 FILE_SHARE_READ
,NULL
,
263 FILE_ATTRIBUTE_NORMAL
,
266 if (hFile
== INVALID_HANDLE_VALUE
)
268 WARN("failed to open file %s\n", debugstr_w(lpfilename
));
272 result
= GetFileTime(hFile
, &create_time
, &accessed_time
, &write_time
);
276 WARN("GetFileTime failed\n");
279 if (SHFileGeneralGetFileTimeString(&create_time
,resultstr
))
281 hDlgCtrl
= GetDlgItem(hwndDlg
, 14015);
282 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
285 if (SHFileGeneralGetFileTimeString(&accessed_time
, resultstr
))
287 hDlgCtrl
= GetDlgItem(hwndDlg
, 14017);
288 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
291 if (SHFileGeneralGetFileTimeString(&write_time
, resultstr
))
293 hDlgCtrl
= GetDlgItem(hwndDlg
, 14019);
294 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
297 if (!GetFileSizeEx(hFile
, &file_size
))
299 WARN("GetFileSize failed\n");
304 if (!StrFormatByteSizeW(file_size
.QuadPart
, resultstr
, sizeof(resultstr
) / sizeof(WCHAR
)))
306 hDlgCtrl
= GetDlgItem(hwndDlg
, 14011);
307 TRACE("result size %u resultstr %s\n", file_size
.QuadPart
, debugstr_w(resultstr
));
308 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)resultstr
);
311 lpfilesize
->QuadPart
= (ULONGLONG
)file_size
.QuadPart
;
316 /*************************************************************************
318 * SH_SetFileVersionText [Internal]
324 SH_FileVersionQuerySetText(HWND hwndDlg
, DWORD dlgId
, LPVOID pInfo
, WCHAR
* text
, WCHAR
** resptr
)
329 if(hwndDlg
== NULL
|| resptr
== NULL
|| text
== NULL
)
332 if(VerQueryValueW(pInfo
, text
, (LPVOID
*)resptr
, &reslen
))
334 /* file description property */
335 hDlgCtrl
= GetDlgItem(hwndDlg
, dlgId
);
336 TRACE("%s :: %s\n",debugstr_w(text
), debugstr_w(*resptr
));
337 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)*resptr
);
343 /*************************************************************************
345 * SH_FileVersionQuerySetListText [Internal]
347 * retrieves a version string and adds it to listbox
353 SH_FileVersionQuerySetListText(HWND hwndDlg
, LPVOID pInfo
, const WCHAR
* text
, WCHAR
**resptr
, WORD lang
, WORD code
)
358 static const WCHAR wFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n',
359 'f','o','\\','%','0','4','x','%','0','4','x','\\','%','s',0 };
362 TRACE("text %s, resptr %p hwndDlg %p\n",debugstr_w(text
), resptr
, hwndDlg
);
364 if(hwndDlg
== NULL
|| resptr
== NULL
|| text
== NULL
)
367 sprintfW(buff
, wFormat
, lang
, code
, text
);
368 if(VerQueryValueW(pInfo
, buff
, (LPVOID
*)resptr
, &reslen
))
370 /* listbox name property */
371 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
372 TRACE("%s :: %s\n",debugstr_w(text
), debugstr_w(*resptr
));
373 index
= SendMessageW(hDlgCtrl
, LB_ADDSTRING
, (WPARAM
)-1, (LPARAM
)text
);
374 SendMessageW(hDlgCtrl
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)(WCHAR
*)*resptr
);
380 /*************************************************************************
382 * SH_FileVersionInitialize [Internal]
384 * sets all file version properties in dialog
387 SH_FileVersionInitialize(HWND hwndDlg
, WCHAR
* lpfilename
)
398 LPLANGANDCODEPAGE lplangcode
;
400 static const WCHAR wVersionFormat
[] = { '%','d','.','%','d','.','%','d','.','%','d',0 };
401 static const WCHAR wFileDescriptionFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
402 '\\','%','0','4','x','%','0','4','x','\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
403 static const WCHAR wLegalCopyrightFormat
[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
404 '\\','%','0','4','x','%','0','4','x','\\','L','e','g','a','l','C','o','p','y','r','i','g','h','t',0 };
405 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 };
406 static const WCHAR wCompanyName
[] = { 'C','o','m','p','a','n','y','N','a','m','e',0 };
407 static const WCHAR wFileVersion
[] = { 'F','i','l','e','V','e','r','s','i','o','n',0 };
408 static const WCHAR wInternalName
[] = { 'I','n','t','e','r','n','a','l','N','a','m','e',0 };
409 static const WCHAR wOriginalFilename
[] = { 'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0 };
410 static const WCHAR wProductName
[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
411 static const WCHAR wProductVersion
[] = { 'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0 };
412 static const WCHAR wSlash
[] = { '\\',0 };
418 if(!(versize
= GetFileVersionInfoSizeW(lpfilename
, &handle
)))
420 WARN("GetFileVersionInfoSize failed\n");
424 if(!(pBuf
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, versize
)))
426 WARN("HeapAlloc failed bytes %x\n",versize
);
430 if(!GetFileVersionInfoW(lpfilename
, handle
, versize
, pBuf
))
432 HeapFree(GetProcessHeap(), 0, pBuf
);
435 if(VerQueryValueW(pBuf
, wSlash
, &info
, &infolen
))
437 VS_FIXEDFILEINFO
* inf
= (VS_FIXEDFILEINFO
*)info
;
438 sprintfW(buff
, wVersionFormat
, HIWORD(inf
->dwFileVersionMS
),
439 LOWORD(inf
->dwFileVersionMS
),
440 HIWORD(inf
->dwFileVersionLS
),
441 LOWORD(inf
->dwFileVersionLS
));
443 hDlgCtrl
= GetDlgItem(hwndDlg
, 14001);
444 TRACE("MS %x LS %x res %s \n",inf
->dwFileVersionMS
, inf
->dwFileVersionLS
, debugstr_w(buff
));
445 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)buff
);
447 if(VerQueryValueW(pBuf
, wTranslation
, (LPVOID
*)&lplangcode
, &infolen
))
449 /* FIXME find language from current locale / if not available,
451 * for now default to first available language
453 lang
= lplangcode
->lang
;
454 code
= lplangcode
->code
;
457 sprintfW(buff
, wFileDescriptionFormat
, lang
, code
);
458 SH_FileVersionQuerySetText(hwndDlg
, 14003, pBuf
, buff
, &str
);
460 sprintfW(buff
, wLegalCopyrightFormat
, lang
, code
);
461 SH_FileVersionQuerySetText(hwndDlg
, 14005, pBuf
, buff
, &str
);
463 /* listbox properties */
464 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wCompanyName
, &str
, lang
, code
);
465 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wFileVersion
, &str
, lang
, code
);
466 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wInternalName
, &str
, lang
, code
);
468 /* FIXME insert language identifier */
470 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wOriginalFilename
, &str
, lang
, code
);
471 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wProductName
, &str
, lang
, code
);
472 SH_FileVersionQuerySetListText(hwndDlg
, pBuf
, wProductVersion
, &str
, lang
, code
);
473 SetWindowLong(hwndDlg
, DWL_USER
, (LONG
)pBuf
);
475 /* select first item */
476 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
477 SendMessageW(hDlgCtrl
, LB_SETCURSEL
, 0, 0);
478 str
= (WCHAR
*)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)0, (LPARAM
)NULL
);
479 hDlgCtrl
= GetDlgItem(hwndDlg
, 14010);
480 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)str
);
484 /*************************************************************************
486 * SH_FileVersionDlgProc
488 * wnd proc of 'Version' property sheet page
492 SH_FileVersionDlgProc(
499 LPPROPSHEETPAGE ppsp
;
505 ppsp
= (LPPROPSHEETPAGE
)lParam
;
509 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n",hwndDlg
, lParam
, ppsp
->lParam
);
511 lpstr
= (WCHAR
*)ppsp
->lParam
;
516 return SH_FileVersionInitialize(hwndDlg
, lpstr
);
520 if(LOWORD(wParam
) == 14009 && HIWORD(wParam
) == LBN_DBLCLK
)
526 hDlgCtrl
= GetDlgItem(hwndDlg
, 14009);
527 lresult
= SendMessageW(hDlgCtrl
, LB_GETCURSEL
, (WPARAM
)NULL
, (LPARAM
)NULL
);
528 if(lresult
== LB_ERR
)
532 str
= (WCHAR
*)SendMessageW(hDlgCtrl
, LB_GETITEMDATA
, (WPARAM
)lresult
, (LPARAM
)NULL
);
538 hDlgCtrl
= GetDlgItem(hwndDlg
, 14010);
539 TRACE("hDlgCtrl %x string %s \n",hDlgCtrl
, debugstr_w(str
));
540 SendMessageW(hDlgCtrl
, WM_SETTEXT
, (WPARAM
)NULL
, (LPARAM
)str
);
546 buf
= (LPVOID
)GetWindowLong(hwndDlg
, DWL_USER
);
547 HeapFree(GetProcessHeap(), 0, buf
);
556 /*************************************************************************
558 * SH_FileGeneralDlgProc
560 * wnd proc of 'General' property sheet page
566 SH_FileGeneralDlgProc(
573 LPPROPSHEETPAGEW ppsp
;
578 ppsp
= (LPPROPSHEETPAGEW
)lParam
;
581 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n",hwndDlg
, lParam
, ppsp
->lParam
);
583 lpstr
= (WCHAR
*)ppsp
->lParam
;
587 ERR("no filename\n");
590 /* set general text properties filename filelocation and icon */
591 SH_FileGeneralSetText(hwndDlg
, lpstr
);
592 /* enumerate file extension from registry and application which opens it*/
593 SH_FileGeneralSetFileType(hwndDlg
, strrchrW(lpstr
, '.'));
594 /* set file time create/modfied/accessed */
595 SH_FileGeneralSetFileSizeTime(hwndDlg
, lpstr
, NULL
);
604 /*************************************************************************
606 * SH_ShowPropertiesDialog
608 * called from ShellExecuteExW32
610 * lpf contains (quoted) path of folder/file
612 * TODO: provide button change application type if file has registered type
613 * make filename field editable and apply changes to filename on close
617 SH_ShowPropertiesDialog(WCHAR
* lpf
)
619 PROPSHEETHEADERW pinfo
;
620 HPROPSHEETPAGE hppages
[MAX_PROPERTY_SHEET_PAGE
];
621 HPROPSHEETPAGE hpage
;
622 WCHAR wFileName
[MAX_PATH
];
626 TRACE("SH_ShowPropertiesDialog entered filename %s\n", debugstr_w(lpf
));
634 memset(hppages
, 0x0, sizeof(HPROPSHEETPAGE
) * MAX_PROPERTY_SHEET_PAGE
);
637 /* remove quotes from lpf */
638 LPWSTR src
= lpf
+ 1;
639 LPWSTR dst
= wFileName
;
641 while(*src
&& *src
!='"')
648 strcpyW(wFileName
, lpf
);
651 if (PathIsDirectoryW(wFileName
) || strlenW(wFileName
) == 3)
653 FIXME("directory / drive resources are missing\n");
656 hpage
= SH_CreatePropertySheetPage("SHELL_FILE_GENERAL_DLG", SH_FileGeneralDlgProc
, (LPARAM
)wFileName
, NULL
);
661 hppages
[num_pages
] = hpage
;
663 if ( GetFileVersionInfoSizeW(lpf
, &dwHandle
) )
665 if ( (hpage
= SH_CreatePropertySheetPage("SHELL_FILE_VERSION_DLG",SH_FileVersionDlgProc
, (LPARAM
)lpf
, NULL
))!= NULL
)
667 hppages
[num_pages
] = hpage
;
671 memset(&pinfo
, 0x0, sizeof(PROPSHEETHEADERW
));
672 pinfo
.dwSize
= sizeof(PROPSHEETHEADERW
);
673 pinfo
.dwFlags
= PSH_NOCONTEXTHELP
| PSH_PROPTITLE
;
674 pinfo
.nPages
= num_pages
;
675 pinfo
.u3
.phpage
= hppages
;
676 pinfo
.pszCaption
= wFileName
;
677 return (PropertySheetW(&pinfo
) != -1);