Reactos
1/*
2 * Open With Context Menu extension
3 *
4 * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
5 * Copyright 2009 Andrew Hill
6 * Copyright 2012 Rafal Harabien
7 *
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.
12 *
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.
17 *
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
21 */
22
23#include "precomp.h"
24
25WINE_DEFAULT_DEBUG_CHANNEL(shell);
26
27//
28// [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system]
29// "NoInternetOpenWith"=dword:00000001
30//
31
32EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath);
33
34static SIZE_T PathGetAppFromCommandLine(LPCWSTR pszIn, LPWSTR pszOut, SIZE_T cchMax)
35{
36 SIZE_T count = 0;
37 WCHAR stop = ' ';
38 if (pszIn[0] == '"')
39 stop = *(pszIn++);
40
41 for (LPCWSTR pwszSrc = pszIn; *pwszSrc && *pwszSrc != stop; ++pwszSrc)
42 {
43 if (++count >= cchMax)
44 return 0;
45 *(pszOut++) = *pwszSrc;
46 }
47 *pszOut = UNICODE_NULL;
48 return count;
49}
50
51HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax)
52{
53 WCHAR szDll[MAX_PATH + 100];
54 if (!PathGetAppFromCommandLine(pszCmd, szDll, _countof(szDll)))
55 return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
56
57 PWSTR pszName = PathFindFileNameW(szDll);
58 if (_wcsicmp(pszName, L"rundll32") && _wcsicmp(pszName, L"rundll32.exe"))
59 return E_UNEXPECTED;
60
61 PCWSTR pszDllStart = pszCmd + (pszName - szDll) + lstrlenW(pszName);
62
63 if (*pszDllStart == '\"')
64 ++pszDllStart; // Skip possible end quote of ..\rundll32.exe" foo.dll,func
65 while (*pszDllStart <= ' ' && *pszDllStart)
66 ++pszDllStart;
67 if (PathGetAppFromCommandLine(pszDllStart, szDll, _countof(szDll)))
68 {
69 BOOL quoted = *pszDllStart == '\"';
70 PWSTR pszComma = szDll + lstrlenW(szDll);
71 while (!quoted && pszComma > szDll && *pszComma != ',' && *pszComma != '\\' && *pszComma != '/')
72 --pszComma;
73 SIZE_T cch = pszComma - szDll;
74 if (cch <= cchMax && (quoted || *pszComma == ','))
75 {
76 *pszComma = UNICODE_NULL;
77 lstrcpynW(pszOut, szDll, cchMax);
78 return S_OK;
79 }
80 }
81 return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
82}
83
84static HRESULT SH32_EvaluateValidExecApp(_Inout_ PWSTR pszCmd, _In_ SIZE_T cchMax)
85{
86 // FIXME: SHEvaluateSystemCommandTemplate is not implemented yet, using a minimal version.
87 if (!PathGetAppFromCommandLine(pszCmd, pszCmd, cchMax))
88 return E_FAIL;
89
90 UINT fPRF = PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK;
91 WCHAR szCurrDir[MAX_PATH];
92 LPCWSTR pszDirsArr[2] = { szCurrDir, NULL }, *ppszDirs = NULL;
93 if (GetCurrentDirectoryW(_countof(szCurrDir), szCurrDir))
94 ppszDirs = pszDirsArr;
95 if (PathResolveW(pszCmd, ppszDirs, fPRF | (ppszDirs ? PRF_FIRSTDIRDEF : 0)))
96 return S_OK;
97 return E_FAIL;
98}
99
100HRESULT SH32_InvokeOpenWith(_In_ PCWSTR pszPath, _In_ LPCMINVOKECOMMANDINFO pici, _Out_ HANDLE *phProcess)
101{
102 if (!pszPath || !pici)
103 return HResultFromWin32(ERROR_INVALID_PARAMETER);
104
105 HRESULT hr = HResultFromWin32(ERROR_NO_ASSOCIATION);
106 SHELLEXECUTEINFOW sei = { sizeof(sei), CmicFlagsToSeeFlags(pici->fMask), pici->hwnd };
107 sei.fMask |= SEE_MASK_CLASSKEY | SEE_MASK_NOZONECHECKS;
108 sei.lpFile = pszPath;
109 sei.nShow = pici->nShow;
110 if (phProcess)
111 {
112 sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
113 sei.hProcess = NULL;
114 }
115
116 if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Unknown", 0, KEY_READ, &sei.hkeyClass))
117 {
118 // Use the internal dialog only if HKCR\Unknown\shell\openas\command exists but is invalid.
119 WCHAR szCmd[MAX_PATH * 2];
120 DWORD cch = _countof(szCmd);
121 hr = AssocQueryStringByKeyW(ASSOCF_NOTRUNCATE | ASSOCF_NOFIXUPS |
122 ASSOCF_IGNOREBASECLASS | ASSOCF_INIT_IGNOREUNKNOWN,
123 ASSOCSTR_COMMAND, sei.hkeyClass, NULL, szCmd, &cch);
124 if (SUCCEEDED(hr) && FAILED(SH32_EvaluateValidExecApp(szCmd, _countof(szCmd))))
125 {
126 OPENASINFO info = { pszPath, NULL, OAIF_EXEC | OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION };
127 hr = SHOpenWithDialog(sei.hwnd, &info);
128 }
129 else
130 {
131 hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError());
132 }
133 RegCloseKey(sei.hkeyClass);
134 }
135 else if (!(pici->fMask & CMIC_MASK_FLAG_NO_UI))
136 {
137 SHELL_ErrorBox(sei.hwnd, hr);
138 }
139
140 if (phProcess)
141 *phProcess = sei.hProcess;
142 return hr;
143}
144
145class COpenWithList
146{
147 public:
148 struct SApp
149 {
150 WCHAR wszFilename[MAX_PATH];
151 WCHAR wszCmd[MAX_PATH];
152 //WCHAR wszManufacturer[256];
153 WCHAR wszName[256];
154 BOOL bHidden;
155 BOOL bRecommended;
156 BOOL bMRUList;
157 HICON hIcon;
158 };
159
160 COpenWithList();
161 ~COpenWithList();
162
163 BOOL Load();
164 SApp *Add(LPCWSTR pwszPath);
165 static BOOL SaveApp(SApp *pApp);
166 SApp *Find(LPCWSTR pwszFilename);
167 static LPCWSTR GetName(SApp *pApp);
168 static HICON GetIcon(SApp *pApp);
169 static BOOL Execute(SApp *pApp, LPCWSTR pwszFilePath);
170 static BOOL IsHidden(SApp *pApp);
171 inline BOOL IsNoOpen(VOID) { return m_bNoOpen; }
172 BOOL LoadRecommended(LPCWSTR pwszFilePath);
173 BOOL SetDefaultHandler(SApp *pApp, LPCWSTR pwszFilename);
174
175 inline SApp *GetList() { return m_pApp; }
176 inline UINT GetCount() { return m_cApp; }
177 inline UINT GetRecommendedCount() { return m_cRecommended; }
178
179 private:
180 typedef struct _LANGANDCODEPAGE
181 {
182 WORD lang;
183 WORD code;
184 } LANGANDCODEPAGE, *LPLANGANDCODEPAGE;
185
186 SApp *m_pApp;
187 UINT m_cApp, m_cRecommended;
188 BOOL m_bNoOpen;
189
190 SApp *AddInternal(LPCWSTR pwszFilename);
191 static BOOL LoadInfo(SApp *pApp);
192 static BOOL GetPathFromCmd(LPWSTR pwszAppPath, LPCWSTR pwszCmd);
193 BOOL LoadProgIdList(HKEY hKey, LPCWSTR pwszExt);
194 static HANDLE OpenMRUList(HKEY hKey);
195 BOOL LoadMRUList(HKEY hKey);
196 BOOL LoadAppList(HKEY hKey);
197 VOID LoadFromProgIdKey(HKEY hKey, LPCWSTR pwszExt);
198 VOID LoadRecommendedFromHKCR(LPCWSTR pwszExt);
199 VOID LoadRecommendedFromHKCU(LPCWSTR pwszExt);
200 static BOOL AddAppToMRUList(SApp *pApp, LPCWSTR pwszFilename);
201
202 inline VOID SetRecommended(SApp *pApp)
203 {
204 if (!pApp->bRecommended)
205 ++m_cRecommended;
206 pApp->bRecommended = TRUE;
207 }
208};
209
210COpenWithList::COpenWithList():
211 m_pApp(NULL), m_cApp(0), m_cRecommended(0), m_bNoOpen(FALSE) {}
212
213COpenWithList::~COpenWithList()
214{
215 for (UINT i = 0; i < m_cApp; ++i)
216 if (m_pApp[i].hIcon)
217 DestroyIcon(m_pApp[i].hIcon);
218
219 HeapFree(GetProcessHeap(), 0, m_pApp);
220}
221
222BOOL COpenWithList::Load()
223{
224 HKEY hKey, hKeyApp;
225 WCHAR wszName[256], wszBuf[100];
226 DWORD i = 0, cchName, dwSize;
227 SApp *pApp;
228
229 if (RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Applications", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
230 {
231 ERR("RegOpenKeyEx HKCR\\Applications failed!\n");
232 return FALSE;
233 }
234
235 while (TRUE)
236 {
237 cchName = _countof(wszName);
238 if (RegEnumKeyEx(hKey, i++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
239 break;
240
241 pApp = AddInternal(wszName);
242
243 if (pApp)
244 {
245 if (RegOpenKeyW(hKey, wszName, &hKeyApp) == ERROR_SUCCESS)
246 {
247 if ((RegQueryValueExW(hKeyApp, L"NoOpenWith", NULL, NULL, NULL, NULL) != ERROR_SUCCESS) &&
248 (RegQueryValueExW(hKeyApp, L"NoStartPage", NULL, NULL, NULL, NULL) != ERROR_SUCCESS))
249 {
250 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s\\shell\\open\\command", wszName);
251 dwSize = sizeof(pApp->wszCmd);
252 if (RegGetValueW(hKey, wszBuf, L"", RRF_RT_REG_SZ, NULL, pApp->wszCmd, &dwSize) != ERROR_SUCCESS)
253 {
254 ERR("Failed to add app %ls\n", wszName);
255 pApp->bHidden = TRUE;
256 }
257 else
258 {
259 TRACE("App added %ls\n", pApp->wszCmd);
260 }
261 }
262 else
263 {
264 pApp->bHidden = TRUE;
265 }
266 RegCloseKey(hKeyApp);
267 }
268 else
269 {
270 pApp->bHidden = TRUE;
271 }
272 }
273 else
274 {
275 ERR("AddInternal failed\n");
276 }
277 }
278
279 RegCloseKey(hKey);
280 return TRUE;
281}
282
283COpenWithList::SApp *COpenWithList::Add(LPCWSTR pwszPath)
284{
285 SApp *pApp = AddInternal(PathFindFileNameW(pwszPath));
286
287 if (pApp)
288 {
289 StringCbPrintfW(pApp->wszCmd, sizeof(pApp->wszCmd), L"\"%s\" \"%%1\"", pwszPath);
290 SaveApp(pApp);
291 }
292
293 return pApp;
294}
295
296BOOL COpenWithList::SaveApp(SApp *pApp)
297{
298 WCHAR wszBuf[256];
299 HKEY hKey;
300
301 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s\\shell\\open\\command", pApp->wszFilename);
302 if (RegCreateKeyEx(HKEY_CLASSES_ROOT, wszBuf, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
303 {
304 ERR("RegOpenKeyEx failed\n");
305 return FALSE;
306 }
307
308 if (RegSetValueEx(hKey, L"", 0, REG_SZ, (PBYTE)pApp->wszCmd, (wcslen(pApp->wszCmd)+1)*sizeof(WCHAR)) != ERROR_SUCCESS)
309 ERR("Cannot add app to registry\n");
310
311 RegCloseKey(hKey);
312 return TRUE;
313}
314
315COpenWithList::SApp *COpenWithList::Find(LPCWSTR pwszFilename)
316{
317 for (UINT i = 0; i < m_cApp; ++i)
318 if (_wcsicmp(m_pApp[i].wszFilename, pwszFilename) == 0)
319 return &m_pApp[i];
320 return NULL;
321}
322
323LPCWSTR COpenWithList::GetName(SApp *pApp)
324{
325 if (!pApp->wszName[0])
326 {
327 if (!LoadInfo(pApp))
328 {
329 WARN("Failed to load %ls info\n", pApp->wszFilename);
330 StringCbCopyW(pApp->wszName, sizeof(pApp->wszName), pApp->wszFilename);
331
332 WCHAR wszPath[MAX_PATH];
333 if (!GetPathFromCmd(wszPath, pApp->wszCmd))
334 {
335 return NULL;
336 }
337 }
338 }
339
340 TRACE("%ls name: %ls\n", pApp->wszFilename, pApp->wszName);
341 return pApp->wszName;
342}
343
344HICON COpenWithList::GetIcon(SApp *pApp)
345{
346 if (!pApp->hIcon)
347 {
348 WCHAR wszPath[MAX_PATH];
349
350 GetPathFromCmd(wszPath, pApp->wszCmd);
351 if (!ExtractIconExW(wszPath, 0, NULL, &pApp->hIcon, 1))
352 {
353 SHFILEINFO fi;
354 /* FIXME: Ideally we should include SHGFI_USEFILEATTRIBUTES because we already
355 ** know the file has no icons but SHGetFileInfo is broken in that case (CORE-19122).
356 ** Without SHGFI_USEFILEATTRIBUTES we needlessly hit the disk again but it will
357 ** return the correct default .exe icon.
358 */
359 SHGetFileInfoW(wszPath, 0, &fi, sizeof(fi), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_SHELLICONSIZE);
360 pApp->hIcon = fi.hIcon;
361 }
362 }
363
364 TRACE("%ls icon: %p\n", pApp->wszFilename, pApp->hIcon);
365
366 return pApp->hIcon;
367}
368
369BOOL COpenWithList::Execute(COpenWithList::SApp *pApp, LPCWSTR pwszFilePath)
370{
371 WCHAR wszBuf[256];
372 HKEY hKey;
373
374 /* Add app to registry if it wasnt there before */
375 SaveApp(pApp);
376 if (!pApp->bMRUList)
377 AddAppToMRUList(pApp, pwszFilePath);
378
379 /* Get a handle to the reg key */
380 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s", pApp->wszFilename);
381 if (RegCreateKeyEx(HKEY_CLASSES_ROOT, wszBuf, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
382 {
383 ERR("RegOpenKeyEx failed\n");
384 return FALSE;
385 }
386
387 /* Let ShellExecuteExW do the work */
388 SHELLEXECUTEINFOW sei = {sizeof(SHELLEXECUTEINFOW), SEE_MASK_CLASSKEY};
389 sei.nShow = SW_SHOWNORMAL;
390 sei.hkeyClass = hKey;
391 sei.lpFile = pwszFilePath;
392
393 ShellExecuteExW(&sei);
394
395 return TRUE;
396}
397
398BOOL COpenWithList::IsHidden(SApp *pApp)
399{
400 WCHAR wszBuf[100];
401 DWORD dwSize = 0;
402
403 if (pApp->bHidden)
404 return pApp->bHidden;
405
406 if (FAILED(StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s", pApp->wszFilename)))
407 {
408 ERR("insufficient buffer\n");
409 return FALSE;
410 }
411
412 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"NoOpenWith", RRF_RT_REG_SZ, NULL, NULL, &dwSize) != ERROR_SUCCESS)
413 return FALSE;
414
415 pApp->bHidden = TRUE;
416 return TRUE;
417}
418
419COpenWithList::SApp *COpenWithList::AddInternal(LPCWSTR pwszFilename)
420{
421 /* Check for duplicate */
422 SApp *pApp = Find(pwszFilename);
423 if (pApp)
424 return pApp;
425
426 /* Create new item */
427 if (!m_pApp)
428 m_pApp = static_cast<SApp *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(m_pApp[0])));
429 else
430 m_pApp = static_cast<SApp *>(HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, m_pApp, (m_cApp + 1)*sizeof(m_pApp[0])));
431 if (!m_pApp)
432 {
433 ERR("Allocation failed\n");
434 return NULL;
435 }
436
437 pApp = &m_pApp[m_cApp++];
438 wcscpy(pApp->wszFilename, pwszFilename);
439 return pApp;
440}
441
442BOOL COpenWithList::LoadInfo(COpenWithList::SApp *pApp)
443{
444 UINT cbSize, cchLen;
445 LPVOID pBuf;
446 WORD wLang = 0, wCode = 0;
447 LPLANGANDCODEPAGE lpLangCode;
448 WCHAR wszBuf[100];
449 WCHAR *pResult;
450 WCHAR wszPath[MAX_PATH];
451 BOOL success = FALSE;
452
453 GetPathFromCmd(wszPath, pApp->wszCmd);
454 TRACE("LoadInfo %ls\n", wszPath);
455
456 /* query version info size */
457 cbSize = GetFileVersionInfoSizeW(wszPath, NULL);
458 if (!cbSize)
459 {
460 ERR("GetFileVersionInfoSizeW %ls failed: %lu\n", wszPath, GetLastError());
461 return FALSE;
462 }
463
464 /* allocate buffer */
465 pBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSize + 200);
466 if (!pBuf)
467 {
468 ERR("HeapAlloc failed\n");
469 return FALSE;
470 }
471
472 /* query version info */
473 if (!GetFileVersionInfoW(wszPath, 0, cbSize, pBuf))
474 {
475 ERR("GetFileVersionInfoW %ls failed: %lu\n", wszPath, GetLastError());
476 HeapFree(GetProcessHeap(), 0, pBuf);
477 return FALSE;
478 }
479
480 /* query lang code */
481 if (VerQueryValueW(pBuf, L"VarFileInfo\\Translation", (LPVOID*)&lpLangCode, &cbSize))
482 {
483 /* FIXME: find language from current locale / if not available,
484 * default to english
485 * for now default to first available language
486 */
487 wLang = lpLangCode->lang;
488 wCode = lpLangCode->code;
489 }
490
491 /* Query name */
492 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\FileDescription", wLang, wCode);
493 success = VerQueryValueW(pBuf, wszBuf, (LPVOID *)&pResult, &cchLen) && (cchLen > 1);
494 if (success)
495 StringCchCopyNW(pApp->wszName, _countof(pApp->wszName), pResult, cchLen);
496 else
497 ERR("Cannot get app name\n");
498
499 /* Query manufacturer */
500 /*swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\CompanyName", wLang, wCode);
501
502 if (VerQueryValueW(pBuf, wszBuf, (LPVOID *)&pResult, &cchLen))
503 StringCchCopyNW(pApp->wszManufacturer, _countof(pApp->wszManufacturer), pResult, cchLen);*/
504 HeapFree(GetProcessHeap(), 0, pBuf);
505 return success;
506}
507
508BOOL COpenWithList::GetPathFromCmd(LPWSTR pwszAppPath, LPCWSTR pwszCmd)
509{
510 WCHAR wszBuf[MAX_PATH];
511
512 /* Remove arguments */
513 if (!PathGetAppFromCommandLine(pwszCmd, wszBuf, _countof(wszBuf)))
514 return FALSE;
515
516 /* Replace rundll32.exe with the dll path */
517 SHELL32_GetDllFromRundll32CommandLine(pwszCmd, wszBuf, _countof(wszBuf));
518
519 /* Expand env. vars and optionally search for path */
520 ExpandEnvironmentStrings(wszBuf, pwszAppPath, MAX_PATH);
521 if (!PathFileExists(pwszAppPath))
522 {
523 UINT cch = SearchPathW(NULL, pwszAppPath, NULL, MAX_PATH, pwszAppPath, NULL);
524 if (!cch || cch >= MAX_PATH)
525 return FALSE;
526 }
527 return TRUE;
528}
529
530BOOL COpenWithList::LoadRecommended(LPCWSTR pwszFilePath)
531{
532 LPCWSTR pwszExt;
533
534 pwszExt = PathFindExtensionW(pwszFilePath);
535 if (!pwszExt[0])
536 return FALSE;
537
538 /* load programs directly associated from HKCU */
539 LoadRecommendedFromHKCU(pwszExt);
540
541 /* load programs associated from HKCR\Extension */
542 LoadRecommendedFromHKCR(pwszExt);
543
544 return TRUE;
545}
546
547BOOL COpenWithList::LoadProgIdList(HKEY hKey, LPCWSTR pwszExt)
548{
549 HKEY hSubkey, hSubkey2;
550 WCHAR wszProgId[256];
551 DWORD i = 0, cchProgId;
552
553 if (RegOpenKeyExW(hKey, L"OpenWithProgIDs", 0, KEY_READ, &hSubkey) != ERROR_SUCCESS)
554 return FALSE;
555
556 while (TRUE)
557 {
558 /* Enumerate values - value name is ProgId */
559 cchProgId = _countof(wszProgId);
560 if (RegEnumValue(hSubkey, i++, wszProgId, &cchProgId, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
561 break;
562
563 /* If ProgId exists load it */
564 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszProgId, 0, KEY_READ, &hSubkey2) == ERROR_SUCCESS)
565 {
566 LoadFromProgIdKey(hSubkey2, pwszExt);
567 RegCloseKey(hSubkey2);
568 }
569 }
570
571 RegCloseKey(hSubkey);
572 return TRUE;
573}
574
575HANDLE COpenWithList::OpenMRUList(HKEY hKey)
576{
577 MRUINFOW Info;
578
579 /* Initialize mru list info */
580 Info.cbSize = sizeof(Info);
581 Info.uMax = 32;
582 Info.fFlags = MRU_STRING;
583 Info.hKey = hKey;
584 Info.lpszSubKey = L"OpenWithList";
585 Info.lpfnCompare = NULL;
586
587 return CreateMRUListW(&Info);
588}
589
590BOOL COpenWithList::LoadMRUList(HKEY hKey)
591{
592 HANDLE hList;
593 int nItem, nCount, nResult;
594 WCHAR wszAppFilename[MAX_PATH];
595
596 /* Open MRU list */
597 hList = OpenMRUList(hKey);
598 if (!hList)
599 {
600 TRACE("OpenMRUList failed\n");
601 return FALSE;
602 }
603
604 /* Get list count */
605 nCount = EnumMRUListW(hList, -1, NULL, 0);
606
607 for(nItem = 0; nItem < nCount; nItem++)
608 {
609 nResult = EnumMRUListW(hList, nItem, wszAppFilename, _countof(wszAppFilename));
610 if (nResult <= 0)
611 continue;
612
613 /* Insert item */
614 SApp *pApp = Find(wszAppFilename);
615
616 TRACE("Recommended app %ls: %p\n", wszAppFilename, pApp);
617 if (pApp)
618 {
619 pApp->bMRUList = TRUE;
620 SetRecommended(pApp);
621 }
622 }
623
624 /* Free the MRU list */
625 FreeMRUList(hList);
626 return TRUE;
627}
628
629BOOL COpenWithList::LoadAppList(HKEY hKey)
630{
631 WCHAR wszAppFilename[MAX_PATH];
632 HKEY hSubkey;
633 DWORD i = 0, cchAppFilename;
634
635 if (RegOpenKeyExW(hKey, L"OpenWithList", 0, KEY_READ, &hSubkey) != ERROR_SUCCESS)
636 return FALSE;
637
638 while (TRUE)
639 {
640 /* Enum registry keys - each of them is app name */
641 cchAppFilename = _countof(wszAppFilename);
642 if (RegEnumKeyExW(hSubkey, i++, wszAppFilename, &cchAppFilename, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
643 break;
644
645 /* Set application as recommended */
646 SApp *pApp = Find(wszAppFilename);
647
648 TRACE("Recommended app %ls: %p\n", wszAppFilename, pApp);
649 if (pApp)
650 SetRecommended(pApp);
651 }
652
653 RegCloseKey(hSubkey);
654 return TRUE;
655}
656
657VOID COpenWithList::LoadFromProgIdKey(HKEY hKey, LPCWSTR pwszExt)
658{
659 WCHAR wszCmd[MAX_PATH], wszPath[MAX_PATH];
660 DWORD dwSize = 0;
661
662 /* Check if NoOpen value exists */
663 if (RegGetValueW(hKey, NULL, L"NoOpen", RRF_RT_REG_SZ, NULL, NULL, &dwSize) == ERROR_SUCCESS)
664 {
665 /* Display warning dialog */
666 m_bNoOpen = TRUE;
667 }
668
669 /* Check if there is a directly available execute key */
670 dwSize = sizeof(wszCmd);
671 if (RegGetValueW(hKey, L"shell\\open\\command", NULL, RRF_RT_REG_SZ, NULL, (PVOID)wszCmd, &dwSize) == ERROR_SUCCESS)
672 {
673 /* Erase extra arguments */
674 GetPathFromCmd(wszPath, wszCmd);
675
676 /* Add application */
677 SApp *pApp = AddInternal(PathFindFileNameW(wszPath));
678 TRACE("Add app %ls: %p\n", wszPath, pApp);
679
680 if (pApp)
681 {
682 StringCbCopyW(pApp->wszCmd, sizeof(pApp->wszCmd), wszCmd);
683 SetRecommended(pApp);
684 }
685 }
686}
687
688VOID COpenWithList::LoadRecommendedFromHKCR(LPCWSTR pwszExt)
689{
690 HKEY hKey, hSubkey;
691 WCHAR wszBuf[MAX_PATH], wszBuf2[MAX_PATH];
692 DWORD dwSize;
693
694 /* Check if extension exists */
695 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
696 {
697 /* Load items from SystemFileAssociations\Ext key */
698 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"SystemFileAssociations\\%s", pwszExt);
699 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
700 return;
701 }
702
703 /* Load programs referenced from HKCR\ProgId */
704 dwSize = sizeof(wszBuf);
705 if (RegGetValueW(hKey, NULL, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS &&
706 RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hSubkey) == ERROR_SUCCESS)
707 {
708 LoadFromProgIdKey(hSubkey, pwszExt);
709 RegCloseKey(hSubkey);
710 }
711 else
712 LoadFromProgIdKey(hKey, pwszExt);
713
714 /* Load items from HKCR\Ext\OpenWithList */
715 LoadAppList(hKey);
716
717 /* Load items from HKCR\Ext\OpenWithProgIDs */
718 if (RegOpenKeyExW(hKey, L"OpenWithProgIDs", 0, KEY_READ, &hSubkey) == ERROR_SUCCESS)
719 {
720 LoadProgIdList(hSubkey, pwszExt);
721 RegCloseKey(hSubkey);
722 }
723
724 /* Load additional items from referenced PerceivedType */
725 dwSize = sizeof(wszBuf);
726 if (RegGetValueW(hKey, NULL, L"PerceivedType", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS)
727 {
728 StringCbPrintfW(wszBuf2, sizeof(wszBuf2), L"SystemFileAssociations\\%s", wszBuf);
729 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf2, 0, KEY_READ | KEY_WRITE, &hSubkey) == ERROR_SUCCESS)
730 {
731 /* Load from OpenWithList key */
732 LoadAppList(hSubkey);
733 RegCloseKey(hSubkey);
734 }
735 }
736
737 /* Close the key */
738 RegCloseKey(hKey);
739}
740
741VOID COpenWithList::LoadRecommendedFromHKCU(LPCWSTR pwszExt)
742{
743 WCHAR wszBuf[MAX_PATH];
744 HKEY hKey;
745
746 StringCbPrintfW(wszBuf, sizeof(wszBuf),
747 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
748 pwszExt);
749 if (RegOpenKeyExW(HKEY_CURRENT_USER, wszBuf, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
750 {
751 /* Load MRU and ProgId lists */
752 LoadMRUList(hKey);
753 LoadProgIdList(hKey, pwszExt);
754
755 /* Handle "Application" value */
756 DWORD cbBuf = sizeof(wszBuf);
757 if (RegGetValueW(hKey, NULL, L"Application", RRF_RT_REG_SZ, NULL, wszBuf, &cbBuf) == ERROR_SUCCESS)
758 {
759 SApp *pApp = Find(wszBuf);
760 if (pApp)
761 SetRecommended(pApp);
762 }
763
764 /* Close the key */
765 RegCloseKey(hKey);
766 }
767}
768
769BOOL COpenWithList::AddAppToMRUList(SApp *pApp, LPCWSTR pwszFilename)
770{
771 WCHAR wszBuf[100];
772 LPCWSTR pwszExt;
773 HKEY hKey;
774 HANDLE hList;
775
776 /* Get file extension */
777 pwszExt = PathFindExtensionW(pwszFilename);
778 if (!pwszExt[0])
779 return FALSE;
780
781 /* Build registry key */
782 if (FAILED(StringCbPrintfW(wszBuf, sizeof(wszBuf),
783 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
784 pwszExt)))
785 {
786 ERR("insufficient buffer\n");
787 return FALSE;
788 }
789
790 /* Open base key for this file extension */
791 if (RegCreateKeyExW(HKEY_CURRENT_USER, wszBuf, 0, NULL, 0, KEY_WRITE | KEY_READ, NULL, &hKey, NULL) != ERROR_SUCCESS)
792 return FALSE;
793
794 /* Open MRU list */
795 hList = OpenMRUList(hKey);
796 if (hList)
797 {
798 /* Insert the entry */
799 AddMRUStringW(hList, pApp->wszFilename);
800
801 /* Set MRU presence */
802 pApp->bMRUList = TRUE;
803
804 /* Close MRU list */
805 FreeMRUList(hList);
806 }
807
808 RegCloseKey(hKey);
809 return TRUE;
810}
811
812BOOL COpenWithList::SetDefaultHandler(SApp *pApp, LPCWSTR pwszFilename)
813{
814 HKEY hKey, hSrcKey, hDestKey;
815 WCHAR wszBuf[256];
816
817 TRACE("SetDefaultHandler %ls %ls\n", pApp->wszFilename, pwszFilename);
818
819 /* Extract file extension */
820 LPCWSTR pwszExt = PathFindExtensionW(pwszFilename);
821 if (!pwszExt[0] || !pwszExt[1])
822 return FALSE;
823
824 /* Create file extension key */
825 if (RegCreateKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
826 {
827 ERR("Can't open ext key\n");
828 return FALSE;
829 }
830
831 DWORD dwSize = sizeof(wszBuf);
832 LONG lResult = RegGetValueW(hKey, NULL, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize);
833
834 if (lResult == ERROR_FILE_NOT_FOUND)
835 {
836 /* A new entry was created or the default key is not set: set the prog key id */
837 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s_auto_file", pwszExt + 1);
838 if (RegSetValueExW(hKey, L"", 0, REG_SZ, (const BYTE*)wszBuf, (wcslen(wszBuf) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
839 {
840 RegCloseKey(hKey);
841 ERR("RegSetValueExW failed\n");
842 return FALSE;
843 }
844 }
845 else if (lResult != ERROR_SUCCESS)
846 {
847 RegCloseKey(hKey);
848 ERR("RegGetValueExW failed: 0x%08x\n", lResult);
849 return FALSE;
850 }
851
852 /* Close file extension key */
853 RegCloseKey(hKey);
854
855 /* Create prog id key */
856 if (RegCreateKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
857 {
858 ERR("RegCreateKeyExW failed\n");
859 return FALSE;
860 }
861
862 /* Check if there already verbs existing for that app */
863 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s\\shell", pApp->wszFilename);
864 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hSrcKey) != ERROR_SUCCESS)
865 {
866 ERR("RegOpenKeyExW %ls failed\n", wszBuf);
867 RegCloseKey(hKey);
868 return FALSE;
869 }
870
871 /* Open destination key */
872 if (RegCreateKeyExW(hKey, L"shell", 0, NULL, 0, KEY_WRITE, NULL, &hDestKey, NULL) != ERROR_SUCCESS)
873 {
874 ERR("RegCreateKeyExW failed\n");
875 RegCloseKey(hSrcKey);
876 RegCloseKey(hKey);
877 return FALSE;
878 }
879
880 /* Create "DefaultIcon" key */
881 HKEY hDefIconKey;
882 if (RegCreateKeyExW(hKey, L"DefaultIcon", 0, NULL, 0, KEY_WRITE, NULL,
883 &hDefIconKey, NULL) == ERROR_SUCCESS)
884 {
885 RegSetString(hDefIconKey, NULL, pApp->wszFilename, REG_EXPAND_SZ);
886 RegCloseKey(hDefIconKey);
887 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, NULL, NULL);
888 }
889
890 /* Copy static verbs from Classes\Applications key */
891 /* FIXME: SHCopyKey does not copy the security attributes of the keys */
892 /* FIXME: Windows does not actually copy the verb keys */
893 /* FIXME: Should probably delete any existing DelegateExecute/DropTarget/DDE verb information first */
894 LSTATUS Result = SHCopyKeyW(hSrcKey, NULL, hDestKey, 0);
895#ifdef __REACTOS__
896 // FIXME: When OpenWith is used to set a new default on Windows, the FileExts key
897 // is changed to force this association. ROS does not support this. The best
898 // we can do is to try to set the verb we (incorrectly) copied as the new default.
899 HKEY hAppKey;
900 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s", pApp->wszFilename);
901 if (Result == ERROR_SUCCESS && !RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hAppKey))
902 {
903 if (HCR_GetDefaultVerbW(hAppKey, NULL, wszBuf, _countof(wszBuf)) && *wszBuf)
904 RegSetString(hDestKey, NULL, wszBuf, REG_SZ);
905 RegCloseKey(hAppKey);
906 }
907#endif // __REACTOS__
908 RegCloseKey(hDestKey);
909 RegCloseKey(hSrcKey);
910 RegCloseKey(hKey);
911
912 if (Result != ERROR_SUCCESS)
913 {
914 ERR("SHCopyKeyW failed\n");
915 return FALSE;
916 }
917
918 return TRUE;
919}
920
921class COpenWithDialog
922{
923 public:
924 COpenWithDialog(const OPENASINFO *pInfo, COpenWithList *pAppList);
925 ~COpenWithDialog();
926 static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
927 BOOL IsNoOpen(HWND hwnd);
928
929 private:
930 VOID Init(HWND hwnd);
931 VOID AddApp(COpenWithList::SApp *pApp, BOOL bSelected);
932 VOID Browse();
933 VOID Accept();
934 static INT_PTR CALLBACK NoOpenDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
935 COpenWithList::SApp *GetCurrentApp();
936
937 const OPENASINFO *m_pInfo;
938 COpenWithList *m_pAppList;
939 UINT m_InFlags;
940 BOOL m_bListAllocated;
941 HWND m_hDialog, m_hTreeView;
942 HTREEITEM m_hRecommend;
943 HTREEITEM m_hOther;
944 HIMAGELIST m_hImgList;
945 BOOL m_bNoOpen;
946};
947
948COpenWithDialog::COpenWithDialog(const OPENASINFO *pInfo, COpenWithList *pAppList = NULL):
949 m_pInfo(pInfo), m_pAppList(pAppList), m_hImgList(NULL), m_bNoOpen(FALSE)
950{
951 if (!m_pAppList)
952 {
953 m_pAppList = new COpenWithList;
954 m_bListAllocated = TRUE;
955 }
956 else
957 m_bListAllocated = FALSE;
958}
959
960COpenWithDialog::~COpenWithDialog()
961{
962 if (m_bListAllocated && m_pAppList)
963 delete m_pAppList;
964 if (m_hImgList)
965 ImageList_Destroy(m_hImgList);
966}
967
968INT_PTR CALLBACK COpenWithDialog::NoOpenDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
969{
970 switch(Message)
971 {
972 case WM_INITDIALOG:
973 {
974 return TRUE;
975 }
976 case WM_CLOSE:
977 EndDialog(hwnd, IDNO);
978 break;
979 case WM_COMMAND:
980 switch(LOWORD(wParam))
981 {
982 case IDYES:
983 EndDialog(hwnd, IDYES);
984 break;
985 case IDNO:
986 EndDialog(hwnd, IDNO);
987 break;
988 }
989 break;
990 default:
991 return FALSE;
992 }
993 return TRUE;
994}
995
996BOOL COpenWithDialog::IsNoOpen(HWND hwnd)
997{
998 /* Only do the actual check if the file type has the 'NoOpen' flag. */
999 if (m_bNoOpen)
1000 {
1001 int dReturnValue = DialogBox(shell32_hInstance, MAKEINTRESOURCE(IDD_NOOPEN), hwnd, NoOpenDlgProc);
1002
1003 if (dReturnValue == IDNO)
1004 return TRUE;
1005 else if (dReturnValue == -1)
1006 {
1007 ERR("IsNoOpen failed to load dialog box\n");
1008 return TRUE;
1009 }
1010 }
1011
1012 return FALSE;
1013}
1014
1015VOID COpenWithDialog::AddApp(COpenWithList::SApp *pApp, BOOL bSelected)
1016{
1017 LPCWSTR pwszName = m_pAppList->GetName(pApp);
1018 if (!pwszName) return;
1019 HICON hIcon = m_pAppList->GetIcon(pApp);
1020
1021 TRACE("AddApp Cmd %ls Name %ls\n", pApp->wszCmd, pwszName);
1022
1023 /* Add item to the list */
1024 TVINSERTSTRUCT tvins;
1025
1026 if (pApp->bRecommended)
1027 tvins.hParent = tvins.hInsertAfter = m_hRecommend;
1028 else
1029 tvins.hParent = tvins.hInsertAfter = m_hOther;
1030
1031 tvins.item.mask = TVIF_TEXT|TVIF_PARAM;
1032 tvins.item.pszText = const_cast<LPWSTR>(pwszName);
1033 tvins.item.lParam = (LPARAM)pApp;
1034
1035 if (hIcon)
1036 {
1037 tvins.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1038 tvins.item.iImage = tvins.item.iSelectedImage = ImageList_AddIcon(m_hImgList, hIcon);
1039 }
1040
1041 HTREEITEM hItem = TreeView_InsertItem(m_hTreeView, &tvins);
1042
1043 if (bSelected)
1044 TreeView_SelectItem(m_hTreeView, hItem);
1045}
1046
1047VOID COpenWithDialog::Browse()
1048{
1049 WCHAR wszTitle[64];
1050 WCHAR wszFilter[256];
1051 WCHAR wszPath[MAX_PATH];
1052 OPENFILENAMEW ofn;
1053
1054 /* Initialize OPENFILENAMEW structure */
1055 ZeroMemory(&ofn, sizeof(OPENFILENAMEW));
1056 ofn.lStructSize = sizeof(OPENFILENAMEW);
1057 ofn.hInstance = shell32_hInstance;
1058 ofn.hwndOwner = m_hDialog;
1059 ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
1060 ofn.nMaxFile = (sizeof(wszPath) / sizeof(WCHAR));
1061 ofn.lpstrFile = wszPath;
1062 ofn.lpstrInitialDir = L"%programfiles%";
1063
1064 /* Init title */
1065 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH, wszTitle, sizeof(wszTitle) / sizeof(WCHAR)))
1066 {
1067 ofn.lpstrTitle = wszTitle;
1068 ofn.nMaxFileTitle = wcslen(wszTitle);
1069 }
1070
1071 /* Init the filter string */
1072 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH_FILTER, wszFilter, sizeof(wszFilter) / sizeof(WCHAR)))
1073 ofn.lpstrFilter = wszFilter;
1074 ZeroMemory(wszPath, sizeof(wszPath));
1075
1076 /* Create OpenFile dialog */
1077 if (!GetOpenFileNameW(&ofn))
1078 return;
1079
1080 /* Setup context for insert proc */
1081 COpenWithList::SApp *pApp = m_pAppList->Add(wszPath);
1082 AddApp(pApp, TRUE);
1083}
1084
1085COpenWithList::SApp *COpenWithDialog::GetCurrentApp()
1086{
1087 TVITEM tvi;
1088 tvi.hItem = TreeView_GetSelection(m_hTreeView);
1089 if (!tvi.hItem)
1090 return NULL;
1091
1092 tvi.mask = TVIF_PARAM;
1093 if (!TreeView_GetItem(m_hTreeView, &tvi))
1094 return NULL;
1095
1096 return (COpenWithList::SApp*)tvi.lParam;
1097}
1098
1099VOID COpenWithDialog::Init(HWND hwnd)
1100{
1101 TRACE("COpenWithDialog::Init hwnd %p\n", hwnd);
1102
1103 m_hDialog = hwnd;
1104 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)this);
1105
1106 UINT fDisallow = 0;
1107 PCWSTR pszExt = PathFindExtensionW(m_pInfo->pcszFile);
1108 // Don't allow registration for "" nor "." nor ".exe" etc.
1109 if (!pszExt || !pszExt[0] || !pszExt[1] || PathIsExeW(m_pInfo->pcszFile))
1110 fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION;
1111 if (SHRestricted(REST_NOFILEASSOCIATE))
1112 fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
1113
1114 /* Handle register checkbox */
1115 m_InFlags = m_pInfo->oaifInFlags & ~fDisallow;
1116 HWND hRegisterCheckbox = GetDlgItem(hwnd, 14003);
1117 if (!(m_InFlags & OAIF_ALLOW_REGISTRATION))
1118 EnableWindow(hRegisterCheckbox, FALSE);
1119 if (m_InFlags & OAIF_FORCE_REGISTRATION)
1120 SendMessage(hRegisterCheckbox, BM_SETCHECK, BST_CHECKED, 0);
1121 if (m_InFlags & OAIF_HIDE_REGISTRATION)
1122 ShowWindow(hRegisterCheckbox, SW_HIDE);
1123
1124 if (m_pInfo->pcszFile)
1125 {
1126 WCHAR wszBuf[MAX_PATH];
1127 UINT cchBuf;
1128
1129 /* Add filename to label */
1130 cchBuf = GetDlgItemTextW(hwnd, 14001, wszBuf, _countof(wszBuf));
1131 StringCchCopyW(wszBuf + cchBuf, _countof(wszBuf) - cchBuf, PathFindFileNameW(m_pInfo->pcszFile));
1132 SetDlgItemTextW(hwnd, 14001, wszBuf);
1133
1134 /* Load applications from registry */
1135 m_pAppList->Load();
1136 m_pAppList->LoadRecommended(m_pInfo->pcszFile);
1137
1138 /* Determine if the type of file can be opened directly from the shell */
1139 if (m_pAppList->IsNoOpen() != FALSE)
1140 m_bNoOpen = TRUE;
1141
1142 /* Init treeview */
1143 m_hTreeView = GetDlgItem(hwnd, 14002);
1144 m_hImgList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, m_pAppList->GetCount() + 1, m_pAppList->GetCount() + 1);
1145 (void)TreeView_SetImageList(m_hTreeView, m_hImgList, TVSIL_NORMAL);
1146
1147 /* If there are some recommendations add parent nodes: Recommended and Others */
1148 UINT cRecommended = m_pAppList->GetRecommendedCount();
1149 if (cRecommended > 0)
1150 {
1151 TVINSERTSTRUCT tvins;
1152 HICON hFolderIcon;
1153
1154 tvins.hParent = tvins.hInsertAfter = TVI_ROOT;
1155 tvins.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
1156 tvins.item.pszText = (LPWSTR)wszBuf;
1157 tvins.item.state = tvins.item.stateMask = TVIS_EXPANDED;
1158 hFolderIcon = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_PROGRAMS_FOLDER), IMAGE_ICON, 0, 0, 0);
1159 tvins.item.iImage = tvins.item.iSelectedImage = ImageList_AddIcon(m_hImgList, hFolderIcon);
1160
1161 LoadStringW(shell32_hInstance, IDS_OPEN_WITH_RECOMMENDED, wszBuf, _countof(wszBuf));
1162 m_hRecommend = TreeView_InsertItem(m_hTreeView, &tvins);
1163
1164 LoadStringW(shell32_hInstance, IDS_OPEN_WITH_OTHER, wszBuf, _countof(wszBuf));
1165 m_hOther = TreeView_InsertItem(m_hTreeView, &tvins);
1166 }
1167 else
1168 m_hRecommend = m_hOther = TVI_ROOT;
1169
1170 /* Add all applications */
1171 BOOL bNoAppSelected = TRUE;
1172 COpenWithList::SApp *pAppList = m_pAppList->GetList();
1173 for (UINT i = 0; i < m_pAppList->GetCount(); ++i)
1174 {
1175 if (!COpenWithList::IsHidden(&pAppList[i]))
1176 {
1177 if (bNoAppSelected && (pAppList[i].bRecommended || !cRecommended))
1178 {
1179 AddApp(&pAppList[i], TRUE);
1180 bNoAppSelected = FALSE;
1181 }
1182 else
1183 AddApp(&pAppList[i], FALSE);
1184 }
1185 }
1186 }
1187}
1188
1189VOID COpenWithDialog::Accept()
1190{
1191 COpenWithList::SApp *pApp = GetCurrentApp();
1192 if (pApp)
1193 {
1194 /* Set programm as default handler */
1195 if (IsDlgButtonChecked(m_hDialog, 14003) == BST_CHECKED && (m_InFlags & OAIF_REGISTER_EXT))
1196 m_pAppList->SetDefaultHandler(pApp, m_pInfo->pcszFile);
1197
1198 /* Execute program */
1199 if (m_InFlags & OAIF_EXEC)
1200 m_pAppList->Execute(pApp, m_pInfo->pcszFile);
1201
1202 EndDialog(m_hDialog, 1);
1203 }
1204}
1205
1206INT_PTR CALLBACK COpenWithDialog::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1207{
1208 COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
1209
1210 switch(uMsg)
1211 {
1212 case WM_INITDIALOG:
1213 {
1214 COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(lParam);
1215
1216 pThis->Init(hwndDlg);
1217 return TRUE;
1218 }
1219 case WM_COMMAND:
1220 switch(LOWORD(wParam))
1221 {
1222 case 14004: /* browse */
1223 {
1224 pThis->Browse();
1225 return TRUE;
1226 }
1227 case IDOK: /* ok */
1228 {
1229 pThis->Accept();
1230 return TRUE;
1231 }
1232 case IDCANCEL: /* cancel */
1233 EndDialog(hwndDlg, 0);
1234 return TRUE;
1235 default:
1236 break;
1237 }
1238 break;
1239 case WM_NOTIFY:
1240 switch (((LPNMHDR)lParam)->code)
1241 {
1242 case TVN_SELCHANGED:
1243 EnableWindow(GetDlgItem(hwndDlg, IDOK), pThis->GetCurrentApp() ? TRUE : FALSE);
1244 break;
1245 case NM_DBLCLK:
1246 case NM_RETURN:
1247 pThis->Accept();
1248 break;
1249 }
1250 break;
1251 case WM_CLOSE:
1252 EndDialog(hwndDlg, 0);
1253 return TRUE;
1254 default:
1255 break;
1256 }
1257 return FALSE;
1258}
1259
1260COpenWithMenu::COpenWithMenu()
1261{
1262 m_idCmdFirst = 0;
1263 m_idCmdLast = 0;
1264 m_pAppList = new COpenWithList;
1265}
1266
1267COpenWithMenu::~COpenWithMenu()
1268{
1269 TRACE("Destroying COpenWithMenu(%p)\n", this);
1270
1271 if (m_hSubMenu)
1272 {
1273 INT Count, Index;
1274 MENUITEMINFOW mii;
1275
1276 /* get item count */
1277 Count = GetMenuItemCount(m_hSubMenu);
1278 if (Count == -1)
1279 return;
1280
1281 /* setup menuitem info */
1282 ZeroMemory(&mii, sizeof(mii));
1283 mii.cbSize = sizeof(mii);
1284 mii.fMask = MIIM_DATA | MIIM_FTYPE | MIIM_CHECKMARKS;
1285
1286 for(Index = 0; Index < Count; Index++)
1287 {
1288 if (GetMenuItemInfoW(m_hSubMenu, Index, TRUE, &mii))
1289 {
1290 if (mii.hbmpChecked)
1291 DeleteObject(mii.hbmpChecked);
1292 }
1293 }
1294 }
1295
1296 if (m_pAppList)
1297 delete m_pAppList;
1298}
1299
1300HBITMAP COpenWithMenu::IconToBitmap(HICON hIcon)
1301{
1302 HDC hdc, hdcScr;
1303 HBITMAP hbm, hbmOld;
1304 RECT rc;
1305
1306 hdcScr = GetDC(NULL);
1307 hdc = CreateCompatibleDC(hdcScr);
1308 SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
1309 hbm = CreateCompatibleBitmap(hdcScr, rc.right, rc.bottom);
1310 ReleaseDC(NULL, hdcScr);
1311
1312 hbmOld = (HBITMAP)SelectObject(hdc, hbm);
1313 FillRect(hdc, &rc, (HBRUSH)(COLOR_MENU + 1));
1314 if (!DrawIconEx(hdc, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
1315 ERR("DrawIcon failed: %x\n", GetLastError());
1316 SelectObject(hdc, hbmOld);
1317
1318 DeleteDC(hdc);
1319
1320 return hbm;
1321}
1322
1323VOID COpenWithMenu::AddChooseProgramItem()
1324{
1325 MENUITEMINFOW mii;
1326 WCHAR wszBuf[128];
1327
1328 ZeroMemory(&mii, sizeof(mii));
1329 mii.cbSize = sizeof(mii);
1330 mii.fMask = MIIM_TYPE | MIIM_ID;
1331 mii.fType = MFT_SEPARATOR;
1332 mii.wID = -1;
1333 InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1334
1335 if (!LoadStringW(shell32_hInstance, IDS_OPEN_WITH_CHOOSE, wszBuf, _countof(wszBuf)))
1336 {
1337 ERR("Failed to load string\n");
1338 return;
1339 }
1340
1341 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1342 mii.fType = MFT_STRING;
1343 mii.fState = MFS_ENABLED;
1344 mii.wID = m_idCmdLast;
1345 mii.dwTypeData = (LPWSTR)wszBuf;
1346 mii.cch = wcslen(wszBuf);
1347
1348 InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1349}
1350
1351VOID COpenWithMenu::AddApp(PVOID pApp)
1352{
1353 MENUITEMINFOW mii;
1354 LPCWSTR pwszName = m_pAppList->GetName((COpenWithList::SApp*)pApp);
1355 if (!pwszName) return;
1356
1357 ZeroMemory(&mii, sizeof(mii));
1358 mii.cbSize = sizeof(mii);
1359 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
1360 mii.fType = MFT_STRING;
1361 mii.fState = MFS_ENABLED;
1362 mii.wID = m_idCmdLast;
1363 mii.dwTypeData = const_cast<LPWSTR>(pwszName);
1364 mii.dwItemData = (ULONG_PTR)pApp;
1365
1366 HICON hIcon = m_pAppList->GetIcon((COpenWithList::SApp*)pApp);
1367 if (hIcon)
1368 {
1369 mii.fMask |= MIIM_CHECKMARKS;
1370 mii.hbmpChecked = mii.hbmpUnchecked = IconToBitmap(hIcon);
1371 }
1372
1373 if (InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii))
1374 m_idCmdLast++;
1375}
1376
1377static const CMVERBMAP g_VerbMap[] =
1378{
1379 { "openas", 0 },
1380 { NULL }
1381};
1382
1383HRESULT WINAPI COpenWithMenu::QueryContextMenu(
1384 HMENU hMenu,
1385 UINT indexMenu,
1386 UINT idCmdFirst,
1387 UINT idCmdLast,
1388 UINT uFlags)
1389{
1390 TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
1391
1392 INT DefaultPos = GetMenuDefaultItem(hMenu, TRUE, 0);
1393
1394 WCHAR wszName[100];
1395 UINT NameId = (DefaultPos == -1 ? IDS_OPEN : IDS_OPEN_WITH);
1396 if (!LoadStringW(shell32_hInstance, NameId, wszName, _countof(wszName)))
1397 {
1398 ERR("Failed to load string\n");
1399 return E_FAIL;
1400 }
1401
1402 /* Init first cmd id and submenu */
1403 m_idCmdFirst = m_idCmdLast = idCmdFirst;
1404 m_hSubMenu = NULL;
1405
1406 /* We can only be a submenu if we are not the default */
1407 if (DefaultPos != -1)
1408 {
1409 /* Load applications list */
1410 m_pAppList->Load();
1411 m_pAppList->LoadRecommended(m_wszPath);
1412
1413 /* Create submenu only if there is more than one application and menu has a default item */
1414 if (m_pAppList->GetRecommendedCount() > 1)
1415 {
1416 m_hSubMenu = CreatePopupMenu();
1417
1418 for(UINT i = 0; i < m_pAppList->GetCount(); ++i)
1419 {
1420 COpenWithList::SApp *pApp = m_pAppList->GetList() + i;
1421 if (pApp->bRecommended)
1422 AddApp(pApp);
1423 }
1424
1425 AddChooseProgramItem();
1426 }
1427 }
1428
1429 /* Insert menu item */
1430 MENUITEMINFOW mii;
1431 ZeroMemory(&mii, sizeof(mii));
1432 mii.cbSize = sizeof(mii);
1433 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1434 if (m_hSubMenu)
1435 {
1436 mii.fMask |= MIIM_SUBMENU;
1437 mii.hSubMenu = m_hSubMenu;
1438 mii.wID = -1;
1439 }
1440 else
1441 mii.wID = m_idCmdLast;
1442
1443 mii.fType = MFT_STRING;
1444 mii.dwTypeData = (LPWSTR)wszName;
1445 mii.fState = MFS_ENABLED;
1446 if (DefaultPos == -1)
1447 {
1448 mii.fState |= MFS_DEFAULT;
1449 indexMenu = 0;
1450 }
1451
1452 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
1453 return E_FAIL;
1454
1455 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, m_idCmdLast - m_idCmdFirst + 1);
1456}
1457
1458HRESULT WINAPI
1459COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1460{
1461 const SIZE_T idChooseApp = m_idCmdLast;
1462 HRESULT hr = E_FAIL;
1463
1464 TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst, m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb));
1465
1466 if (!IS_INTRESOURCE(lpici->lpVerb) && SHELL_MapContextMenuVerbToCmdId(lpici, g_VerbMap) == 0)
1467 goto DoChooseApp;
1468
1469 if (IS_INTRESOURCE(lpici->lpVerb) && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast)
1470 {
1471 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp)
1472 {
1473DoChooseApp:
1474 hr = SH32_InvokeOpenWith(m_wszPath, lpici, NULL);
1475 }
1476 else
1477 {
1478 /* retrieve menu item info */
1479 MENUITEMINFOW mii;
1480 ZeroMemory(&mii, sizeof(mii));
1481 mii.cbSize = sizeof(mii);
1482 mii.fMask = MIIM_DATA | MIIM_FTYPE;
1483
1484 if (GetMenuItemInfoW(m_hSubMenu, LOWORD(lpici->lpVerb), TRUE, &mii) && mii.dwItemData)
1485 {
1486 /* launch item with specified app */
1487 COpenWithList::SApp *pApp = (COpenWithList::SApp*)mii.dwItemData;
1488 COpenWithList::Execute(pApp, m_wszPath);
1489 hr = S_OK;
1490 }
1491 }
1492 }
1493
1494 return hr;
1495}
1496
1497HRESULT WINAPI
1498COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
1499 UINT* pwReserved, LPSTR pszName, UINT cchMax )
1500{
1501 TRACE("%p %lu %u %p %p %u\n", this,
1502 idCmd, uType, pwReserved, pszName, cchMax );
1503
1504 const SIZE_T idChooseApp = m_idCmdLast;
1505 if (m_idCmdFirst + idCmd == idChooseApp)
1506 return SHELL_GetCommandStringImpl(0, uType, pszName, cchMax, g_VerbMap);
1507
1508 return E_NOTIMPL;
1509}
1510
1511HRESULT WINAPI COpenWithMenu::HandleMenuMsg(
1512 UINT uMsg,
1513 WPARAM wParam,
1514 LPARAM lParam)
1515{
1516 TRACE("This %p uMsg %x\n", this, uMsg);
1517
1518 return E_NOTIMPL;
1519}
1520
1521HRESULT WINAPI
1522COpenWithMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
1523 IDataObject *pdtobj,
1524 HKEY hkeyProgID)
1525{
1526 LPCITEMIDLIST pidlFolder2;
1527 LPCITEMIDLIST pidlChild;
1528
1529 TRACE("This %p\n", this);
1530
1531 if (pdtobj == NULL)
1532 return E_INVALIDARG;
1533
1534 CDataObjectHIDA pida(pdtobj);
1535 if (FAILED(pida.hr()))
1536 {
1537 ERR("pdtobj->GetData failed with 0x%x\n", pida.hr());
1538 return pida.hr();
1539 }
1540
1541 ASSERT(pida->cidl >= 1);
1542
1543 pidlFolder2 = HIDA_GetPIDLFolder(pida);
1544 pidlChild = HIDA_GetPIDLItem(pida, 0);
1545
1546 if (!_ILIsValue(pidlChild))
1547 {
1548 TRACE("pidl is not a file\n");
1549 return E_FAIL;
1550 }
1551
1552 CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlFolder2, pidlChild));
1553 if (!pidl)
1554 {
1555 ERR("no mem\n");
1556 return E_OUTOFMEMORY;
1557 }
1558
1559 if (!SHGetPathFromIDListW(pidl, m_wszPath))
1560 {
1561 ERR("SHGetPathFromIDListW failed\n");
1562 return E_FAIL;
1563 }
1564
1565 TRACE("szPath %s\n", debugstr_w(m_wszPath));
1566
1567 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
1568 if (PathIsExeW(pwszExt) || !_wcsicmp(pwszExt, L".lnk"))
1569 {
1570 TRACE("file is a executable or shortcut\n");
1571 return E_FAIL;
1572 }
1573
1574 return S_OK;
1575}
1576
1577HRESULT WINAPI
1578SHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
1579{
1580 INT_PTR ret;
1581
1582 TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent, poainfo);
1583
1584 InitCommonControls();
1585
1586 if (poainfo->pcszClass == NULL && poainfo->pcszFile == NULL)
1587 return E_FAIL;
1588
1589 COpenWithDialog pDialog(poainfo);
1590
1591 if (pDialog.IsNoOpen(hwndParent))
1592 return S_OK;
1593
1594 ret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCE(IDD_OPEN_WITH), hwndParent,
1595 COpenWithDialog::DialogProc, (LPARAM)&pDialog);
1596
1597 if (ret == (INT_PTR)-1)
1598 {
1599 ERR("Failed to create dialog: %u\n", GetLastError());
1600 return E_FAIL;
1601 }
1602
1603 return S_OK;
1604}