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