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 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 BOOL
LoadRecommended(LPCWSTR pwszFilePath
);
61 BOOL
SetDefaultHandler(SApp
*pApp
, LPCWSTR pwszFilename
);
63 inline SApp
*GetList() { return m_pApp
; }
64 inline UINT
GetCount() { return m_cApp
; }
65 inline UINT
GetRecommendedCount() { return m_cRecommended
; }
68 typedef struct _LANGANDCODEPAGE
72 } LANGANDCODEPAGE
, *LPLANGANDCODEPAGE
;
75 UINT m_cApp
, m_cRecommended
;
78 SApp
*AddInternal(LPCWSTR pwszFilename
);
79 static BOOL
LoadInfo(SApp
*pApp
);
80 static VOID
GetPathFromCmd(LPWSTR pwszAppPath
, LPCWSTR pwszCmd
);
81 BOOL
LoadProgIdList(HKEY hKey
, LPCWSTR pwszExt
);
82 static HANDLE
OpenMRUList(HKEY hKey
);
83 BOOL
LoadMRUList(HKEY hKey
);
84 BOOL
LoadAppList(HKEY hKey
);
85 VOID
LoadFromProgIdKey(HKEY hKey
, LPCWSTR pwszExt
);
86 VOID
LoadRecommendedFromHKCR(LPCWSTR pwszExt
);
87 VOID
LoadRecommendedFromHKCU(LPCWSTR pwszExt
);
88 static BOOL
AddAppToMRUList(SApp
*pApp
, LPCWSTR pwszFilename
);
90 inline VOID
SetRecommended(SApp
*pApp
)
92 if (!pApp
->bRecommended
)
94 pApp
->bRecommended
= TRUE
;
98 COpenWithList::COpenWithList():
99 m_pApp(NULL
), m_cApp(0), m_cRecommended(0), m_bNoOpen(FALSE
) {}
101 COpenWithList::~COpenWithList()
103 for (UINT i
= 0; i
< m_cApp
; ++i
)
105 DestroyIcon(m_pApp
[i
].hIcon
);
107 HeapFree(GetProcessHeap(), 0, m_pApp
);
110 BOOL
COpenWithList::Load()
113 WCHAR wszName
[256], wszBuf
[100];;
114 DWORD i
= 0, cchName
, dwSize
;
117 if (RegOpenKeyEx(HKEY_CLASSES_ROOT
, L
"Applications", 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
119 ERR("RegOpenKeyEx HKCR\\Applications failed!\n");
125 cchName
= _countof(wszName
);
126 if (RegEnumKeyEx(hKey
, i
++, wszName
, &cchName
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
129 pApp
= AddInternal(wszName
);
133 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s\\shell\\open\\command", wszName
);
134 dwSize
= sizeof(pApp
->wszCmd
);
135 if (RegGetValueW(hKey
, wszBuf
, L
"", RRF_RT_REG_SZ
, NULL
, pApp
->wszCmd
, &dwSize
) != ERROR_SUCCESS
)
137 ERR("Failed to add app %ls\n", wszName
);
138 pApp
->bHidden
= TRUE
;
141 TRACE("App added %ls\n", pApp
->wszCmd
);
144 ERR("AddInternal failed\n");
151 COpenWithList::SApp
*COpenWithList::Add(LPCWSTR pwszPath
)
153 SApp
*pApp
= AddInternal(PathFindFileNameW(pwszPath
));
157 StringCbPrintfW(pApp
->wszCmd
, sizeof(pApp
->wszCmd
), L
"\"%s\" %%1", pwszPath
);
164 BOOL
COpenWithList::SaveApp(SApp
*pApp
)
169 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s\\shell\\open\\command", pApp
->wszFilename
);
170 if (RegCreateKeyEx(HKEY_CLASSES_ROOT
, wszBuf
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
172 ERR("RegOpenKeyEx failed\n");
176 if (RegSetValueEx(hKey
, L
"", 0, REG_SZ
, (PBYTE
)pApp
->wszCmd
, (wcslen(pApp
->wszCmd
)+1)*sizeof(WCHAR
)) != ERROR_SUCCESS
)
177 ERR("Cannot add app to registry\n");
183 COpenWithList::SApp
*COpenWithList::Find(LPCWSTR pwszFilename
)
185 for (UINT i
= 0; i
< m_cApp
; ++i
)
186 if (wcsicmp(m_pApp
[i
].wszFilename
, pwszFilename
) == 0)
191 LPCWSTR
COpenWithList::GetName(SApp
*pApp
)
193 if (!pApp
->wszName
[0])
197 WARN("Failed to load %ls info\n", pApp
->wszFilename
);
198 StringCbCopyW(pApp
->wszName
, sizeof(pApp
->wszName
), pApp
->wszFilename
);
202 TRACE("%ls name: %ls\n", pApp
->wszFilename
, pApp
->wszName
);
203 return pApp
->wszName
;
206 HICON
COpenWithList::GetIcon(SApp
*pApp
)
210 WCHAR wszPath
[MAX_PATH
];
212 GetPathFromCmd(wszPath
, pApp
->wszCmd
);
213 pApp
->hIcon
= ExtractIconW(shell32_hInstance
, wszPath
, 0);
216 TRACE("%ls icon: %p\n", pApp
->wszFilename
, pApp
->hIcon
);
221 BOOL
COpenWithList::Execute(COpenWithList::SApp
*pApp
, LPCWSTR pwszFilePath
)
224 PROCESS_INFORMATION pi
;
225 WCHAR wszBuf
[MAX_PATH
* 2 + 8], *pszEnd
= wszBuf
;
226 size_t cchRemaining
= _countof(wszBuf
);
228 /* setup path with argument */
229 ZeroMemory(&si
, sizeof(STARTUPINFOW
));
230 si
.cb
= sizeof(STARTUPINFOW
);
232 /* Build the command line */
233 for (UINT i
= 0; pApp
->wszCmd
[i
] && cchRemaining
> 1; ++i
)
235 if (pApp
->wszCmd
[i
] != '%')
237 *(pszEnd
++) = pApp
->wszCmd
[i
];
240 else if (pApp
->wszCmd
[++i
] == '1')
242 if (StrChrW(pwszFilePath
, L
' ') && cchRemaining
> 3)
243 StringCchPrintfExW(pszEnd
, cchRemaining
, &pszEnd
, &cchRemaining
, 0, L
"\"%ls\"", pwszFilePath
);
245 StringCchCopyExW(pszEnd
, cchRemaining
, pwszFilePath
, &pszEnd
, &cchRemaining
, 0);
248 /* NULL-terminate the command string */
249 if (cchRemaining
> 0)
252 /* Start the application now */
253 TRACE("Starting process %ls\n", wszBuf
);
254 if (!CreateProcessW(NULL
, wszBuf
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
256 ERR("CreateProcessW %ls failed\n", wszBuf
);
260 /* Add app to registry if it wasnt there before */
263 AddAppToMRUList(pApp
, pwszFilePath
);
265 CloseHandle(pi
.hThread
);
266 CloseHandle(pi
.hProcess
);
270 BOOL
COpenWithList::IsHidden(SApp
*pApp
)
276 return pApp
->bHidden
;
278 if (FAILED(StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s", pApp
->wszFilename
)))
280 ERR("insufficient buffer\n");
284 if (RegGetValueW(HKEY_CLASSES_ROOT
, wszBuf
, L
"NoOpenWith", RRF_RT_REG_SZ
, NULL
, NULL
, &dwSize
) != ERROR_SUCCESS
)
287 pApp
->bHidden
= TRUE
;
291 COpenWithList::SApp
*COpenWithList::AddInternal(LPCWSTR pwszFilename
)
293 /* Check for duplicate */
294 SApp
*pApp
= Find(pwszFilename
);
298 /* Create new item */
300 m_pApp
= static_cast<SApp
*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(m_pApp
[0])));
302 m_pApp
= static_cast<SApp
*>(HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, m_pApp
, (m_cApp
+ 1)*sizeof(m_pApp
[0])));
305 ERR("Allocation failed\n");
309 pApp
= &m_pApp
[m_cApp
++];
310 wcscpy(pApp
->wszFilename
, pwszFilename
);
314 BOOL
COpenWithList::LoadInfo(COpenWithList::SApp
*pApp
)
318 WORD wLang
= 0, wCode
= 0;
319 LPLANGANDCODEPAGE lpLangCode
;
322 WCHAR wszPath
[MAX_PATH
];
324 GetPathFromCmd(wszPath
, pApp
->wszCmd
);
325 TRACE("LoadInfo %ls\n", wszPath
);
327 /* query version info size */
328 cbSize
= GetFileVersionInfoSizeW(wszPath
, NULL
);
331 ERR("GetFileVersionInfoSizeW %ls failed: %lu\n", wszPath
, GetLastError());
335 /* allocate buffer */
336 pBuf
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, cbSize
+ 200);
339 ERR("HeapAlloc failed\n");
343 /* query version info */
344 if (!GetFileVersionInfoW(wszPath
, 0, cbSize
, pBuf
))
346 ERR("GetFileVersionInfoW %ls failed: %lu\n", wszPath
, GetLastError());
347 HeapFree(GetProcessHeap(), 0, pBuf
);
351 /* query lang code */
352 if (VerQueryValueW(pBuf
, L
"VarFileInfo\\Translation", (LPVOID
*)&lpLangCode
, &cbSize
))
354 /* FIXME: find language from current locale / if not available,
356 * for now default to first available language
358 wLang
= lpLangCode
->lang
;
359 wCode
= lpLangCode
->code
;
363 swprintf(wszBuf
, L
"\\StringFileInfo\\%04x%04x\\FileDescription", wLang
, wCode
);
364 if (VerQueryValueW(pBuf
, wszBuf
, (LPVOID
*)&pResult
, &cchLen
))
365 StringCchCopyNW(pApp
->wszName
, _countof(pApp
->wszName
), pResult
, cchLen
);
367 ERR("Cannot get app name\n");
369 /* Query manufacturer */
370 /*swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\CompanyName", wLang, wCode);
372 if (VerQueryValueW(pBuf, wszBuf, (LPVOID *)&pResult, &cchLen))
373 StringCchCopyNW(pApp->wszManufacturer, _countof(pApp->wszManufacturer), pResult, cchLen);*/
374 HeapFree(GetProcessHeap(), 0, pBuf
);
378 VOID
COpenWithList::GetPathFromCmd(LPWSTR pwszAppPath
, LPCWSTR pwszCmd
)
380 WCHAR wszBuf
[MAX_PATH
], *pwszDest
= wszBuf
;
382 /* Remove arguments */
383 if (pwszCmd
[0] == '"')
385 for(LPCWSTR pwszSrc
= pwszCmd
+ 1; *pwszSrc
&& *pwszSrc
!= '"'; ++pwszSrc
)
386 *(pwszDest
++) = *pwszSrc
;
390 for(LPCWSTR pwszSrc
= pwszCmd
; *pwszSrc
&& *pwszSrc
!= ' '; ++pwszSrc
)
391 *(pwszDest
++) = *pwszSrc
;
396 /* Expand evn vers and optionally search for path */
397 ExpandEnvironmentStrings(wszBuf
, pwszAppPath
, MAX_PATH
);
398 if (!PathFileExists(pwszAppPath
))
399 SearchPath(NULL
, pwszAppPath
, NULL
, MAX_PATH
, pwszAppPath
, NULL
);
402 BOOL
COpenWithList::LoadRecommended(LPCWSTR pwszFilePath
)
406 pwszExt
= PathFindExtensionW(pwszFilePath
);
410 /* load programs directly associated from HKCU */
411 LoadRecommendedFromHKCU(pwszExt
);
413 /* load programs associated from HKCR\Extension */
414 LoadRecommendedFromHKCR(pwszExt
);
419 BOOL
COpenWithList::LoadProgIdList(HKEY hKey
, LPCWSTR pwszExt
)
421 HKEY hSubkey
, hSubkey2
;
422 WCHAR wszProgId
[256];
423 DWORD i
= 0, cchProgId
;
425 if (RegOpenKeyExW(hKey
, L
"OpenWithProgIDs", 0, KEY_READ
, &hSubkey
) != ERROR_SUCCESS
)
430 /* Enumerate values - value name is ProgId */
431 cchProgId
= _countof(wszProgId
);
432 if (RegEnumValue(hSubkey
, i
++, wszProgId
, &cchProgId
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
435 /* If ProgId exists load it */
436 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszProgId
, 0, KEY_READ
, &hSubkey2
) == ERROR_SUCCESS
)
438 LoadFromProgIdKey(hSubkey2
, pwszExt
);
439 RegCloseKey(hSubkey2
);
443 RegCloseKey(hSubkey
);
447 HANDLE
COpenWithList::OpenMRUList(HKEY hKey
)
451 /* Initialize mru list info */
452 Info
.cbSize
= sizeof(Info
);
454 Info
.dwFlags
= MRU_STRING
;
456 Info
.lpszSubKey
= L
"OpenWithList";
457 Info
.lpfnCompare
= NULL
;
459 return CreateMRUListW(&Info
);
462 BOOL
COpenWithList::LoadMRUList(HKEY hKey
)
465 int nItem
, nCount
, nResult
;
466 WCHAR wszAppFilename
[MAX_PATH
];
469 hList
= OpenMRUList(hKey
);
472 TRACE("OpenMRUList failed\n");
477 nCount
= EnumMRUListW(hList
, -1, NULL
, 0);
479 for(nItem
= 0; nItem
< nCount
; nItem
++)
481 nResult
= EnumMRUListW(hList
, nItem
, wszAppFilename
, _countof(wszAppFilename
));
486 SApp
*pApp
= Find(wszAppFilename
);
488 TRACE("Recommended app %ls: %p\n", wszAppFilename
, pApp
);
491 pApp
->bMRUList
= TRUE
;
492 SetRecommended(pApp
);
496 /* Free the MRU list */
501 BOOL
COpenWithList::LoadAppList(HKEY hKey
)
503 WCHAR wszAppFilename
[MAX_PATH
];
505 DWORD i
= 0, cchAppFilename
;
507 if (RegOpenKeyExW(hKey
, L
"OpenWithList", 0, KEY_READ
, &hSubkey
) != ERROR_SUCCESS
)
512 /* Enum registry keys - each of them is app name */
513 cchAppFilename
= _countof(wszAppFilename
);
514 if (RegEnumKeyExW(hSubkey
, i
++, wszAppFilename
, &cchAppFilename
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
517 /* Set application as recommended */
518 SApp
*pApp
= Find(wszAppFilename
);
520 TRACE("Recommended app %ls: %p\n", wszAppFilename
, pApp
);
522 SetRecommended(pApp
);
525 RegCloseKey(hSubkey
);
529 VOID
COpenWithList::LoadFromProgIdKey(HKEY hKey
, LPCWSTR pwszExt
)
531 WCHAR wszCmd
[MAX_PATH
], wszPath
[MAX_PATH
];
534 /* Check if NoOpen value exists */
535 if (RegGetValueW(hKey
, NULL
, L
"NoOpen", RRF_RT_REG_SZ
, NULL
, NULL
, &dwSize
) == ERROR_SUCCESS
)
537 /* Display warning dialog */
541 /* Check if there is a directly available execute key */
542 dwSize
= sizeof(wszCmd
);
543 if (RegGetValueW(hKey
, L
"shell\\open\\command", NULL
, RRF_RT_REG_SZ
, NULL
, (PVOID
)wszCmd
, &dwSize
) == ERROR_SUCCESS
)
545 /* Erase extra arguments */
546 GetPathFromCmd(wszPath
, wszCmd
);
548 /* Add application */
549 SApp
*pApp
= AddInternal(PathFindFileNameW(wszPath
));
550 TRACE("Add app %ls: %p\n", wszPath
, pApp
);
554 StringCbCopyW(pApp
->wszCmd
, sizeof(pApp
->wszCmd
), wszCmd
);
555 SetRecommended(pApp
);
557 AddAppToMRUList(pApp
, pwszExt
);
562 VOID
COpenWithList::LoadRecommendedFromHKCR(LPCWSTR pwszExt
)
565 WCHAR wszBuf
[MAX_PATH
], wszBuf2
[MAX_PATH
];
568 /* Check if extension exists */
569 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
571 /* Load items from SystemFileAssociations\Ext key */
572 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"SystemFileAssociations\\%s", pwszExt
);
573 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
577 /* Load programs referenced from HKCR\ProgId */
578 dwSize
= sizeof(wszBuf
);
579 if (RegGetValueW(hKey
, NULL
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
&&
580 RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hSubkey
) == ERROR_SUCCESS
)
582 LoadFromProgIdKey(hSubkey
, pwszExt
);
583 RegCloseKey(hSubkey
);
586 LoadFromProgIdKey(hKey
, pwszExt
);
588 /* Load items from HKCR\Ext\OpenWithList */
591 /* Load items from HKCR\Ext\OpenWithProgIDs */
592 if (RegOpenKeyExW(hKey
, L
"OpenWithProgIDs", 0, KEY_READ
, &hSubkey
) == ERROR_SUCCESS
)
594 LoadProgIdList(hSubkey
, pwszExt
);
595 RegCloseKey(hSubkey
);
598 /* Load additional items from referenced PerceivedType */
599 dwSize
= sizeof(wszBuf
);
600 if (RegGetValueW(hKey
, NULL
, L
"PerceivedType", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) == ERROR_SUCCESS
)
604 StringCbPrintfW(wszBuf2
, sizeof(wszBuf2
), L
"SystemFileAssociations\\%s", wszBuf
);
605 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf2
, 0, KEY_READ
| KEY_WRITE
, &hSubkey
) == ERROR_SUCCESS
)
607 /* Load from OpenWithList key */
608 LoadAppList(hSubkey
);
609 RegCloseKey(hSubkey
);
617 VOID
COpenWithList::LoadRecommendedFromHKCU(LPCWSTR pwszExt
)
619 WCHAR wszBuf
[MAX_PATH
];
622 StringCbPrintfW(wszBuf
, sizeof(wszBuf
),
623 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
625 if (RegOpenKeyExW(HKEY_CURRENT_USER
, wszBuf
, 0, KEY_READ
, &hKey
) == ERROR_SUCCESS
)
627 /* Load MRU and ProgId lists */
629 LoadProgIdList(hKey
, pwszExt
);
631 /* Handle "Aplication" value */
632 DWORD cbBuf
= sizeof(wszBuf
);
633 if (RegGetValueW(hKey
, NULL
, L
"Application", RRF_RT_REG_SZ
, NULL
, wszBuf
, &cbBuf
) == ERROR_SUCCESS
)
635 SApp
*pApp
= Find(wszBuf
);
637 SetRecommended(pApp
);
645 BOOL
COpenWithList::AddAppToMRUList(SApp
*pApp
, LPCWSTR pwszFilename
)
652 /* Get file extension */
653 pwszExt
= PathFindExtensionW(pwszFilename
);
657 /* Build registry key */
658 if (FAILED(StringCbPrintfW(wszBuf
, sizeof(wszBuf
),
659 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
662 ERR("insufficient buffer\n");
666 /* Open base key for this file extension */
667 if (RegCreateKeyExW(HKEY_CURRENT_USER
, wszBuf
, 0, NULL
, 0, KEY_WRITE
| KEY_READ
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
671 hList
= OpenMRUList(hKey
);
674 /* Insert the entry */
675 AddMRUStringW(hList
, pApp
->wszFilename
);
685 BOOL
COpenWithList::SetDefaultHandler(SApp
*pApp
, LPCWSTR pwszFilename
)
687 HKEY hKey
, hSrcKey
, hDestKey
;
691 TRACE("SetDefaultHandler %ls %ls", pApp
->wszFilename
, pwszFilename
);
693 /* Extract file extension */
694 LPCWSTR pwszExt
= PathFindExtensionW(pwszFilename
);
695 if (!pwszExt
[0] || !pwszExt
[1])
698 /* Create file extension key */
699 if (RegCreateKeyExW(HKEY_CLASSES_ROOT
, pwszExt
, 0, NULL
, 0, KEY_READ
|KEY_WRITE
, NULL
, &hKey
, &dwDisposition
) != ERROR_SUCCESS
)
701 ERR("Cannot open ext key");
705 if (dwDisposition
== REG_CREATED_NEW_KEY
)
707 /* A new entry was created create the prog key id */
708 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"%s_auto_file", pwszExt
+ 1);
709 if (RegSetValueExW(hKey
, L
"", 0, REG_SZ
, (const BYTE
*)wszBuf
, (wcslen(wszBuf
) + 1) * sizeof(WCHAR
)) != ERROR_SUCCESS
)
712 ERR("RegSetValueExW failed\n");
718 /* Entry already exists fetch prog key id */
719 DWORD dwSize
= sizeof(wszBuf
);
720 if (RegGetValueW(hKey
, NULL
, L
"", RRF_RT_REG_SZ
, NULL
, wszBuf
, &dwSize
) != ERROR_SUCCESS
)
722 ERR("RegGetValueW failed: %lu\n", GetLastError());
728 /* Close file extension key */
731 /* Create prog id key */
732 if (RegCreateKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, NULL
, 0, KEY_WRITE
, NULL
, &hKey
, NULL
) != ERROR_SUCCESS
)
734 ERR("RegCreateKeyExW failed\n");
738 /* Check if there already verbs existing for that app */
739 StringCbPrintfW(wszBuf
, sizeof(wszBuf
), L
"Applications\\%s\\shell", pApp
->wszFilename
);
740 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
, wszBuf
, 0, KEY_READ
, &hSrcKey
) != ERROR_SUCCESS
)
742 ERR("RegOpenKeyExW %ls failed\n", wszBuf
);
747 /* Open destination key */
748 if (RegCreateKeyExW(hKey
, L
"shell", 0, NULL
, 0, KEY_WRITE
, NULL
, &hDestKey
, NULL
) != ERROR_SUCCESS
)
750 ERR("RegCreateKeyExW failed\n");
751 RegCloseKey(hSrcKey
);
756 /* Copy static verbs from Classes\Applications key */
757 LONG Result
= RegCopyTreeW(hSrcKey
, NULL
, hDestKey
);
758 RegCloseKey(hDestKey
);
759 RegCloseKey(hSrcKey
);
762 if (Result
!= ERROR_SUCCESS
)
764 ERR("RegCopyTreeW failed\n");
771 class COpenWithDialog
774 COpenWithDialog(const OPENASINFO
*pInfo
, COpenWithList
*pAppList
);
776 static INT_PTR CALLBACK
DialogProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
779 VOID
Init(HWND hwnd
);
780 VOID
AddApp(COpenWithList::SApp
*pApp
, BOOL bSelected
);
783 COpenWithList::SApp
*GetCurrentApp();
785 const OPENASINFO
*m_pInfo
;
786 COpenWithList
*m_pAppList
;
787 BOOL m_bListAllocated
;
788 HWND m_hDialog
, m_hTreeView
;
789 HTREEITEM m_hRecommend
;
791 HIMAGELIST m_hImgList
;
794 COpenWithDialog::COpenWithDialog(const OPENASINFO
*pInfo
, COpenWithList
*pAppList
= NULL
):
795 m_pInfo(pInfo
), m_pAppList(pAppList
), m_hImgList(NULL
)
799 m_pAppList
= new COpenWithList
;
800 m_bListAllocated
= TRUE
;
803 m_bListAllocated
= FALSE
;
806 COpenWithDialog::~COpenWithDialog()
808 if (m_bListAllocated
&& m_pAppList
)
811 ImageList_Destroy(m_hImgList
);
814 VOID
COpenWithDialog::AddApp(COpenWithList::SApp
*pApp
, BOOL bSelected
)
816 LPCWSTR pwszName
= m_pAppList
->GetName(pApp
);
817 HICON hIcon
= m_pAppList
->GetIcon(pApp
);
819 TRACE("AddApp Cmd %ls Name %ls\n", pApp
->wszCmd
, pwszName
);
821 /* Add item to the list */
822 TVINSERTSTRUCT tvins
;
824 if (pApp
->bRecommended
)
825 tvins
.hParent
= tvins
.hInsertAfter
= m_hRecommend
;
827 tvins
.hParent
= tvins
.hInsertAfter
= m_hOther
;
829 tvins
.item
.mask
= TVIF_TEXT
|TVIF_PARAM
;
830 tvins
.item
.pszText
= (LPWSTR
)pwszName
;
831 tvins
.item
.lParam
= (LPARAM
)pApp
;
835 tvins
.item
.mask
|= TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
836 tvins
.item
.iImage
= tvins
.item
.iSelectedImage
= ImageList_AddIcon(m_hImgList
, hIcon
);
839 HTREEITEM hItem
= TreeView_InsertItem(m_hTreeView
, &tvins
);
842 TreeView_SelectItem(m_hTreeView
, hItem
);
845 VOID
COpenWithDialog::Browse()
848 WCHAR wszFilter
[256];
849 WCHAR wszPath
[MAX_PATH
];
852 /* Initialize OPENFILENAMEW structure */
853 ZeroMemory(&ofn
, sizeof(OPENFILENAMEW
));
854 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
855 ofn
.hInstance
= shell32_hInstance
;
856 ofn
.hwndOwner
= m_hDialog
;
857 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
858 ofn
.nMaxFile
= (sizeof(wszPath
) / sizeof(WCHAR
));
859 ofn
.lpstrFile
= wszPath
;
862 if (LoadStringW(shell32_hInstance
, IDS_OPEN_WITH
, wszTitle
, sizeof(wszTitle
) / sizeof(WCHAR
)))
864 ofn
.lpstrTitle
= wszTitle
;
865 ofn
.nMaxFileTitle
= wcslen(wszTitle
);
868 /* Init the filter string */
869 if (LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_FILTER
, wszFilter
, sizeof(wszFilter
) / sizeof(WCHAR
)))
870 ofn
.lpstrFilter
= wszFilter
;
871 ZeroMemory(wszPath
, sizeof(wszPath
));
873 /* Create OpenFile dialog */
874 if (!GetOpenFileNameW(&ofn
))
877 /* Setup context for insert proc */
878 COpenWithList::SApp
*pApp
= m_pAppList
->Add(wszPath
);
882 COpenWithList::SApp
*COpenWithDialog::GetCurrentApp()
885 tvi
.hItem
= TreeView_GetSelection(m_hTreeView
);
889 tvi
.mask
= TVIF_PARAM
;
890 if (!TreeView_GetItem(m_hTreeView
, &tvi
))
893 return (COpenWithList::SApp
*)tvi
.lParam
;
896 VOID
COpenWithDialog::Init(HWND hwnd
)
898 TRACE("COpenWithDialog::Init hwnd %p\n", hwnd
);
901 SetWindowLongPtr(hwnd
, DWLP_USER
, (LONG_PTR
)this);
903 /* Handle register checkbox */
904 HWND hRegisterCheckbox
= GetDlgItem(hwnd
, 14003);
905 if (!(m_pInfo
->oaifInFlags
& OAIF_ALLOW_REGISTRATION
))
906 EnableWindow(hRegisterCheckbox
, FALSE
);
907 if (m_pInfo
->oaifInFlags
& OAIF_FORCE_REGISTRATION
)
908 SendMessage(hRegisterCheckbox
, BM_SETCHECK
, BST_CHECKED
, 0);
909 if (m_pInfo
->oaifInFlags
& OAIF_HIDE_REGISTRATION
)
910 ShowWindow(hRegisterCheckbox
, SW_HIDE
);
912 if (m_pInfo
->pcszFile
)
914 WCHAR wszBuf
[MAX_PATH
];
917 /* Add filename to label */
918 cchBuf
= GetDlgItemTextW(hwnd
, 14001, wszBuf
, _countof(wszBuf
));
919 StringCchCopyW(wszBuf
+ cchBuf
, _countof(wszBuf
) - cchBuf
, PathFindFileNameW(m_pInfo
->pcszFile
));
920 SetDlgItemTextW(hwnd
, 14001, wszBuf
);
922 /* Load applications from registry */
924 m_pAppList
->LoadRecommended(m_pInfo
->pcszFile
);
927 m_hTreeView
= GetDlgItem(hwnd
, 14002);
928 m_hImgList
= ImageList_Create(16, 16, 0, m_pAppList
->GetCount() + 1, m_pAppList
->GetCount() + 1);
929 (void)TreeView_SetImageList(m_hTreeView
, m_hImgList
, TVSIL_NORMAL
);
931 /* If there are some recommendations add parent nodes: Recommended and Others */
932 UINT cRecommended
= m_pAppList
->GetRecommendedCount();
933 if (cRecommended
> 0)
935 TVINSERTSTRUCT tvins
;
938 tvins
.hParent
= tvins
.hInsertAfter
= TVI_ROOT
;
939 tvins
.item
.mask
= TVIF_TEXT
|TVIF_STATE
|TVIF_IMAGE
|TVIF_SELECTEDIMAGE
;
940 tvins
.item
.pszText
= (LPWSTR
)wszBuf
;
941 tvins
.item
.state
= tvins
.item
.stateMask
= TVIS_EXPANDED
;
942 hFolderIcon
= (HICON
)LoadImage(shell32_hInstance
, MAKEINTRESOURCE(IDI_SHELL_FOLDER
), IMAGE_ICON
, 0, 0, 0);
943 tvins
.item
.iImage
= tvins
.item
.iSelectedImage
= ImageList_AddIcon(m_hImgList
, hFolderIcon
);
945 LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_RECOMMENDED
, wszBuf
, _countof(wszBuf
));
946 m_hRecommend
= TreeView_InsertItem(m_hTreeView
, &tvins
);
948 LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_OTHER
, wszBuf
, _countof(wszBuf
));
949 m_hOther
= TreeView_InsertItem(m_hTreeView
, &tvins
);
952 m_hRecommend
= m_hOther
= TVI_ROOT
;
954 /* Add all applications */
955 BOOL bNoAppSelected
= TRUE
;
956 COpenWithList::SApp
*pAppList
= m_pAppList
->GetList();
957 for (UINT i
= 0; i
< m_pAppList
->GetCount(); ++i
)
959 if (!COpenWithList::IsHidden(&pAppList
[i
]))
961 if (bNoAppSelected
&& (pAppList
[i
].bRecommended
|| !cRecommended
))
963 AddApp(&pAppList
[i
], TRUE
);
964 bNoAppSelected
= FALSE
;
967 AddApp(&pAppList
[i
], FALSE
);
973 VOID
COpenWithDialog::Accept()
975 COpenWithList::SApp
*pApp
= GetCurrentApp();
978 /* Set programm as default handler */
979 if (SendDlgItemMessage(m_hDialog
, 14003, BM_GETCHECK
, 0, 0) == BST_CHECKED
)
980 m_pAppList
->SetDefaultHandler(pApp
, m_pInfo
->pcszFile
);
982 /* Execute program */
983 if (m_pInfo
->oaifInFlags
& OAIF_EXEC
)
984 m_pAppList
->Execute(pApp
, m_pInfo
->pcszFile
);
986 DestroyWindow(m_hDialog
);
990 INT_PTR CALLBACK
COpenWithDialog::DialogProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
992 COpenWithDialog
*pThis
= reinterpret_cast<COpenWithDialog
*>(GetWindowLongPtr(hwndDlg
, DWLP_USER
));
998 COpenWithDialog
*pThis
= reinterpret_cast<COpenWithDialog
*>(lParam
);
1000 pThis
->Init(hwndDlg
);
1004 switch(LOWORD(wParam
))
1006 case 14004: /* browse */
1016 case IDCANCEL
: /* cancel */
1017 DestroyWindow(hwndDlg
);
1024 switch (((LPNMHDR
)lParam
)->code
)
1026 case TVN_SELCHANGED
:
1027 EnableWindow(GetDlgItem(hwndDlg
, IDOK
), pThis
->GetCurrentApp() ? TRUE
: FALSE
);
1036 DestroyWindow(hwndDlg
);
1044 COpenWithMenu::COpenWithMenu()
1048 m_pAppList
= new COpenWithList
;
1051 COpenWithMenu::~COpenWithMenu()
1053 TRACE("Destroying COpenWithMenu(%p)\n", this);
1060 /* get item count */
1061 Count
= GetMenuItemCount(m_hSubMenu
);
1065 /* setup menuitem info */
1066 ZeroMemory(&mii
, sizeof(mii
));
1067 mii
.cbSize
= sizeof(mii
);
1068 mii
.fMask
= MIIM_DATA
| MIIM_FTYPE
| MIIM_CHECKMARKS
;
1070 for(Index
= 0; Index
< Count
; Index
++)
1072 if (GetMenuItemInfoW(m_hSubMenu
, Index
, TRUE
, &mii
))
1074 if (mii
.hbmpChecked
)
1075 DeleteObject(mii
.hbmpChecked
);
1084 HBITMAP
COpenWithMenu::IconToBitmap(HICON hIcon
)
1087 HBITMAP hbm
, hbmOld
;
1090 hdcScr
= GetDC(NULL
);
1091 hdc
= CreateCompatibleDC(hdcScr
);
1092 SetRect(&rc
, 0, 0, GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
));
1093 hbm
= CreateCompatibleBitmap(hdcScr
, rc
.right
, rc
.bottom
);
1094 ReleaseDC(NULL
, hdcScr
);
1096 hbmOld
= (HBITMAP
)SelectObject(hdc
, hbm
);
1097 FillRect(hdc
, &rc
, (HBRUSH
)(COLOR_MENU
+ 1));
1098 if (!DrawIconEx(hdc
, 0, 0, hIcon
, rc
.right
, rc
.bottom
, 0, NULL
, DI_NORMAL
))
1099 ERR("DrawIcon failed: %x\n", GetLastError());
1100 SelectObject(hdc
, hbmOld
);
1107 VOID
COpenWithMenu::AddChooseProgramItem()
1112 ZeroMemory(&mii
, sizeof(mii
));
1113 mii
.cbSize
= sizeof(mii
);
1114 mii
.fMask
= MIIM_TYPE
| MIIM_ID
;
1115 mii
.fType
= MFT_SEPARATOR
;
1117 InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
);
1119 if (!LoadStringW(shell32_hInstance
, IDS_OPEN_WITH_CHOOSE
, wszBuf
, _countof(wszBuf
)))
1121 ERR("Failed to load string\n");
1125 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
1126 mii
.fType
= MFT_STRING
;
1127 mii
.fState
= MFS_ENABLED
;
1128 mii
.wID
= m_idCmdLast
;
1129 mii
.dwTypeData
= (LPWSTR
)wszBuf
;
1130 mii
.cch
= wcslen(wszBuf
);
1132 InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
);
1135 VOID
COpenWithMenu::AddApp(PVOID pApp
)
1138 LPCWSTR pwszName
= m_pAppList
->GetName((COpenWithList::SApp
*)pApp
);
1140 ZeroMemory(&mii
, sizeof(mii
));
1141 mii
.cbSize
= sizeof(mii
);
1142 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
1143 mii
.fType
= MFT_STRING
;
1144 mii
.fState
= MFS_ENABLED
;
1145 mii
.wID
= m_idCmdLast
;
1146 mii
.dwTypeData
= (LPWSTR
)pwszName
;
1147 mii
.cch
= wcslen(mii
.dwTypeData
);
1148 mii
.dwItemData
= (ULONG_PTR
)pApp
;
1150 HICON hIcon
= m_pAppList
->GetIcon((COpenWithList::SApp
*)pApp
);
1153 mii
.fMask
|= MIIM_CHECKMARKS
;
1154 mii
.hbmpChecked
= mii
.hbmpUnchecked
= IconToBitmap(hIcon
);
1157 if (InsertMenuItemW(m_hSubMenu
, -1, TRUE
, &mii
))
1161 HRESULT WINAPI
COpenWithMenu::QueryContextMenu(
1168 TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
1170 INT DefaultPos
= GetMenuDefaultItem(hMenu
, TRUE
, 0);
1173 UINT NameId
= (DefaultPos
== -1 ? IDS_OPEN
: IDS_OPEN_WITH
);
1174 if (!LoadStringW(shell32_hInstance
, NameId
, wszName
, _countof(wszName
)))
1176 ERR("Failed to load string\n");
1180 /* Init first cmd id and submenu */
1181 m_idCmdFirst
= m_idCmdLast
= idCmdFirst
;
1184 /* If we are going to be default item, we shouldn't be submenu */
1185 if (DefaultPos
!= -1)
1187 /* Load applications list */
1189 m_pAppList
->LoadRecommended(m_wszPath
);
1191 /* Create submenu only if there is more than one application and menu has a default item */
1192 if (m_pAppList
->GetRecommendedCount() > 1)
1194 m_hSubMenu
= CreatePopupMenu();
1196 for(UINT i
= 0; i
< m_pAppList
->GetCount(); ++i
)
1198 COpenWithList::SApp
*pApp
= m_pAppList
->GetList() + i
;
1199 if (pApp
->bRecommended
)
1203 AddChooseProgramItem();
1207 /* Insert menu item */
1209 ZeroMemory(&mii
, sizeof(mii
));
1210 mii
.cbSize
= sizeof(mii
);
1211 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
1214 mii
.fMask
|= MIIM_SUBMENU
;
1215 mii
.hSubMenu
= m_hSubMenu
;
1219 mii
.wID
= m_idCmdLast
;
1221 mii
.fType
= MFT_STRING
;
1222 mii
.dwTypeData
= (LPWSTR
)wszName
;
1223 mii
.cch
= wcslen(wszName
);
1225 mii
.fState
= MFS_ENABLED
;
1226 if (DefaultPos
== -1)
1227 mii
.fState
|= MFS_DEFAULT
;
1229 if (!InsertMenuItemW(hMenu
, DefaultPos
+ 1, TRUE
, &mii
))
1232 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, m_idCmdLast
- m_idCmdFirst
+ 1);
1236 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
1238 HRESULT hr
= E_FAIL
;
1240 TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst
, m_idCmdLast
, m_idCmdFirst
+ LOWORD(lpici
->lpVerb
));
1242 if (HIWORD(lpici
->lpVerb
) == 0 && m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) <= m_idCmdLast
)
1244 if (m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) == m_idCmdLast
)
1247 LPCWSTR pwszExt
= PathFindExtensionW(m_wszPath
);
1249 info
.pcszFile
= m_wszPath
;
1250 info
.oaifInFlags
= OAIF_EXEC
;
1252 info
.oaifInFlags
|= OAIF_REGISTER_EXT
| OAIF_ALLOW_REGISTRATION
;
1253 info
.pcszClass
= NULL
;
1254 hr
= SHOpenWithDialog(lpici
->hwnd
, &info
);
1258 /* retrieve menu item info */
1260 ZeroMemory(&mii
, sizeof(mii
));
1261 mii
.cbSize
= sizeof(mii
);
1262 mii
.fMask
= MIIM_DATA
| MIIM_FTYPE
;
1264 if (GetMenuItemInfoW(m_hSubMenu
, LOWORD(lpici
->lpVerb
), TRUE
, &mii
) && mii
.dwItemData
)
1266 /* launch item with specified app */
1267 COpenWithList::SApp
*pApp
= (COpenWithList::SApp
*)mii
.dwItemData
;
1268 COpenWithList::Execute(pApp
, m_wszPath
);
1278 COpenWithMenu::GetCommandString(UINT_PTR idCmd
, UINT uType
,
1279 UINT
* pwReserved
, LPSTR pszName
, UINT cchMax
)
1281 FIXME("%p %lu %u %p %p %u\n", this,
1282 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
1287 HRESULT WINAPI
COpenWithMenu::HandleMenuMsg(
1292 TRACE("This %p uMsg %x\n", this, uMsg
);
1298 COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder
,
1299 IDataObject
*pdtobj
,
1306 LPCITEMIDLIST pidlFolder2
;
1307 LPCITEMIDLIST pidlChild
;
1311 TRACE("This %p\n", this);
1314 return E_INVALIDARG
;
1316 fmt
.cfFormat
= RegisterClipboardFormatW(CFSTR_SHELLIDLIST
);
1318 fmt
.dwAspect
= DVASPECT_CONTENT
;
1320 fmt
.tymed
= TYMED_HGLOBAL
;
1322 hr
= pdtobj
->GetData(&fmt
, &medium
);
1326 ERR("pdtobj->GetData failed with 0x%x\n", hr
);
1330 pida
= (LPIDA
)GlobalLock(medium
.hGlobal
);
1331 ASSERT(pida
->cidl
>= 1);
1333 pidlFolder2
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+ pida
->aoffset
[0]);
1334 pidlChild
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+ pida
->aoffset
[1]);
1336 pidl
= ILCombine(pidlFolder2
, pidlChild
);
1338 GlobalUnlock(medium
.hGlobal
);
1339 GlobalFree(medium
.hGlobal
);
1344 return E_OUTOFMEMORY
;
1346 if (!_ILIsValue(pidlChild
))
1348 TRACE("pidl is not a file\n");
1349 SHFree((void*)pidl
);
1353 if (!SHGetPathFromIDListW(pidl
, m_wszPath
))
1355 SHFree((void*)pidl
);
1356 ERR("SHGetPathFromIDListW failed\n");
1360 SHFree((void*)pidl
);
1361 TRACE("szPath %s\n", debugstr_w(m_wszPath
));
1363 pwszExt
= PathFindExtensionW(m_wszPath
);
1364 if (PathIsExeW(pwszExt
) || !_wcsicmp(pwszExt
, L
".lnk"))
1366 TRACE("file is a executable or shortcut\n");
1374 SHOpenWithDialog(HWND hwndParent
, const OPENASINFO
*poainfo
)
1378 COpenWithDialog
*pDialog
;
1380 TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent
, poainfo
);
1382 InitCommonControls();
1384 if (poainfo
->pcszClass
== NULL
&& poainfo
->pcszFile
== NULL
)
1387 pDialog
= new COpenWithDialog(poainfo
);
1389 return E_OUTOFMEMORY
;
1391 hwnd
= CreateDialogParam(shell32_hInstance
, MAKEINTRESOURCE(IDD_OPEN_WITH
), hwndParent
, COpenWithDialog::DialogProc
, (LPARAM
)pDialog
);
1394 ERR("Failed to create dialog\n");
1398 ShowWindow(hwnd
, SW_SHOWNORMAL
);
1400 while (GetMessage(&msg
, NULL
, 0, 0) && IsWindow(hwnd
))
1402 if (!IsDialogMessage(hwnd
, &msg
))
1404 TranslateMessage(&msg
);
1405 DispatchMessage(&msg
);