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