2 * Open With Context Menu extension
4 * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
5 * Copyright 2009 Andrew Hill
6 * Copyright 2012 Rafal Harabien
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
28 // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system]
29 // "NoInternetOpenWith"=dword:00000001
32 EXTERN_C BOOL
PathIsExeW(LPCWSTR lpszPath
);
39 WCHAR wszFilename
[MAX_PATH
];
40 WCHAR wszCmd
[MAX_PATH
];
41 //WCHAR wszManufacturer[256];
53 SApp
*Add(LPCWSTR pwszPath
);
54 static BOOL
SaveApp(SApp
*pApp
);
55 SApp
*Find(LPCWSTR pwszFilename
);
56 static LPCWSTR
GetName(SApp
*pApp
);
57 static HICON
GetIcon(SApp
*pApp
);
58 static BOOL
Execute(SApp
*pApp
, LPCWSTR pwszFilePath
);
59 static BOOL
IsHidden(SApp
*pApp
);
60 inline BOOL
IsNoOpen(VOID
) { return m_bNoOpen
; }
61 BOOL
LoadRecommended(LPCWSTR pwszFilePath
);
62 BOOL
SetDefaultHandler(SApp
*pApp
, LPCWSTR pwszFilename
);
64 inline SApp
*GetList() { return m_pApp
; }
65 inline UINT
GetCount() { return m_cApp
; }
66 inline UINT
GetRecommendedCount() { return m_cRecommended
; }
69 typedef struct _LANGANDCODEPAGE
73 } LANGANDCODEPAGE
, *LPLANGANDCODEPAGE
;
76 UINT m_cApp
, m_cRecommended
;
79 SApp
*AddInternal(LPCWSTR pwszFilename
);
80 static BOOL
LoadInfo(SApp
*pApp
);
81 static VOID
GetPathFromCmd(LPWSTR pwszAppPath
, LPCWSTR pwszCmd
);
82 BOOL
LoadProgIdList(HKEY hKey
, LPCWSTR pwszExt
);
83 static HANDLE
OpenMRUList(HKEY hKey
);
84 BOOL
LoadMRUList(HKEY hKey
);
85 BOOL
LoadAppList(HKEY hKey
);
86 VOID
LoadFromProgIdKey(HKEY hKey
, LPCWSTR pwszExt
);
87 VOID
LoadRecommendedFromHKCR(LPCWSTR pwszExt
);
88 VOID
LoadRecommendedFromHKCU(LPCWSTR pwszExt
);
89 static BOOL
AddAppToMRUList(SApp
*pApp
, LPCWSTR pwszFilename
);
91 inline VOID
SetRecommended(SApp
*pApp
)
93 if (!pApp
->bRecommended
)
95 pApp
->bRecommended
= TRUE
;
99 COpenWithList::COpenWithList():
100 m_pApp(NULL
), m_cApp(0), m_cRecommended(0), m_bNoOpen(FALSE
) {}
102 COpenWithList::~COpenWithList()
104 for (UINT i
= 0; i
< m_cApp
; ++i
)
106 DestroyIcon(m_pApp
[i
].hIcon
);
108 HeapFree(GetProcessHeap(), 0, m_pApp
);
111 BOOL
COpenWithList::Load()
114 WCHAR wszName
[256], wszBuf
[100];;
115 DWORD i
= 0, cchName
, dwSize
;
118 if (RegOpenKeyEx(HKEY_CLASSES_ROOT
, L
"Applications", 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
120 ERR("RegOpenKeyEx HKCR\\Applications failed!\n");
126 cchName
= _countof(wszName
);
127 if (RegEnumKeyEx(hKey
, i
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
130 pApp
= AddInternal(wszName
);
134 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s\\shell\\open\\command", wszName
);
135 dwSize
= sizeof(pApp
->wszCmd
);
136 if (RegGetValueW(hKey
, wszBuf
, L
"", RRF_RT_REG_SZ
, NULL
, pApp
->wszCmd
, &dwSize
) != ERROR_SUCCESS
)
138 ERR("Failed to add app %ls\n", wszName
);
139 pApp
->bHidden
= TRUE
;
142 TRACE("App added %ls\n", pApp
->wszCmd
);
145 ERR("AddInternal failed\n");
152 COpenWithList::SApp
*COpenWithList::Add(LPCWSTR pwszPath
)
154 SApp
*pApp
= AddInternal(PathFindFileNameW(pwszPath
));
158 StringCbPrintfW(pApp
->wszCmd
, sizeof(pApp
->wszCmd
), L
"\"%s\" %%1", pwszPath
);
165 BOOL
COpenWithList::SaveApp(SApp
*pApp
)
170 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s\\shell\\open\\command", pApp
->wszFilename
);
171 if (RegCreateKeyEx(HKEY_CLASSES_ROOT
, wszBuf
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
173 ERR("RegOpenKeyEx failed\n");
177 if (RegSetValueEx(hKey
, L
"", 0, REG_SZ
, (PBYTE
)pApp
->wszCmd
, (wcslen(pApp
->wszCmd
)+1)*sizeof(WCHAR
)) != ERROR_SUCCESS
)
178 ERR("Cannot add app to registry\n");
184 COpenWithList::SApp
*COpenWithList::Find(LPCWSTR pwszFilename
)
186 for (UINT i
= 0; i
< m_cApp
; ++i
)
187 if (wcsicmp(m_pApp
[i
].wszFilename
, pwszFilename
) == 0)
192 LPCWSTR
COpenWithList::GetName(SApp
*pApp
)
194 if (!pApp
->wszName
[0])
198 WARN("Failed to load %ls info\n", pApp
->wszFilename
);
199 StringCbCopyW(pApp
->wszName
, sizeof(pApp
->wszName
), pApp
->wszFilename
);
203 TRACE("%ls name: %ls\n", pApp
->wszFilename
, pApp
->wszName
);
204 return pApp
->wszName
;
207 HICON
COpenWithList::GetIcon(SApp
*pApp
)
211 WCHAR wszPath
[MAX_PATH
];
213 GetPathFromCmd(wszPath
, pApp
->wszCmd
);
214 ExtractIconExW(wszPath
, 0, NULL
, &pApp
->hIcon
, 1);
217 TRACE("%ls icon: %p\n", pApp
->wszFilename
, pApp
->hIcon
);
222 BOOL
COpenWithList::Execute(COpenWithList::SApp
*pApp
, LPCWSTR pwszFilePath
)
227 /* Add app to registry if it wasnt there before */
230 AddAppToMRUList(pApp
, pwszFilePath
);
232 /* Get a handle to the reg key */
233 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s", pApp
->wszFilename
);
234 if (RegCreateKeyEx(HKEY_CLASSES_ROOT
, wszBuf
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
236 ERR("RegOpenKeyEx failed\n");
240 /* Let ShellExecuteExW do the work */
241 SHELLEXECUTEINFOW sei
= {sizeof(SHELLEXECUTEINFOW
), SEE_MASK_CLASSKEY
};
242 sei
.nShow
= SW_SHOWNORMAL
;
243 sei
.hkeyClass
= hKey
;
244 sei
.lpFile
= pwszFilePath
;
246 ShellExecuteExW(&sei
);
251 BOOL
COpenWithList::IsHidden(SApp
*pApp
)
257 return pApp
->bHidden
;
259 if (FAILED(StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s", pApp
->wszFilename
)))
261 ERR("insufficient buffer\n");
265 if (RegGetValueW(HKEY_CLASSES_ROOT
, wszBuf
, L
"NoOpenWith", RRF_RT_REG_SZ
, NULL
, NULL
, &dwSize
) != ERROR_SUCCESS
)
268 pApp
->bHidden
= TRUE
;
272 COpenWithList::SApp
*COpenWithList::AddInternal(LPCWSTR pwszFilename
)
274 /* Check for duplicate */
275 SApp
*pApp
= Find(pwszFilename
);
279 /* Create new item */
281 m_pApp
= static_cast<SApp
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(m_pApp
[0])));
283 m_pApp
= static_cast<SApp
*>(HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, m_pApp
, (m_cApp
+ 1)*sizeof(m_pApp
[0])));
286 ERR("Allocation failed\n");
290 pApp
= &m_pApp
[m_cApp
++];
291 wcscpy(pApp
->wszFilename
, pwszFilename
);
295 BOOL
COpenWithList::LoadInfo(COpenWithList::SApp
*pApp
)
299 WORD wLang
= 0, wCode
= 0;
300 LPLANGANDCODEPAGE lpLangCode
;
303 WCHAR wszPath
[MAX_PATH
];
305 GetPathFromCmd(wszPath
, pApp
->wszCmd
);
306 TRACE("LoadInfo %ls\n", wszPath
);
308 /* query version info size */
309 cbSize
= GetFileVersionInfoSizeW(wszPath
, NULL
);
312 ERR("GetFileVersionInfoSizeW %ls failed: %lu\n", wszPath
, GetLastError());
316 /* allocate buffer */
317 pBuf
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbSize
+ 200);
320 ERR("HeapAlloc failed\n");
324 /* query version info */
325 if (!GetFileVersionInfoW(wszPath
, 0, cbSize
, pBuf
))
327 ERR("GetFileVersionInfoW %ls failed: %lu\n", wszPath
, GetLastError());
328 HeapFree(GetProcessHeap(), 0, pBuf
);
332 /* query lang code */
333 if (VerQueryValueW(pBuf
, L
"VarFileInfo\\Translation", (LPVOID
*)&lpLangCode
, &cbSize
))
335 /* FIXME: find language from current locale / if not available,
337 * for now default to first available language
339 wLang
= lpLangCode
->lang
;
340 wCode
= lpLangCode
->code
;
344 swprintf(wszBuf
, L
"\\StringFileInfo\\%04x%04x\\FileDescription", wLang
, wCode
);
345 if (VerQueryValueW(pBuf
, wszBuf
, (LPVOID
*)&pResult
, &cchLen
))
346 StringCchCopyNW(pApp
->wszName
, _countof(pApp
->wszName
), pResult
, cchLen
);
348 ERR("Cannot get app name\n");
350 /* Query manufacturer */
351 /*swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\CompanyName", wLang, wCode);
353 if (VerQueryValueW(pBuf, wszBuf, (LPVOID *)&pResult, &cchLen))
354 StringCchCopyNW(pApp->wszManufacturer, _countof(pApp->wszManufacturer), pResult, cchLen);*/
355 HeapFree(GetProcessHeap(), 0, pBuf
);
359 VOID
COpenWithList::GetPathFromCmd(LPWSTR pwszAppPath
, LPCWSTR pwszCmd
)
361 WCHAR wszBuf
[MAX_PATH
], *pwszDest
= wszBuf
;
363 /* Remove arguments */
364 if (pwszCmd
[0] == '"')
366 for(LPCWSTR pwszSrc
= pwszCmd
+ 1; *pwszSrc
&& *pwszSrc
!= '"'; ++pwszSrc
)
367 *(pwszDest
++) = *pwszSrc
;
371 for(LPCWSTR pwszSrc
= pwszCmd
; *pwszSrc
&& *pwszSrc
!= ' '; ++pwszSrc
)
372 *(pwszDest
++) = *pwszSrc
;
377 /* Expand evn vers and optionally search for path */
378 ExpandEnvironmentStrings(wszBuf
, pwszAppPath
, MAX_PATH
);
379 if (!PathFileExists(pwszAppPath
))
380 SearchPath(NULL
, pwszAppPath
, NULL
, MAX_PATH
, pwszAppPath
, NULL
);
383 BOOL
COpenWithList::LoadRecommended(LPCWSTR pwszFilePath
)
387 pwszExt
= PathFindExtensionW(pwszFilePath
);
391 /* load programs directly associated from HKCU */
392 LoadRecommendedFromHKCU(pwszExt
);
394 /* load programs associated from HKCR\Extension */
395 LoadRecommendedFromHKCR(pwszExt
);
400 BOOL
COpenWithList::LoadProgIdList(HKEY hKey
, LPCWSTR pwszExt
)
402 HKEY hSubkey
, hSubkey2
;
403 WCHAR wszProgId
[256];
404 DWORD i
= 0, cchProgId
;
406 if (RegOpenKeyExW(hKey
, L
"OpenWithProgIDs", 0, KEY_READ
, &hSubkey
) != ERROR_SUCCESS
)
411 /* Enumerate values - value name is ProgId */
412 cchProgId
= _countof(wszProgId
);
413 if (RegEnumValue(hSubkey
, i
++, wszProgId
, &cchProgId
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
416 /* If ProgId exists load it */
417 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszProgId
, 0, KEY_READ
, &hSubkey2
) == ERROR_SUCCESS
)
419 LoadFromProgIdKey(hSubkey2
, pwszExt
);
420 RegCloseKey(hSubkey2
);
424 RegCloseKey(hSubkey
);
428 HANDLE
COpenWithList::OpenMRUList(HKEY hKey
)
432 /* Initialize mru list info */
433 Info
.cbSize
= sizeof(Info
);
435 Info
.dwFlags
= MRU_STRING
;
437 Info
.lpszSubKey
= L
"OpenWithList";
438 Info
.lpfnCompare
= NULL
;
440 return CreateMRUListW(&Info
);
443 BOOL
COpenWithList::LoadMRUList(HKEY hKey
)
446 int nItem
, nCount
, nResult
;
447 WCHAR wszAppFilename
[MAX_PATH
];
450 hList
= OpenMRUList(hKey
);
453 TRACE("OpenMRUList failed\n");
458 nCount
= EnumMRUListW(hList
, -1, NULL
, 0);
460 for(nItem
= 0; nItem
< nCount
; nItem
++)
462 nResult
= EnumMRUListW(hList
, nItem
, wszAppFilename
, _countof(wszAppFilename
));
467 SApp
*pApp
= Find(wszAppFilename
);
469 TRACE("Recommended app %ls: %p\n", wszAppFilename
, pApp
);
472 pApp
->bMRUList
= TRUE
;
473 SetRecommended(pApp
);
477 /* Free the MRU list */
482 BOOL
COpenWithList::LoadAppList(HKEY hKey
)
484 WCHAR wszAppFilename
[MAX_PATH
];
486 DWORD i
= 0, cchAppFilename
;
488 if (RegOpenKeyExW(hKey
, L
"OpenWithList", 0, KEY_READ
, &hSubkey
) != ERROR_SUCCESS
)
493 /* Enum registry keys - each of them is app name */
494 cchAppFilename
= _countof(wszAppFilename
);
495 if (RegEnumKeyExW(hSubkey
, i
++, wszAppFilename
, &cchAppFilename
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
498 /* Set application as recommended */
499 SApp
*pApp
= Find(wszAppFilename
);
501 TRACE("Recommended app %ls: %p\n", wszAppFilename
, pApp
);
503 SetRecommended(pApp
);
506 RegCloseKey(hSubkey
);
510 VOID
COpenWithList::LoadFromProgIdKey(HKEY hKey
, LPCWSTR pwszExt
)
512 WCHAR wszCmd
[MAX_PATH
], wszPath
[MAX_PATH
];
515 /* Check if NoOpen value exists */
516 if (RegGetValueW(hKey
, NULL
, L
"NoOpen", RRF_RT_REG_SZ
, NULL
, NULL
, &dwSize
) == ERROR_SUCCESS
)
518 /* Display warning dialog */
522 /* Check if there is a directly available execute key */
523 dwSize
= sizeof(wszCmd
);
524 if (RegGetValueW(hKey
, L
"shell\\open\\command", NULL
, RRF_RT_REG_SZ
, NULL
, (PVOID
)wszCmd
, &dwSize
) == ERROR_SUCCESS
)
526 /* Erase extra arguments */
527 GetPathFromCmd(wszPath
, wszCmd
);
529 /* Add application */
530 SApp
*pApp
= AddInternal(PathFindFileNameW(wszPath
));
531 TRACE("Add app %ls: %p\n", wszPath
, pApp
);
535 StringCbCopyW(pApp
->wszCmd
, sizeof(pApp
->wszCmd
), wszCmd
);
536 SetRecommended(pApp
);
538 AddAppToMRUList(pApp
, pwszExt
);
543 VOID
COpenWithList::LoadRecommendedFromHKCR(LPCWSTR pwszExt
)
546 WCHAR wszBuf
[MAX_PATH
], wszBuf2
[MAX_PATH
];
549 /* Check if extension exists */
550 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
552 /* Load items from SystemFileAssociations\Ext key */
553 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"SystemFileAssociations\\%s", pwszExt
);
554 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
558 /* Load programs referenced from HKCR\ProgId */
559 dwSize
= sizeof(wszBuf
);
560 if (RegGetValueW(hKey
, NULL
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
&&
561 RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hSubkey
) == ERROR_SUCCESS
)
563 LoadFromProgIdKey(hSubkey
, pwszExt
);
564 RegCloseKey(hSubkey
);
567 LoadFromProgIdKey(hKey
, pwszExt
);
569 /* Load items from HKCR\Ext\OpenWithList */
572 /* Load items from HKCR\Ext\OpenWithProgIDs */
573 if (RegOpenKeyExW(hKey
, L
"OpenWithProgIDs", 0, KEY_READ
, &hSubkey
) == ERROR_SUCCESS
)
575 LoadProgIdList(hSubkey
, pwszExt
);
576 RegCloseKey(hSubkey
);
579 /* Load additional items from referenced PerceivedType */
580 dwSize
= sizeof(wszBuf
);
581 if (RegGetValueW(hKey
, NULL
, L
"PerceivedType", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
)
583 StringCbPrintfW(wszBuf2
, sizeof(wszBuf2
), L
"SystemFileAssociations\\%s", wszBuf
);
584 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf2
, 0, KEY_READ
| KEY_WRITE
, &hSubkey
) == ERROR_SUCCESS
)
586 /* Load from OpenWithList key */
587 LoadAppList(hSubkey
);
588 RegCloseKey(hSubkey
);
596 VOID
COpenWithList::LoadRecommendedFromHKCU(LPCWSTR pwszExt
)
598 WCHAR wszBuf
[MAX_PATH
];
601 StringCbPrintfW(wszBuf
, sizeof(wszBuf
),
602 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
604 if (RegOpenKeyExW(HKEY_CURRENT_USER
, wszBuf
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
606 /* Load MRU and ProgId lists */
608 LoadProgIdList(hKey
, pwszExt
);
610 /* Handle "Aplication" value */
611 DWORD cbBuf
= sizeof(wszBuf
);
612 if (RegGetValueW(hKey
, NULL
, L
"Application", RRF_RT_REG_SZ
, NULL
, wszBuf
, &cbBuf
) == ERROR_SUCCESS
)
614 SApp
*pApp
= Find(wszBuf
);
616 SetRecommended(pApp
);
624 BOOL
COpenWithList::AddAppToMRUList(SApp
*pApp
, LPCWSTR pwszFilename
)
631 /* Get file extension */
632 pwszExt
= PathFindExtensionW(pwszFilename
);
636 /* Build registry key */
637 if (FAILED(StringCbPrintfW(wszBuf
, sizeof(wszBuf
),
638 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
641 ERR("insufficient buffer\n");
645 /* Open base key for this file extension */
646 if (RegCreateKeyExW(HKEY_CURRENT_USER
, wszBuf
, 0, NULL
, 0, KEY_WRITE
| KEY_READ
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
650 hList
= OpenMRUList(hKey
);
653 /* Insert the entry */
654 AddMRUStringW(hList
, pApp
->wszFilename
);
664 BOOL
COpenWithList::SetDefaultHandler(SApp
*pApp
, LPCWSTR pwszFilename
)
666 HKEY hKey
, hSrcKey
, hDestKey
;
669 TRACE("SetDefaultHandler %ls %ls\n", pApp
->wszFilename
, pwszFilename
);
671 /* Extract file extension */
672 LPCWSTR pwszExt
= PathFindExtensionW(pwszFilename
);
673 if (!pwszExt
[0] || !pwszExt
[1])
676 /* Create file extension key */
677 if (RegCreateKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, NULL
, 0, KEY_READ
|KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
679 ERR("Cannot open ext key");
683 DWORD dwSize
= sizeof(wszBuf
);
684 LONG lResult
= RegGetValueW(hKey
, NULL
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
);
686 if (lResult
== ERROR_FILE_NOT_FOUND
)
688 /* A new entry was created or the default key is not set: set the prog key id */
689 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s_auto_file", pwszExt
+ 1);
690 if (RegSetValueExW(hKey
, L
"", 0, REG_SZ
, (const BYTE
*)wszBuf
, (wcslen(wszBuf
) + 1) * sizeof(WCHAR
)) != ERROR_SUCCESS
)
693 ERR("RegSetValueExW failed\n");
697 else if (lResult
!= ERROR_SUCCESS
)
700 ERR("RegGetValueExW failed: 0x%08x\n", lResult
);
704 /* Close file extension key */
707 /* Create prog id key */
708 if (RegCreateKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
710 ERR("RegCreateKeyExW failed\n");
714 /* Check if there already verbs existing for that app */
715 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s\\shell", pApp
->wszFilename
);
716 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hSrcKey
) != ERROR_SUCCESS
)
718 ERR("RegOpenKeyExW %ls failed\n", wszBuf
);
723 /* Open destination key */
724 if (RegCreateKeyExW(hKey
, L
"shell", 0, NULL
, 0, KEY_WRITE
, NULL
, &hDestKey
, NULL
) != ERROR_SUCCESS
)
726 ERR("RegCreateKeyExW failed\n");
727 RegCloseKey(hSrcKey
);
732 /* Copy static verbs from Classes\Applications key */
733 /* FIXME: SHCopyKey does not copy the security attributes of the keys */
734 LSTATUS Result
= SHCopyKeyW(hSrcKey
, NULL
, hDestKey
, 0);
735 RegCloseKey(hDestKey
);
736 RegCloseKey(hSrcKey
);
739 if (Result
!= ERROR_SUCCESS
)
741 ERR("SHCopyKeyW failed\n");
748 class COpenWithDialog
751 COpenWithDialog(const OPENASINFO
*pInfo
, COpenWithList
*pAppList
);
753 static INT_PTR CALLBACK
DialogProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
754 BOOL
IsNoOpen(HWND hwnd
);
757 VOID
Init(HWND hwnd
);
758 VOID
AddApp(COpenWithList::SApp
*pApp
, BOOL bSelected
);
761 static INT_PTR CALLBACK
NoOpenDlgProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
);
762 COpenWithList::SApp
*GetCurrentApp();
764 const OPENASINFO
*m_pInfo
;
765 COpenWithList
*m_pAppList
;
766 BOOL m_bListAllocated
;
767 HWND m_hDialog
, m_hTreeView
;
768 HTREEITEM m_hRecommend
;
770 HIMAGELIST m_hImgList
;
774 COpenWithDialog::COpenWithDialog(const OPENASINFO
*pInfo
, COpenWithList
*pAppList
= NULL
):
775 m_pInfo(pInfo
), m_pAppList(pAppList
), m_hImgList(NULL
), m_bNoOpen(FALSE
)
779 m_pAppList
= new COpenWithList
;
780 m_bListAllocated
= TRUE
;
783 m_bListAllocated
= FALSE
;
786 COpenWithDialog::~COpenWithDialog()
788 if (m_bListAllocated
&& m_pAppList
)
791 ImageList_Destroy(m_hImgList
);
794 INT_PTR CALLBACK
COpenWithDialog::NoOpenDlgProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
803 EndDialog(hwnd
, IDNO
);
806 switch(LOWORD(wParam
))
809 EndDialog(hwnd
, IDYES
);
812 EndDialog(hwnd
, IDNO
);
822 BOOL
COpenWithDialog::IsNoOpen(HWND hwnd
)
824 /* Only do the actual check if the file type has the 'NoOpen' flag. */
827 int dReturnValue
= DialogBox(shell32_hInstance
, MAKEINTRESOURCE(IDD_NOOPEN
), hwnd
, NoOpenDlgProc
);
829 if (dReturnValue
== IDNO
)
831 else if (dReturnValue
== -1)
833 ERR("IsNoOpen failed to load the dialog box.");
841 VOID
COpenWithDialog::AddApp(COpenWithList::SApp
*pApp
, BOOL bSelected
)
843 LPCWSTR pwszName
= m_pAppList
->GetName(pApp
);
844 HICON hIcon
= m_pAppList
->GetIcon(pApp
);
846 TRACE("AddApp Cmd %ls Name %ls\n", pApp
->wszCmd
, pwszName
);
848 /* Add item to the list */
849 TVINSERTSTRUCT tvins
;
851 if (pApp
->bRecommended
)
852 tvins
.hParent
= tvins
.hInsertAfter
= m_hRecommend
;
854 tvins
.hParent
= tvins
.hInsertAfter
= m_hOther
;
856 tvins
.item
.mask
= TVIF_TEXT
|TVIF_PARAM
;
857 tvins
.item
.pszText
= (LPWSTR
)pwszName
;
858 tvins
.item
.lParam
= (LPARAM
)pApp
;
862 tvins
.item
.mask
|= TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
863 tvins
.item
.iImage
= tvins
.item
.iSelectedImage
= ImageList_AddIcon(m_hImgList
, hIcon
);
866 HTREEITEM hItem
= TreeView_InsertItem(m_hTreeView
, &tvins
);
869 TreeView_SelectItem(m_hTreeView
, hItem
);
872 VOID
COpenWithDialog::Browse()
875 WCHAR wszFilter
[256];
876 WCHAR wszPath
[MAX_PATH
];
879 /* Initialize OPENFILENAMEW structure */
880 ZeroMemory(&ofn
, sizeof(OPENFILENAMEW
));
881 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
882 ofn
.hInstance
= shell32_hInstance
;
883 ofn
.hwndOwner
= m_hDialog
;
884 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
885 ofn
.nMaxFile
= (sizeof(wszPath
) / sizeof(WCHAR
));
886 ofn
.lpstrFile
= wszPath
;
887 ofn
.lpstrInitialDir
= L
"%programfiles%";
890 if (LoadStringW(shell32_hInstance
, IDS_OPEN_WITH
, wszTitle
, sizeof(wszTitle
) / sizeof(WCHAR
)))
892 ofn
.lpstrTitle
= wszTitle
;
893 ofn
.nMaxFileTitle
= wcslen(wszTitle
);
896 /* Init the filter string */
897 if (LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_FILTER
, wszFilter
, sizeof(wszFilter
) / sizeof(WCHAR
)))
898 ofn
.lpstrFilter
= wszFilter
;
899 ZeroMemory(wszPath
, sizeof(wszPath
));
901 /* Create OpenFile dialog */
902 if (!GetOpenFileNameW(&ofn
))
905 /* Setup context for insert proc */
906 COpenWithList::SApp
*pApp
= m_pAppList
->Add(wszPath
);
910 COpenWithList::SApp
*COpenWithDialog::GetCurrentApp()
913 tvi
.hItem
= TreeView_GetSelection(m_hTreeView
);
917 tvi
.mask
= TVIF_PARAM
;
918 if (!TreeView_GetItem(m_hTreeView
, &tvi
))
921 return (COpenWithList::SApp
*)tvi
.lParam
;
924 VOID
COpenWithDialog::Init(HWND hwnd
)
926 TRACE("COpenWithDialog::Init hwnd %p\n", hwnd
);
929 SetWindowLongPtr(hwnd
, DWLP_USER
, (LONG_PTR
)this);
931 /* Handle register checkbox */
932 HWND hRegisterCheckbox
= GetDlgItem(hwnd
, 14003);
933 if (!(m_pInfo
->oaifInFlags
& OAIF_ALLOW_REGISTRATION
))
934 EnableWindow(hRegisterCheckbox
, FALSE
);
935 if (m_pInfo
->oaifInFlags
& OAIF_FORCE_REGISTRATION
)
936 SendMessage(hRegisterCheckbox
, BM_SETCHECK
, BST_CHECKED
, 0);
937 if (m_pInfo
->oaifInFlags
& OAIF_HIDE_REGISTRATION
)
938 ShowWindow(hRegisterCheckbox
, SW_HIDE
);
940 if (m_pInfo
->pcszFile
)
942 WCHAR wszBuf
[MAX_PATH
];
945 /* Add filename to label */
946 cchBuf
= GetDlgItemTextW(hwnd
, 14001, wszBuf
, _countof(wszBuf
));
947 StringCchCopyW(wszBuf
+ cchBuf
, _countof(wszBuf
) - cchBuf
, PathFindFileNameW(m_pInfo
->pcszFile
));
948 SetDlgItemTextW(hwnd
, 14001, wszBuf
);
950 /* Load applications from registry */
952 m_pAppList
->LoadRecommended(m_pInfo
->pcszFile
);
954 /* Determine if the type of file can be opened directly from the shell */
955 if (m_pAppList
->IsNoOpen() != FALSE
)
959 m_hTreeView
= GetDlgItem(hwnd
, 14002);
960 m_hImgList
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, m_pAppList
->GetCount() + 1, m_pAppList
->GetCount() + 1);
961 (void)TreeView_SetImageList(m_hTreeView
, m_hImgList
, TVSIL_NORMAL
);
963 /* If there are some recommendations add parent nodes: Recommended and Others */
964 UINT cRecommended
= m_pAppList
->GetRecommendedCount();
965 if (cRecommended
> 0)
967 TVINSERTSTRUCT tvins
;
970 tvins
.hParent
= tvins
.hInsertAfter
= TVI_ROOT
;
971 tvins
.item
.mask
= TVIF_TEXT
|TVIF_STATE
|TVIF_IMAGE
|TVIF_SELECTEDIMAGE
;
972 tvins
.item
.pszText
= (LPWSTR
)wszBuf
;
973 tvins
.item
.state
= tvins
.item
.stateMask
= TVIS_EXPANDED
;
974 hFolderIcon
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_PROGRAMS_FOLDER
), IMAGE_ICON
, 0, 0, 0);
975 tvins
.item
.iImage
= tvins
.item
.iSelectedImage
= ImageList_AddIcon(m_hImgList
, hFolderIcon
);
977 LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_RECOMMENDED
, wszBuf
, _countof(wszBuf
));
978 m_hRecommend
= TreeView_InsertItem(m_hTreeView
, &tvins
);
980 LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_OTHER
, wszBuf
, _countof(wszBuf
));
981 m_hOther
= TreeView_InsertItem(m_hTreeView
, &tvins
);
984 m_hRecommend
= m_hOther
= TVI_ROOT
;
986 /* Add all applications */
987 BOOL bNoAppSelected
= TRUE
;
988 COpenWithList::SApp
*pAppList
= m_pAppList
->GetList();
989 for (UINT i
= 0; i
< m_pAppList
->GetCount(); ++i
)
991 if (!COpenWithList::IsHidden(&pAppList
[i
]))
993 if (bNoAppSelected
&& (pAppList
[i
].bRecommended
|| !cRecommended
))
995 AddApp(&pAppList
[i
], TRUE
);
996 bNoAppSelected
= FALSE
;
999 AddApp(&pAppList
[i
], FALSE
);
1005 VOID
COpenWithDialog::Accept()
1007 COpenWithList::SApp
*pApp
= GetCurrentApp();
1010 /* Set programm as default handler */
1011 if (SendDlgItemMessage(m_hDialog
, 14003, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
1012 m_pAppList
->SetDefaultHandler(pApp
, m_pInfo
->pcszFile
);
1014 /* Execute program */
1015 if (m_pInfo
->oaifInFlags
& OAIF_EXEC
)
1016 m_pAppList
->Execute(pApp
, m_pInfo
->pcszFile
);
1018 EndDialog(m_hDialog
, 1);
1022 INT_PTR CALLBACK
COpenWithDialog::DialogProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1024 COpenWithDialog
*pThis
= reinterpret_cast<COpenWithDialog
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
1030 COpenWithDialog
*pThis
= reinterpret_cast<COpenWithDialog
*>(lParam
);
1032 pThis
->Init(hwndDlg
);
1036 switch(LOWORD(wParam
))
1038 case 14004: /* browse */
1048 case IDCANCEL
: /* cancel */
1049 EndDialog(hwndDlg
, 0);
1056 switch (((LPNMHDR
)lParam
)->code
)
1058 case TVN_SELCHANGED
:
1059 EnableWindow(GetDlgItem(hwndDlg
, IDOK
), pThis
->GetCurrentApp() ? TRUE
: FALSE
);
1068 EndDialog(hwndDlg
, 0);
1076 COpenWithMenu::COpenWithMenu()
1080 m_pAppList
= new COpenWithList
;
1083 COpenWithMenu::~COpenWithMenu()
1085 TRACE("Destroying COpenWithMenu(%p)\n", this);
1092 /* get item count */
1093 Count
= GetMenuItemCount(m_hSubMenu
);
1097 /* setup menuitem info */
1098 ZeroMemory(&mii
, sizeof(mii
));
1099 mii
.cbSize
= sizeof(mii
);
1100 mii
.fMask
= MIIM_DATA
| MIIM_FTYPE
| MIIM_CHECKMARKS
;
1102 for(Index
= 0; Index
< Count
; Index
++)
1104 if (GetMenuItemInfoW(m_hSubMenu
, Index
, TRUE
, &mii
))
1106 if (mii
.hbmpChecked
)
1107 DeleteObject(mii
.hbmpChecked
);
1116 HBITMAP
COpenWithMenu::IconToBitmap(HICON hIcon
)
1119 HBITMAP hbm
, hbmOld
;
1122 hdcScr
= GetDC(NULL
);
1123 hdc
= CreateCompatibleDC(hdcScr
);
1124 SetRect(&rc
, 0, 0, GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
));
1125 hbm
= CreateCompatibleBitmap(hdcScr
, rc
.right
, rc
.bottom
);
1126 ReleaseDC(NULL
, hdcScr
);
1128 hbmOld
= (HBITMAP
)SelectObject(hdc
, hbm
);
1129 FillRect(hdc
, &rc
, (HBRUSH
)(COLOR_MENU
+ 1));
1130 if (!DrawIconEx(hdc
, 0, 0, hIcon
, rc
.right
, rc
.bottom
, 0, NULL
, DI_NORMAL
))
1131 ERR("DrawIcon failed: %x\n", GetLastError());
1132 SelectObject(hdc
, hbmOld
);
1139 VOID
COpenWithMenu::AddChooseProgramItem()
1144 ZeroMemory(&mii
, sizeof(mii
));
1145 mii
.cbSize
= sizeof(mii
);
1146 mii
.fMask
= MIIM_TYPE
| MIIM_ID
;
1147 mii
.fType
= MFT_SEPARATOR
;
1149 InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
);
1151 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_CHOOSE
, wszBuf
, _countof(wszBuf
)))
1153 ERR("Failed to load string\n");
1157 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
1158 mii
.fType
= MFT_STRING
;
1159 mii
.fState
= MFS_ENABLED
;
1160 mii
.wID
= m_idCmdLast
;
1161 mii
.dwTypeData
= (LPWSTR
)wszBuf
;
1162 mii
.cch
= wcslen(wszBuf
);
1164 InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
);
1167 VOID
COpenWithMenu::AddApp(PVOID pApp
)
1170 LPCWSTR pwszName
= m_pAppList
->GetName((COpenWithList::SApp
*)pApp
);
1172 ZeroMemory(&mii
, sizeof(mii
));
1173 mii
.cbSize
= sizeof(mii
);
1174 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
1175 mii
.fType
= MFT_STRING
;
1176 mii
.fState
= MFS_ENABLED
;
1177 mii
.wID
= m_idCmdLast
;
1178 mii
.dwTypeData
= (LPWSTR
)pwszName
;
1179 mii
.cch
= wcslen(mii
.dwTypeData
);
1180 mii
.dwItemData
= (ULONG_PTR
)pApp
;
1182 HICON hIcon
= m_pAppList
->GetIcon((COpenWithList::SApp
*)pApp
);
1185 mii
.fMask
|= MIIM_CHECKMARKS
;
1186 mii
.hbmpChecked
= mii
.hbmpUnchecked
= IconToBitmap(hIcon
);
1189 if (InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
))
1193 HRESULT WINAPI
COpenWithMenu::QueryContextMenu(
1200 TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1202 INT DefaultPos
= GetMenuDefaultItem(hMenu
, TRUE
, 0);
1205 UINT NameId
= (DefaultPos
== -1 ? IDS_OPEN
: IDS_OPEN_WITH
);
1206 if (!LoadStringW(shell32_hInstance
, NameId
, wszName
, _countof(wszName
)))
1208 ERR("Failed to load string\n");
1212 /* Init first cmd id and submenu */
1213 m_idCmdFirst
= m_idCmdLast
= idCmdFirst
;
1216 /* If we are going to be default item, we shouldn't be submenu */
1217 if (DefaultPos
!= -1)
1219 /* Load applications list */
1221 m_pAppList
->LoadRecommended(m_wszPath
);
1223 /* Create submenu only if there is more than one application and menu has a default item */
1224 if (m_pAppList
->GetRecommendedCount() > 1)
1226 m_hSubMenu
= CreatePopupMenu();
1228 for(UINT i
= 0; i
< m_pAppList
->GetCount(); ++i
)
1230 COpenWithList::SApp
*pApp
= m_pAppList
->GetList() + i
;
1231 if (pApp
->bRecommended
)
1235 AddChooseProgramItem();
1239 /* Insert menu item */
1241 ZeroMemory(&mii
, sizeof(mii
));
1242 mii
.cbSize
= sizeof(mii
);
1243 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
1246 mii
.fMask
|= MIIM_SUBMENU
;
1247 mii
.hSubMenu
= m_hSubMenu
;
1251 mii
.wID
= m_idCmdLast
;
1253 mii
.fType
= MFT_STRING
;
1254 mii
.dwTypeData
= (LPWSTR
)wszName
;
1255 mii
.cch
= wcslen(wszName
);
1257 mii
.fState
= MFS_ENABLED
;
1258 if (DefaultPos
== -1)
1259 mii
.fState
|= MFS_DEFAULT
;
1261 if (!InsertMenuItemW(hMenu
, DefaultPos
+ 1, TRUE
, &mii
))
1264 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, m_idCmdLast
- m_idCmdFirst
+ 1);
1268 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1270 HRESULT hr
= E_FAIL
;
1272 TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst
, m_idCmdLast
, m_idCmdFirst
+ LOWORD(lpici
->lpVerb
));
1274 if (HIWORD(lpici
->lpVerb
) == 0 && m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) <= m_idCmdLast
)
1276 if (m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) == m_idCmdLast
)
1279 LPCWSTR pwszExt
= PathFindExtensionW(m_wszPath
);
1281 info
.pcszFile
= m_wszPath
;
1282 info
.oaifInFlags
= OAIF_EXEC
;
1284 info
.oaifInFlags
|= OAIF_REGISTER_EXT
| OAIF_ALLOW_REGISTRATION
;
1285 info
.pcszClass
= NULL
;
1286 hr
= SHOpenWithDialog(lpici
->hwnd
, &info
);
1290 /* retrieve menu item info */
1292 ZeroMemory(&mii
, sizeof(mii
));
1293 mii
.cbSize
= sizeof(mii
);
1294 mii
.fMask
= MIIM_DATA
| MIIM_FTYPE
;
1296 if (GetMenuItemInfoW(m_hSubMenu
, LOWORD(lpici
->lpVerb
), TRUE
, &mii
) && mii
.dwItemData
)
1298 /* launch item with specified app */
1299 COpenWithList::SApp
*pApp
= (COpenWithList::SApp
*)mii
.dwItemData
;
1300 COpenWithList::Execute(pApp
, m_wszPath
);
1310 COpenWithMenu::GetCommandString(UINT_PTR idCmd
, UINT uType
,
1311 UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1313 FIXME("%p %lu %u %p %p %u\n", this,
1314 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1319 HRESULT WINAPI
COpenWithMenu::HandleMenuMsg(
1324 TRACE("This %p uMsg %x\n", this, uMsg
);
1330 COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder
,
1331 IDataObject
*pdtobj
,
1338 LPCITEMIDLIST pidlFolder2
;
1339 LPCITEMIDLIST pidlChild
;
1343 TRACE("This %p\n", this);
1346 return E_INVALIDARG
;
1348 fmt
.cfFormat
= RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
1350 fmt
.dwAspect
= DVASPECT_CONTENT
;
1352 fmt
.tymed
= TYMED_HGLOBAL
;
1354 hr
= pdtobj
->GetData(&fmt
, &medium
);
1358 ERR("pdtobj->GetData failed with 0x%x\n", hr
);
1362 pida
= (LPIDA
)GlobalLock(medium
.hGlobal
);
1363 ASSERT(pida
->cidl
>= 1);
1365 pidlFolder2
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+ pida
->aoffset
[0]);
1366 pidlChild
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+ pida
->aoffset
[1]);
1368 if (!_ILIsValue(pidlChild
))
1370 TRACE("pidl is not a file\n");
1371 GlobalUnlock(medium
.hGlobal
);
1372 GlobalFree(medium
.hGlobal
);
1376 pidl
= ILCombine(pidlFolder2
, pidlChild
);
1378 GlobalUnlock(medium
.hGlobal
);
1379 GlobalFree(medium
.hGlobal
);
1384 return E_OUTOFMEMORY
;
1387 if (!SHGetPathFromIDListW(pidl
, m_wszPath
))
1389 SHFree((void*)pidl
);
1390 ERR("SHGetPathFromIDListW failed\n");
1394 SHFree((void*)pidl
);
1395 TRACE("szPath %s\n", debugstr_w(m_wszPath
));
1397 pwszExt
= PathFindExtensionW(m_wszPath
);
1398 if (PathIsExeW(pwszExt
) || !_wcsicmp(pwszExt
, L
".lnk"))
1400 TRACE("file is a executable or shortcut\n");
1408 SHOpenWithDialog(HWND hwndParent
, const OPENASINFO
*poainfo
)
1412 TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent
, poainfo
);
1414 InitCommonControls();
1416 if (poainfo
->pcszClass
== NULL
&& poainfo
->pcszFile
== NULL
)
1419 COpenWithDialog
pDialog(poainfo
);
1421 if (pDialog
.IsNoOpen(hwndParent
))
1424 ret
= DialogBoxParamW(shell32_hInstance
, MAKEINTRESOURCE(IDD_OPEN_WITH
), hwndParent
,
1425 COpenWithDialog::DialogProc
, (LPARAM
)&pDialog
);
1427 if (ret
== (INT_PTR
)-1)
1429 ERR("Failed to create dialog: %u\n", GetLastError());