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