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