xref: /reactos/dll/win32/shell32/COpenWithMenu.cpp (revision f986527d)
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     CREATEMRULISTW Info;
431 
432     /* Initialize mru list info */
433     Info.cbSize = sizeof(Info);
434     Info.nMaxItems = 32;
435     Info.dwFlags = 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             if (!pApp->bMRUList)
538                 AddAppToMRUList(pApp, pwszExt);
539         }
540     }
541 }
542 
543 VOID COpenWithList::LoadRecommendedFromHKCR(LPCWSTR pwszExt)
544 {
545     HKEY hKey, hSubkey;
546     WCHAR wszBuf[MAX_PATH], wszBuf2[MAX_PATH];
547     DWORD dwSize;
548 
549     /* Check if extension exists */
550     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
551     {
552         /* Load items from SystemFileAssociations\Ext key */
553         StringCbPrintfW(wszBuf, sizeof(wszBuf), L"SystemFileAssociations\\%s", pwszExt);
554         if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
555             return;
556     }
557 
558     /* Load programs referenced from HKCR\ProgId */
559     dwSize = sizeof(wszBuf);
560     if (RegGetValueW(hKey, NULL, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS &&
561         RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hSubkey) == ERROR_SUCCESS)
562     {
563         LoadFromProgIdKey(hSubkey, pwszExt);
564         RegCloseKey(hSubkey);
565     }
566     else
567         LoadFromProgIdKey(hKey, pwszExt);
568 
569     /* Load items from HKCR\Ext\OpenWithList */
570     LoadAppList(hKey);
571 
572     /* Load items from HKCR\Ext\OpenWithProgIDs */
573     if (RegOpenKeyExW(hKey, L"OpenWithProgIDs", 0, KEY_READ, &hSubkey) == ERROR_SUCCESS)
574     {
575         LoadProgIdList(hSubkey, pwszExt);
576         RegCloseKey(hSubkey);
577     }
578 
579     /* Load additional items from referenced PerceivedType */
580     dwSize = sizeof(wszBuf);
581     if (RegGetValueW(hKey, NULL, L"PerceivedType", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS)
582     {
583         StringCbPrintfW(wszBuf2, sizeof(wszBuf2), L"SystemFileAssociations\\%s", wszBuf);
584         if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf2, 0, KEY_READ | KEY_WRITE, &hSubkey) == ERROR_SUCCESS)
585         {
586             /* Load from OpenWithList key */
587             LoadAppList(hSubkey);
588             RegCloseKey(hSubkey);
589         }
590     }
591 
592     /* Close the key */
593     RegCloseKey(hKey);
594 }
595 
596 VOID COpenWithList::LoadRecommendedFromHKCU(LPCWSTR pwszExt)
597 {
598     WCHAR wszBuf[MAX_PATH];
599     HKEY hKey;
600 
601     StringCbPrintfW(wszBuf, sizeof(wszBuf),
602                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
603                     pwszExt);
604     if (RegOpenKeyExW(HKEY_CURRENT_USER, wszBuf, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
605     {
606         /* Load MRU and ProgId lists */
607         LoadMRUList(hKey);
608         LoadProgIdList(hKey, pwszExt);
609 
610         /* Handle "Aplication" value */
611         DWORD cbBuf = sizeof(wszBuf);
612         if (RegGetValueW(hKey, NULL, L"Application", RRF_RT_REG_SZ, NULL, wszBuf, &cbBuf) == ERROR_SUCCESS)
613         {
614             SApp *pApp = Find(wszBuf);
615             if (pApp)
616                 SetRecommended(pApp);
617         }
618 
619         /* Close the key */
620         RegCloseKey(hKey);
621     }
622 }
623 
624 BOOL COpenWithList::AddAppToMRUList(SApp *pApp, LPCWSTR pwszFilename)
625 {
626     WCHAR wszBuf[100];
627     LPCWSTR pwszExt;
628     HKEY hKey;
629     HANDLE hList;
630 
631     /* Get file extension */
632     pwszExt = PathFindExtensionW(pwszFilename);
633     if (!pwszExt[0])
634         return FALSE;
635 
636     /* Build registry key */
637     if (FAILED(StringCbPrintfW(wszBuf, sizeof(wszBuf),
638                                L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s",
639                                pwszExt)))
640     {
641         ERR("insufficient buffer\n");
642         return FALSE;
643     }
644 
645     /* Open base key for this file extension */
646     if (RegCreateKeyExW(HKEY_CURRENT_USER, wszBuf, 0, NULL, 0, KEY_WRITE | KEY_READ, NULL, &hKey, NULL) != ERROR_SUCCESS)
647         return FALSE;
648 
649     /* Open MRU list */
650     hList = OpenMRUList(hKey);
651     if (hList)
652     {
653         /* Insert the entry */
654         AddMRUStringW(hList, pApp->wszFilename);
655 
656         /* Close MRU list */
657         FreeMRUList(hList);
658     }
659 
660     RegCloseKey(hKey);
661     return TRUE;
662 }
663 
664 BOOL COpenWithList::SetDefaultHandler(SApp *pApp, LPCWSTR pwszFilename)
665 {
666     HKEY hKey, hSrcKey, hDestKey;
667     WCHAR wszBuf[256];
668 
669     TRACE("SetDefaultHandler %ls %ls\n", pApp->wszFilename, pwszFilename);
670 
671     /* Extract file extension */
672     LPCWSTR pwszExt = PathFindExtensionW(pwszFilename);
673     if (!pwszExt[0] || !pwszExt[1])
674         return FALSE;
675 
676     /* Create file extension key */
677     if (RegCreateKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
678     {
679         ERR("Cannot open ext key");
680         return FALSE;
681     }
682 
683     DWORD dwSize = sizeof(wszBuf);
684     LONG lResult = RegGetValueW(hKey, NULL, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize);
685 
686     if (lResult == ERROR_FILE_NOT_FOUND)
687     {
688         /* A new entry was created or the default key is not set: set the prog key id */
689         StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s_auto_file", pwszExt + 1);
690         if (RegSetValueExW(hKey, L"", 0, REG_SZ, (const BYTE*)wszBuf, (wcslen(wszBuf) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
691         {
692             RegCloseKey(hKey);
693             ERR("RegSetValueExW failed\n");
694             return FALSE;
695         }
696     }
697     else if (lResult != ERROR_SUCCESS)
698     {
699         RegCloseKey(hKey);
700         ERR("RegGetValueExW failed: 0x%08x\n", lResult);
701         return FALSE;
702     }
703 
704     /* Close file extension key */
705     RegCloseKey(hKey);
706 
707     /* Create prog id key */
708     if (RegCreateKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
709     {
710         ERR("RegCreateKeyExW failed\n");
711         return FALSE;
712     }
713 
714     /* Check if there already verbs existing for that app */
715     StringCbPrintfW(wszBuf, sizeof(wszBuf), L"Applications\\%s\\shell", pApp->wszFilename);
716     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hSrcKey) != ERROR_SUCCESS)
717     {
718         ERR("RegOpenKeyExW %ls failed\n", wszBuf);
719         RegCloseKey(hKey);
720         return FALSE;
721     }
722 
723     /* Open destination key */
724     if (RegCreateKeyExW(hKey, L"shell", 0, NULL, 0, KEY_WRITE, NULL, &hDestKey, NULL) != ERROR_SUCCESS)
725     {
726         ERR("RegCreateKeyExW failed\n");
727         RegCloseKey(hSrcKey);
728         RegCloseKey(hKey);
729         return FALSE;
730     }
731 
732     /* Copy static verbs from Classes\Applications key */
733     /* FIXME: SHCopyKey does not copy the security attributes of the keys */
734     LSTATUS Result = SHCopyKeyW(hSrcKey, NULL, hDestKey, 0);
735     RegCloseKey(hDestKey);
736     RegCloseKey(hSrcKey);
737     RegCloseKey(hKey);
738 
739     if (Result != ERROR_SUCCESS)
740     {
741         ERR("SHCopyKeyW failed\n");
742         return FALSE;
743     }
744 
745     return TRUE;
746 }
747 
748 class COpenWithDialog
749 {
750     public:
751         COpenWithDialog(const OPENASINFO *pInfo, COpenWithList *pAppList);
752         ~COpenWithDialog();
753         static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
754         BOOL IsNoOpen(HWND hwnd);
755 
756     private:
757         VOID Init(HWND hwnd);
758         VOID AddApp(COpenWithList::SApp *pApp, BOOL bSelected);
759         VOID Browse();
760         VOID Accept();
761         static INT_PTR CALLBACK NoOpenDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
762         COpenWithList::SApp *GetCurrentApp();
763 
764         const OPENASINFO *m_pInfo;
765         COpenWithList *m_pAppList;
766         BOOL m_bListAllocated;
767         HWND m_hDialog, m_hTreeView;
768         HTREEITEM m_hRecommend;
769         HTREEITEM m_hOther;
770         HIMAGELIST m_hImgList;
771         BOOL m_bNoOpen;
772 };
773 
774 COpenWithDialog::COpenWithDialog(const OPENASINFO *pInfo, COpenWithList *pAppList = NULL):
775     m_pInfo(pInfo), m_pAppList(pAppList), m_hImgList(NULL), m_bNoOpen(FALSE)
776 {
777     if (!m_pAppList)
778     {
779         m_pAppList = new COpenWithList;
780         m_bListAllocated = TRUE;
781     }
782     else
783         m_bListAllocated = FALSE;
784 }
785 
786 COpenWithDialog::~COpenWithDialog()
787 {
788     if (m_bListAllocated && m_pAppList)
789         delete m_pAppList;
790     if (m_hImgList)
791         ImageList_Destroy(m_hImgList);
792 }
793 
794 INT_PTR CALLBACK COpenWithDialog::NoOpenDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
795 {
796     switch(Message)
797     {
798         case WM_INITDIALOG:
799         {
800             return TRUE;
801         }
802         case WM_CLOSE:
803             EndDialog(hwnd, IDNO);
804             break;
805         case WM_COMMAND:
806             switch(LOWORD(wParam))
807             {
808                 case IDYES:
809                     EndDialog(hwnd, IDYES);
810                 break;
811                 case IDNO:
812                     EndDialog(hwnd, IDNO);
813                 break;
814             }
815         break;
816         default:
817             return FALSE;
818     }
819     return TRUE;
820 }
821 
822 BOOL COpenWithDialog::IsNoOpen(HWND hwnd)
823 {
824     /* Only do the actual check if the file type has the 'NoOpen' flag. */
825     if (m_bNoOpen)
826     {
827         int dReturnValue = DialogBox(shell32_hInstance, MAKEINTRESOURCE(IDD_NOOPEN), hwnd, NoOpenDlgProc);
828 
829         if (dReturnValue == IDNO)
830             return TRUE;
831         else if (dReturnValue == -1)
832         {
833             ERR("IsNoOpen failed to load the dialog box.");
834             return TRUE;
835         }
836     }
837 
838     return FALSE;
839 }
840 
841 VOID COpenWithDialog::AddApp(COpenWithList::SApp *pApp, BOOL bSelected)
842 {
843     LPCWSTR pwszName = m_pAppList->GetName(pApp);
844     HICON hIcon = m_pAppList->GetIcon(pApp);
845 
846     TRACE("AddApp Cmd %ls Name %ls\n", pApp->wszCmd, pwszName);
847 
848     /* Add item to the list */
849     TVINSERTSTRUCT tvins;
850 
851     if (pApp->bRecommended)
852         tvins.hParent = tvins.hInsertAfter = m_hRecommend;
853     else
854         tvins.hParent = tvins.hInsertAfter = m_hOther;
855 
856     tvins.item.mask = TVIF_TEXT|TVIF_PARAM;
857     tvins.item.pszText = (LPWSTR)pwszName;
858     tvins.item.lParam = (LPARAM)pApp;
859 
860     if (hIcon)
861     {
862         tvins.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
863         tvins.item.iImage = tvins.item.iSelectedImage = ImageList_AddIcon(m_hImgList, hIcon);
864     }
865 
866     HTREEITEM hItem = TreeView_InsertItem(m_hTreeView, &tvins);
867 
868     if (bSelected)
869         TreeView_SelectItem(m_hTreeView, hItem);
870 }
871 
872 VOID COpenWithDialog::Browse()
873 {
874     WCHAR wszTitle[64];
875     WCHAR wszFilter[256];
876     WCHAR wszPath[MAX_PATH];
877     OPENFILENAMEW ofn;
878 
879     /* Initialize OPENFILENAMEW structure */
880     ZeroMemory(&ofn, sizeof(OPENFILENAMEW));
881     ofn.lStructSize  = sizeof(OPENFILENAMEW);
882     ofn.hInstance = shell32_hInstance;
883     ofn.hwndOwner = m_hDialog;
884     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
885     ofn.nMaxFile = (sizeof(wszPath) / sizeof(WCHAR));
886     ofn.lpstrFile = wszPath;
887     ofn.lpstrInitialDir = L"%programfiles%";
888 
889     /* Init title */
890     if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH, wszTitle, sizeof(wszTitle) / sizeof(WCHAR)))
891     {
892         ofn.lpstrTitle = wszTitle;
893         ofn.nMaxFileTitle = wcslen(wszTitle);
894     }
895 
896     /* Init the filter string */
897     if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH_FILTER, wszFilter, sizeof(wszFilter) / sizeof(WCHAR)))
898         ofn.lpstrFilter = wszFilter;
899     ZeroMemory(wszPath, sizeof(wszPath));
900 
901     /* Create OpenFile dialog */
902     if (!GetOpenFileNameW(&ofn))
903         return;
904 
905     /* Setup context for insert proc */
906     COpenWithList::SApp *pApp = m_pAppList->Add(wszPath);
907     AddApp(pApp, TRUE);
908 }
909 
910 COpenWithList::SApp *COpenWithDialog::GetCurrentApp()
911 {
912     TVITEM tvi;
913     tvi.hItem = TreeView_GetSelection(m_hTreeView);
914     if (!tvi.hItem)
915         return NULL;
916 
917     tvi.mask = TVIF_PARAM;
918     if (!TreeView_GetItem(m_hTreeView, &tvi))
919         return NULL;
920 
921     return (COpenWithList::SApp*)tvi.lParam;
922 }
923 
924 VOID COpenWithDialog::Init(HWND hwnd)
925 {
926     TRACE("COpenWithDialog::Init hwnd %p\n", hwnd);
927 
928     m_hDialog = hwnd;
929     SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)this);
930 
931     /* Handle register checkbox */
932     HWND hRegisterCheckbox = GetDlgItem(hwnd, 14003);
933     if (!(m_pInfo->oaifInFlags & OAIF_ALLOW_REGISTRATION))
934         EnableWindow(hRegisterCheckbox, FALSE);
935     if (m_pInfo->oaifInFlags & OAIF_FORCE_REGISTRATION)
936         SendMessage(hRegisterCheckbox, BM_SETCHECK, BST_CHECKED, 0);
937     if (m_pInfo->oaifInFlags & OAIF_HIDE_REGISTRATION)
938         ShowWindow(hRegisterCheckbox, SW_HIDE);
939 
940     if (m_pInfo->pcszFile)
941     {
942         WCHAR wszBuf[MAX_PATH];
943         UINT cchBuf;
944 
945         /* Add filename to label */
946         cchBuf = GetDlgItemTextW(hwnd, 14001, wszBuf, _countof(wszBuf));
947         StringCchCopyW(wszBuf + cchBuf, _countof(wszBuf) - cchBuf, PathFindFileNameW(m_pInfo->pcszFile));
948         SetDlgItemTextW(hwnd, 14001, wszBuf);
949 
950         /* Load applications from registry */
951         m_pAppList->Load();
952         m_pAppList->LoadRecommended(m_pInfo->pcszFile);
953 
954         /* Determine if the type of file can be opened directly from the shell */
955         if (m_pAppList->IsNoOpen() != FALSE)
956             m_bNoOpen = TRUE;
957 
958         /* Init treeview */
959         m_hTreeView = GetDlgItem(hwnd, 14002);
960         m_hImgList = ImageList_Create(16, 16,  ILC_COLOR32 | ILC_MASK, m_pAppList->GetCount() + 1, m_pAppList->GetCount() + 1);
961         (void)TreeView_SetImageList(m_hTreeView, m_hImgList, TVSIL_NORMAL);
962 
963         /* If there are some recommendations add parent nodes: Recommended and Others */
964         UINT cRecommended = m_pAppList->GetRecommendedCount();
965         if (cRecommended > 0)
966         {
967             TVINSERTSTRUCT tvins;
968             HICON hFolderIcon;
969 
970             tvins.hParent = tvins.hInsertAfter = TVI_ROOT;
971             tvins.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
972             tvins.item.pszText = (LPWSTR)wszBuf;
973             tvins.item.state = tvins.item.stateMask = TVIS_EXPANDED;
974             hFolderIcon = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_PROGRAMS_FOLDER), IMAGE_ICON, 0, 0, 0);
975             tvins.item.iImage = tvins.item.iSelectedImage = ImageList_AddIcon(m_hImgList, hFolderIcon);
976 
977             LoadStringW(shell32_hInstance, IDS_OPEN_WITH_RECOMMENDED, wszBuf, _countof(wszBuf));
978             m_hRecommend = TreeView_InsertItem(m_hTreeView, &tvins);
979 
980             LoadStringW(shell32_hInstance, IDS_OPEN_WITH_OTHER, wszBuf, _countof(wszBuf));
981             m_hOther = TreeView_InsertItem(m_hTreeView, &tvins);
982         }
983         else
984             m_hRecommend = m_hOther = TVI_ROOT;
985 
986         /* Add all applications */
987         BOOL bNoAppSelected = TRUE;
988         COpenWithList::SApp *pAppList = m_pAppList->GetList();
989         for (UINT i = 0; i < m_pAppList->GetCount(); ++i)
990         {
991             if (!COpenWithList::IsHidden(&pAppList[i]))
992             {
993                 if (bNoAppSelected && (pAppList[i].bRecommended || !cRecommended))
994                 {
995                     AddApp(&pAppList[i], TRUE);
996                     bNoAppSelected = FALSE;
997                 }
998                 else
999                     AddApp(&pAppList[i], FALSE);
1000             }
1001         }
1002     }
1003 }
1004 
1005 VOID COpenWithDialog::Accept()
1006 {
1007     COpenWithList::SApp *pApp = GetCurrentApp();
1008     if (pApp)
1009     {
1010         /* Set programm as default handler */
1011         if (SendDlgItemMessage(m_hDialog, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1012             m_pAppList->SetDefaultHandler(pApp, m_pInfo->pcszFile);
1013 
1014         /* Execute program */
1015         if (m_pInfo->oaifInFlags & OAIF_EXEC)
1016             m_pAppList->Execute(pApp, m_pInfo->pcszFile);
1017 
1018         EndDialog(m_hDialog, 1);
1019     }
1020 }
1021 
1022 INT_PTR CALLBACK COpenWithDialog::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1023 {
1024     COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
1025 
1026     switch(uMsg)
1027     {
1028         case WM_INITDIALOG:
1029         {
1030             COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(lParam);
1031 
1032             pThis->Init(hwndDlg);
1033             return TRUE;
1034         }
1035         case WM_COMMAND:
1036             switch(LOWORD(wParam))
1037             {
1038                 case 14004: /* browse */
1039                 {
1040                     pThis->Browse();
1041                     return TRUE;
1042                 }
1043                 case IDOK: /* ok */
1044                 {
1045                     pThis->Accept();
1046                     return TRUE;
1047                 }
1048                 case IDCANCEL: /* cancel */
1049                     EndDialog(hwndDlg, 0);
1050                     return TRUE;
1051                 default:
1052                     break;
1053             }
1054             break;
1055         case WM_NOTIFY:
1056              switch (((LPNMHDR)lParam)->code)
1057              {
1058                 case TVN_SELCHANGED:
1059                     EnableWindow(GetDlgItem(hwndDlg, IDOK), pThis->GetCurrentApp() ? TRUE : FALSE);
1060                     break;
1061                 case NM_DBLCLK:
1062                 case NM_RETURN:
1063                     pThis->Accept();
1064                     break;
1065              }
1066             break;
1067         case WM_CLOSE:
1068             EndDialog(hwndDlg, 0);
1069             return TRUE;
1070         default:
1071             break;
1072     }
1073     return FALSE;
1074 }
1075 
1076 COpenWithMenu::COpenWithMenu()
1077 {
1078     m_idCmdFirst = 0;
1079     m_idCmdLast = 0;
1080     m_pAppList = new COpenWithList;
1081 }
1082 
1083 COpenWithMenu::~COpenWithMenu()
1084 {
1085     TRACE("Destroying COpenWithMenu(%p)\n", this);
1086 
1087     if (m_hSubMenu)
1088     {
1089         INT Count, Index;
1090         MENUITEMINFOW mii;
1091 
1092         /* get item count */
1093         Count = GetMenuItemCount(m_hSubMenu);
1094         if (Count == -1)
1095             return;
1096 
1097         /* setup menuitem info */
1098         ZeroMemory(&mii, sizeof(mii));
1099         mii.cbSize = sizeof(mii);
1100         mii.fMask = MIIM_DATA | MIIM_FTYPE | MIIM_CHECKMARKS;
1101 
1102         for(Index = 0; Index < Count; Index++)
1103         {
1104             if (GetMenuItemInfoW(m_hSubMenu, Index, TRUE, &mii))
1105             {
1106                 if (mii.hbmpChecked)
1107                     DeleteObject(mii.hbmpChecked);
1108             }
1109         }
1110     }
1111 
1112     if (m_pAppList)
1113         delete m_pAppList;
1114 }
1115 
1116 HBITMAP COpenWithMenu::IconToBitmap(HICON hIcon)
1117 {
1118     HDC hdc, hdcScr;
1119     HBITMAP hbm, hbmOld;
1120     RECT rc;
1121 
1122     hdcScr = GetDC(NULL);
1123     hdc = CreateCompatibleDC(hdcScr);
1124     SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
1125     hbm = CreateCompatibleBitmap(hdcScr, rc.right, rc.bottom);
1126     ReleaseDC(NULL, hdcScr);
1127 
1128     hbmOld = (HBITMAP)SelectObject(hdc, hbm);
1129     FillRect(hdc, &rc, (HBRUSH)(COLOR_MENU + 1));
1130     if (!DrawIconEx(hdc, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
1131         ERR("DrawIcon failed: %x\n", GetLastError());
1132     SelectObject(hdc, hbmOld);
1133 
1134     DeleteDC(hdc);
1135 
1136     return hbm;
1137 }
1138 
1139 VOID COpenWithMenu::AddChooseProgramItem()
1140 {
1141     MENUITEMINFOW mii;
1142     WCHAR wszBuf[128];
1143 
1144     ZeroMemory(&mii, sizeof(mii));
1145     mii.cbSize = sizeof(mii);
1146     mii.fMask = MIIM_TYPE | MIIM_ID;
1147     mii.fType = MFT_SEPARATOR;
1148     mii.wID = -1;
1149     InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1150 
1151     if (!LoadStringW(shell32_hInstance, IDS_OPEN_WITH_CHOOSE, wszBuf, _countof(wszBuf)))
1152     {
1153         ERR("Failed to load string\n");
1154         return;
1155     }
1156 
1157     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1158     mii.fType = MFT_STRING;
1159     mii.fState = MFS_ENABLED;
1160     mii.wID = m_idCmdLast;
1161     mii.dwTypeData = (LPWSTR)wszBuf;
1162     mii.cch = wcslen(wszBuf);
1163 
1164     InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1165 }
1166 
1167 VOID COpenWithMenu::AddApp(PVOID pApp)
1168 {
1169     MENUITEMINFOW mii;
1170     LPCWSTR pwszName = m_pAppList->GetName((COpenWithList::SApp*)pApp);
1171 
1172     ZeroMemory(&mii, sizeof(mii));
1173     mii.cbSize = sizeof(mii);
1174     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
1175     mii.fType = MFT_STRING;
1176     mii.fState = MFS_ENABLED;
1177     mii.wID = m_idCmdLast;
1178     mii.dwTypeData = (LPWSTR)pwszName;
1179     mii.cch = wcslen(mii.dwTypeData);
1180     mii.dwItemData = (ULONG_PTR)pApp;
1181 
1182     HICON hIcon = m_pAppList->GetIcon((COpenWithList::SApp*)pApp);
1183     if (hIcon)
1184     {
1185         mii.fMask |= MIIM_CHECKMARKS;
1186         mii.hbmpChecked = mii.hbmpUnchecked = IconToBitmap(hIcon);
1187     }
1188 
1189     if (InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii))
1190         m_idCmdLast++;
1191 }
1192 
1193 HRESULT WINAPI COpenWithMenu::QueryContextMenu(
1194     HMENU hMenu,
1195     UINT indexMenu,
1196     UINT idCmdFirst,
1197     UINT idCmdLast,
1198     UINT uFlags)
1199 {
1200     TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
1201 
1202     INT DefaultPos = GetMenuDefaultItem(hMenu, TRUE, 0);
1203 
1204     WCHAR wszName[100];
1205     UINT NameId = (DefaultPos == -1 ? IDS_OPEN : IDS_OPEN_WITH);
1206     if (!LoadStringW(shell32_hInstance, NameId, wszName, _countof(wszName)))
1207     {
1208         ERR("Failed to load string\n");
1209         return E_FAIL;
1210     }
1211 
1212     /* Init first cmd id and submenu */
1213     m_idCmdFirst = m_idCmdLast = idCmdFirst;
1214     m_hSubMenu = NULL;
1215 
1216     /* If we are going to be default item, we shouldn't be submenu */
1217     if (DefaultPos != -1)
1218     {
1219         /* Load applications list */
1220         m_pAppList->Load();
1221         m_pAppList->LoadRecommended(m_wszPath);
1222 
1223         /* Create submenu only if there is more than one application and menu has a default item */
1224         if (m_pAppList->GetRecommendedCount() > 1)
1225         {
1226             m_hSubMenu = CreatePopupMenu();
1227 
1228             for(UINT i = 0; i < m_pAppList->GetCount(); ++i)
1229             {
1230                 COpenWithList::SApp *pApp = m_pAppList->GetList() + i;
1231                 if (pApp->bRecommended)
1232                     AddApp(pApp);
1233             }
1234 
1235             AddChooseProgramItem();
1236         }
1237     }
1238 
1239     /* Insert menu item */
1240     MENUITEMINFOW mii;
1241     ZeroMemory(&mii, sizeof(mii));
1242     mii.cbSize = sizeof(mii);
1243     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1244     if (m_hSubMenu)
1245     {
1246         mii.fMask |= MIIM_SUBMENU;
1247         mii.hSubMenu = m_hSubMenu;
1248         mii.wID = -1;
1249     }
1250     else
1251         mii.wID = m_idCmdLast;
1252 
1253     mii.fType = MFT_STRING;
1254     mii.dwTypeData = (LPWSTR)wszName;
1255     mii.cch = wcslen(wszName);
1256 
1257     mii.fState = MFS_ENABLED;
1258     if (DefaultPos == -1)
1259         mii.fState |= MFS_DEFAULT;
1260 
1261     if (!InsertMenuItemW(hMenu, DefaultPos + 1, TRUE, &mii))
1262         return E_FAIL;
1263 
1264     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, m_idCmdLast - m_idCmdFirst + 1);
1265 }
1266 
1267 HRESULT WINAPI
1268 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1269 {
1270     HRESULT hr = E_FAIL;
1271 
1272     TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst, m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb));
1273 
1274     if (HIWORD(lpici->lpVerb) == 0 && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast)
1275     {
1276         if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdLast)
1277         {
1278             OPENASINFO info;
1279             LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
1280 
1281             info.pcszFile = m_wszPath;
1282             info.oaifInFlags = OAIF_EXEC;
1283             if (pwszExt[0])
1284                 info.oaifInFlags |= OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION;
1285             info.pcszClass = NULL;
1286             hr = SHOpenWithDialog(lpici->hwnd, &info);
1287         }
1288         else
1289         {
1290             /* retrieve menu item info */
1291             MENUITEMINFOW mii;
1292             ZeroMemory(&mii, sizeof(mii));
1293             mii.cbSize = sizeof(mii);
1294             mii.fMask = MIIM_DATA | MIIM_FTYPE;
1295 
1296             if (GetMenuItemInfoW(m_hSubMenu, LOWORD(lpici->lpVerb), TRUE, &mii) && mii.dwItemData)
1297             {
1298                 /* launch item with specified app */
1299                 COpenWithList::SApp *pApp = (COpenWithList::SApp*)mii.dwItemData;
1300                 COpenWithList::Execute(pApp, m_wszPath);
1301                 hr = S_OK;
1302             }
1303         }
1304     }
1305 
1306     return hr;
1307 }
1308 
1309 HRESULT WINAPI
1310 COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
1311                                 UINT* pwReserved, LPSTR pszName, UINT cchMax )
1312 {
1313     FIXME("%p %lu %u %p %p %u\n", this,
1314           idCmd, uType, pwReserved, pszName, cchMax );
1315 
1316     return E_NOTIMPL;
1317 }
1318 
1319 HRESULT WINAPI COpenWithMenu::HandleMenuMsg(
1320     UINT uMsg,
1321     WPARAM wParam,
1322     LPARAM lParam)
1323 {
1324     TRACE("This %p uMsg %x\n", this, uMsg);
1325 
1326     return E_NOTIMPL;
1327 }
1328 
1329 HRESULT WINAPI
1330 COpenWithMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
1331                           IDataObject *pdtobj,
1332                           HKEY hkeyProgID)
1333 {
1334     STGMEDIUM medium;
1335     FORMATETC fmt;
1336     HRESULT hr;
1337     LPIDA pida;
1338     LPCITEMIDLIST pidlFolder2;
1339     LPCITEMIDLIST pidlChild;
1340     LPCITEMIDLIST pidl;
1341     LPCWSTR pwszExt;
1342 
1343     TRACE("This %p\n", this);
1344 
1345     if (pdtobj == NULL)
1346         return E_INVALIDARG;
1347 
1348     fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
1349     fmt.ptd = NULL;
1350     fmt.dwAspect = DVASPECT_CONTENT;
1351     fmt.lindex = -1;
1352     fmt.tymed = TYMED_HGLOBAL;
1353 
1354     hr = pdtobj->GetData(&fmt, &medium);
1355 
1356     if (FAILED(hr))
1357     {
1358         ERR("pdtobj->GetData failed with 0x%x\n", hr);
1359         return hr;
1360     }
1361 
1362     pida = (LPIDA)GlobalLock(medium.hGlobal);
1363     ASSERT(pida->cidl >= 1);
1364 
1365     pidlFolder2 = (LPCITEMIDLIST) ((LPBYTE)pida + pida->aoffset[0]);
1366     pidlChild = (LPCITEMIDLIST) ((LPBYTE)pida + pida->aoffset[1]);
1367 
1368     if (!_ILIsValue(pidlChild))
1369     {
1370         TRACE("pidl is not a file\n");
1371         GlobalUnlock(medium.hGlobal);
1372         ReleaseStgMedium(&medium);
1373         return E_FAIL;
1374     }
1375 
1376     pidl = ILCombine(pidlFolder2, pidlChild);
1377 
1378     GlobalUnlock(medium.hGlobal);
1379     ReleaseStgMedium(&medium);
1380 
1381     if (!pidl)
1382     {
1383         ERR("no mem\n");
1384         return E_OUTOFMEMORY;
1385     }
1386 
1387     if (!SHGetPathFromIDListW(pidl, m_wszPath))
1388     {
1389         SHFree((void*)pidl);
1390         ERR("SHGetPathFromIDListW failed\n");
1391         return E_FAIL;
1392     }
1393 
1394     SHFree((void*)pidl);
1395     TRACE("szPath %s\n", debugstr_w(m_wszPath));
1396 
1397     pwszExt = PathFindExtensionW(m_wszPath);
1398     if (PathIsExeW(pwszExt) || !_wcsicmp(pwszExt, L".lnk"))
1399     {
1400         TRACE("file is a executable or shortcut\n");
1401         return E_FAIL;
1402     }
1403 
1404     return S_OK;
1405 }
1406 
1407 HRESULT WINAPI
1408 SHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
1409 {
1410     INT_PTR ret;
1411 
1412     TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent, poainfo);
1413 
1414     InitCommonControls();
1415 
1416     if (poainfo->pcszClass == NULL && poainfo->pcszFile == NULL)
1417         return E_FAIL;
1418 
1419     COpenWithDialog pDialog(poainfo);
1420 
1421     if (pDialog.IsNoOpen(hwndParent))
1422         return S_OK;
1423 
1424     ret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCE(IDD_OPEN_WITH), hwndParent,
1425                           COpenWithDialog::DialogProc, (LPARAM)&pDialog);
1426 
1427     if (ret == (INT_PTR)-1)
1428     {
1429         ERR("Failed to create dialog: %u\n", GetLastError());
1430         return E_FAIL;
1431     }
1432 
1433     return S_OK;
1434 }
1435