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