75d61adaf5788f18740a4a503a93884fd7b72003
[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
911 /* Init title */
912 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH, wszTitle, sizeof(wszTitle) / sizeof(WCHAR)))
913 {
914 ofn.lpstrTitle = wszTitle;
915 ofn.nMaxFileTitle = wcslen(wszTitle);
916 }
917
918 /* Init the filter string */
919 if (LoadStringW(shell32_hInstance, IDS_OPEN_WITH_FILTER, wszFilter, sizeof(wszFilter) / sizeof(WCHAR)))
920 ofn.lpstrFilter = wszFilter;
921 ZeroMemory(wszPath, sizeof(wszPath));
922
923 /* Create OpenFile dialog */
924 if (!GetOpenFileNameW(&ofn))
925 return;
926
927 /* Setup context for insert proc */
928 COpenWithList::SApp *pApp = m_pAppList->Add(wszPath);
929 AddApp(pApp, TRUE);
930 }
931
932 COpenWithList::SApp *COpenWithDialog::GetCurrentApp()
933 {
934 TVITEM tvi;
935 tvi.hItem = TreeView_GetSelection(m_hTreeView);
936 if (!tvi.hItem)
937 return NULL;
938
939 tvi.mask = TVIF_PARAM;
940 if (!TreeView_GetItem(m_hTreeView, &tvi))
941 return NULL;
942
943 return (COpenWithList::SApp*)tvi.lParam;
944 }
945
946 VOID COpenWithDialog::Init(HWND hwnd)
947 {
948 TRACE("COpenWithDialog::Init hwnd %p\n", hwnd);
949
950 m_hDialog = hwnd;
951 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)this);
952
953 /* Handle register checkbox */
954 HWND hRegisterCheckbox = GetDlgItem(hwnd, 14003);
955 if (!(m_pInfo->oaifInFlags & OAIF_ALLOW_REGISTRATION))
956 EnableWindow(hRegisterCheckbox, FALSE);
957 if (m_pInfo->oaifInFlags & OAIF_FORCE_REGISTRATION)
958 SendMessage(hRegisterCheckbox, BM_SETCHECK, BST_CHECKED, 0);
959 if (m_pInfo->oaifInFlags & OAIF_HIDE_REGISTRATION)
960 ShowWindow(hRegisterCheckbox, SW_HIDE);
961
962 if (m_pInfo->pcszFile)
963 {
964 WCHAR wszBuf[MAX_PATH];
965 UINT cchBuf;
966
967 /* Add filename to label */
968 cchBuf = GetDlgItemTextW(hwnd, 14001, wszBuf, _countof(wszBuf));
969 StringCchCopyW(wszBuf + cchBuf, _countof(wszBuf) - cchBuf, PathFindFileNameW(m_pInfo->pcszFile));
970 SetDlgItemTextW(hwnd, 14001, wszBuf);
971
972 /* Load applications from registry */
973 m_pAppList->Load();
974 m_pAppList->LoadRecommended(m_pInfo->pcszFile);
975
976 /* Determine if the type of file can be opened directly from the shell */
977 if (m_pAppList->IsNoOpen() != FALSE)
978 m_bNoOpen = TRUE;
979
980 /* Init treeview */
981 m_hTreeView = GetDlgItem(hwnd, 14002);
982 m_hImgList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, m_pAppList->GetCount() + 1, m_pAppList->GetCount() + 1);
983 (void)TreeView_SetImageList(m_hTreeView, m_hImgList, TVSIL_NORMAL);
984
985 /* If there are some recommendations add parent nodes: Recommended and Others */
986 UINT cRecommended = m_pAppList->GetRecommendedCount();
987 if (cRecommended > 0)
988 {
989 TVINSERTSTRUCT tvins;
990 HICON hFolderIcon;
991
992 tvins.hParent = tvins.hInsertAfter = TVI_ROOT;
993 tvins.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
994 tvins.item.pszText = (LPWSTR)wszBuf;
995 tvins.item.state = tvins.item.stateMask = TVIS_EXPANDED;
996 hFolderIcon = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_PROGRAMS_FOLDER), IMAGE_ICON, 0, 0, 0);
997 tvins.item.iImage = tvins.item.iSelectedImage = ImageList_AddIcon(m_hImgList, hFolderIcon);
998
999 LoadStringW(shell32_hInstance, IDS_OPEN_WITH_RECOMMENDED, wszBuf, _countof(wszBuf));
1000 m_hRecommend = TreeView_InsertItem(m_hTreeView, &tvins);
1001
1002 LoadStringW(shell32_hInstance, IDS_OPEN_WITH_OTHER, wszBuf, _countof(wszBuf));
1003 m_hOther = TreeView_InsertItem(m_hTreeView, &tvins);
1004 }
1005 else
1006 m_hRecommend = m_hOther = TVI_ROOT;
1007
1008 /* Add all applications */
1009 BOOL bNoAppSelected = TRUE;
1010 COpenWithList::SApp *pAppList = m_pAppList->GetList();
1011 for (UINT i = 0; i < m_pAppList->GetCount(); ++i)
1012 {
1013 if (!COpenWithList::IsHidden(&pAppList[i]))
1014 {
1015 if (bNoAppSelected && (pAppList[i].bRecommended || !cRecommended))
1016 {
1017 AddApp(&pAppList[i], TRUE);
1018 bNoAppSelected = FALSE;
1019 }
1020 else
1021 AddApp(&pAppList[i], FALSE);
1022 }
1023 }
1024 }
1025 }
1026
1027 VOID COpenWithDialog::Accept()
1028 {
1029 COpenWithList::SApp *pApp = GetCurrentApp();
1030 if (pApp)
1031 {
1032 /* Set programm as default handler */
1033 if (SendDlgItemMessage(m_hDialog, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1034 m_pAppList->SetDefaultHandler(pApp, m_pInfo->pcszFile);
1035
1036 /* Execute program */
1037 if (m_pInfo->oaifInFlags & OAIF_EXEC)
1038 m_pAppList->Execute(pApp, m_pInfo->pcszFile);
1039
1040 DestroyWindow(m_hDialog);
1041 }
1042 }
1043
1044 INT_PTR CALLBACK COpenWithDialog::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1045 {
1046 COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
1047
1048 switch(uMsg)
1049 {
1050 case WM_INITDIALOG:
1051 {
1052 COpenWithDialog *pThis = reinterpret_cast<COpenWithDialog *>(lParam);
1053
1054 pThis->Init(hwndDlg);
1055 return TRUE;
1056 }
1057 case WM_COMMAND:
1058 switch(LOWORD(wParam))
1059 {
1060 case 14004: /* browse */
1061 {
1062 pThis->Browse();
1063 return TRUE;
1064 }
1065 case IDOK: /* ok */
1066 {
1067 pThis->Accept();
1068 return TRUE;
1069 }
1070 case IDCANCEL: /* cancel */
1071 DestroyWindow(hwndDlg);
1072 return TRUE;
1073 default:
1074 break;
1075 }
1076 break;
1077 case WM_NOTIFY:
1078 switch (((LPNMHDR)lParam)->code)
1079 {
1080 case TVN_SELCHANGED:
1081 EnableWindow(GetDlgItem(hwndDlg, IDOK), pThis->GetCurrentApp() ? TRUE : FALSE);
1082 break;
1083 case NM_DBLCLK:
1084 case NM_RETURN:
1085 pThis->Accept();
1086 break;
1087 }
1088 break;
1089 case WM_CLOSE:
1090 DestroyWindow(hwndDlg);
1091 return TRUE;
1092 default:
1093 break;
1094 }
1095 return FALSE;
1096 }
1097
1098 COpenWithMenu::COpenWithMenu()
1099 {
1100 m_idCmdFirst = 0;
1101 m_idCmdLast = 0;
1102 m_pAppList = new COpenWithList;
1103 }
1104
1105 COpenWithMenu::~COpenWithMenu()
1106 {
1107 TRACE("Destroying COpenWithMenu(%p)\n", this);
1108
1109 if (m_hSubMenu)
1110 {
1111 INT Count, Index;
1112 MENUITEMINFOW mii;
1113
1114 /* get item count */
1115 Count = GetMenuItemCount(m_hSubMenu);
1116 if (Count == -1)
1117 return;
1118
1119 /* setup menuitem info */
1120 ZeroMemory(&mii, sizeof(mii));
1121 mii.cbSize = sizeof(mii);
1122 mii.fMask = MIIM_DATA | MIIM_FTYPE | MIIM_CHECKMARKS;
1123
1124 for(Index = 0; Index < Count; Index++)
1125 {
1126 if (GetMenuItemInfoW(m_hSubMenu, Index, TRUE, &mii))
1127 {
1128 if (mii.hbmpChecked)
1129 DeleteObject(mii.hbmpChecked);
1130 }
1131 }
1132 }
1133
1134 if (m_pAppList)
1135 delete m_pAppList;
1136 }
1137
1138 HBITMAP COpenWithMenu::IconToBitmap(HICON hIcon)
1139 {
1140 HDC hdc, hdcScr;
1141 HBITMAP hbm, hbmOld;
1142 RECT rc;
1143
1144 hdcScr = GetDC(NULL);
1145 hdc = CreateCompatibleDC(hdcScr);
1146 SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK));
1147 hbm = CreateCompatibleBitmap(hdcScr, rc.right, rc.bottom);
1148 ReleaseDC(NULL, hdcScr);
1149
1150 hbmOld = (HBITMAP)SelectObject(hdc, hbm);
1151 FillRect(hdc, &rc, (HBRUSH)(COLOR_MENU + 1));
1152 if (!DrawIconEx(hdc, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
1153 ERR("DrawIcon failed: %x\n", GetLastError());
1154 SelectObject(hdc, hbmOld);
1155
1156 DeleteDC(hdc);
1157
1158 return hbm;
1159 }
1160
1161 VOID COpenWithMenu::AddChooseProgramItem()
1162 {
1163 MENUITEMINFOW mii;
1164 WCHAR wszBuf[128];
1165
1166 ZeroMemory(&mii, sizeof(mii));
1167 mii.cbSize = sizeof(mii);
1168 mii.fMask = MIIM_TYPE | MIIM_ID;
1169 mii.fType = MFT_SEPARATOR;
1170 mii.wID = -1;
1171 InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1172
1173 if (!LoadStringW(shell32_hInstance, IDS_OPEN_WITH_CHOOSE, wszBuf, _countof(wszBuf)))
1174 {
1175 ERR("Failed to load string\n");
1176 return;
1177 }
1178
1179 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1180 mii.fType = MFT_STRING;
1181 mii.fState = MFS_ENABLED;
1182 mii.wID = m_idCmdLast;
1183 mii.dwTypeData = (LPWSTR)wszBuf;
1184 mii.cch = wcslen(wszBuf);
1185
1186 InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii);
1187 }
1188
1189 VOID COpenWithMenu::AddApp(PVOID pApp)
1190 {
1191 MENUITEMINFOW mii;
1192 LPCWSTR pwszName = m_pAppList->GetName((COpenWithList::SApp*)pApp);
1193
1194 ZeroMemory(&mii, sizeof(mii));
1195 mii.cbSize = sizeof(mii);
1196 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
1197 mii.fType = MFT_STRING;
1198 mii.fState = MFS_ENABLED;
1199 mii.wID = m_idCmdLast;
1200 mii.dwTypeData = (LPWSTR)pwszName;
1201 mii.cch = wcslen(mii.dwTypeData);
1202 mii.dwItemData = (ULONG_PTR)pApp;
1203
1204 HICON hIcon = m_pAppList->GetIcon((COpenWithList::SApp*)pApp);
1205 if (hIcon)
1206 {
1207 mii.fMask |= MIIM_CHECKMARKS;
1208 mii.hbmpChecked = mii.hbmpUnchecked = IconToBitmap(hIcon);
1209 }
1210
1211 if (InsertMenuItemW(m_hSubMenu, -1, TRUE, &mii))
1212 m_idCmdLast++;
1213 }
1214
1215 HRESULT WINAPI COpenWithMenu::QueryContextMenu(
1216 HMENU hMenu,
1217 UINT indexMenu,
1218 UINT idCmdFirst,
1219 UINT idCmdLast,
1220 UINT uFlags)
1221 {
1222 TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
1223
1224 INT DefaultPos = GetMenuDefaultItem(hMenu, TRUE, 0);
1225
1226 WCHAR wszName[100];
1227 UINT NameId = (DefaultPos == -1 ? IDS_OPEN : IDS_OPEN_WITH);
1228 if (!LoadStringW(shell32_hInstance, NameId, wszName, _countof(wszName)))
1229 {
1230 ERR("Failed to load string\n");
1231 return E_FAIL;
1232 }
1233
1234 /* Init first cmd id and submenu */
1235 m_idCmdFirst = m_idCmdLast = idCmdFirst;
1236 m_hSubMenu = NULL;
1237
1238 /* If we are going to be default item, we shouldn't be submenu */
1239 if (DefaultPos != -1)
1240 {
1241 /* Load applications list */
1242 m_pAppList->Load();
1243 m_pAppList->LoadRecommended(m_wszPath);
1244
1245 /* Create submenu only if there is more than one application and menu has a default item */
1246 if (m_pAppList->GetRecommendedCount() > 1)
1247 {
1248 m_hSubMenu = CreatePopupMenu();
1249
1250 for(UINT i = 0; i < m_pAppList->GetCount(); ++i)
1251 {
1252 COpenWithList::SApp *pApp = m_pAppList->GetList() + i;
1253 if (pApp->bRecommended)
1254 AddApp(pApp);
1255 }
1256
1257 AddChooseProgramItem();
1258 }
1259 }
1260
1261 /* Insert menu item */
1262 MENUITEMINFOW mii;
1263 ZeroMemory(&mii, sizeof(mii));
1264 mii.cbSize = sizeof(mii);
1265 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
1266 if (m_hSubMenu)
1267 {
1268 mii.fMask |= MIIM_SUBMENU;
1269 mii.hSubMenu = m_hSubMenu;
1270 mii.wID = -1;
1271 }
1272 else
1273 mii.wID = m_idCmdLast;
1274
1275 mii.fType = MFT_STRING;
1276 mii.dwTypeData = (LPWSTR)wszName;
1277 mii.cch = wcslen(wszName);
1278
1279 mii.fState = MFS_ENABLED;
1280 if (DefaultPos == -1)
1281 mii.fState |= MFS_DEFAULT;
1282
1283 if (!InsertMenuItemW(hMenu, DefaultPos + 1, TRUE, &mii))
1284 return E_FAIL;
1285
1286 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, m_idCmdLast - m_idCmdFirst + 1);
1287 }
1288
1289 HRESULT WINAPI
1290 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
1291 {
1292 HRESULT hr = E_FAIL;
1293
1294 TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst, m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb));
1295
1296 if (HIWORD(lpici->lpVerb) == 0 && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast)
1297 {
1298 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdLast)
1299 {
1300 OPENASINFO info;
1301 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
1302
1303 info.pcszFile = m_wszPath;
1304 info.oaifInFlags = OAIF_EXEC;
1305 if (pwszExt[0])
1306 info.oaifInFlags |= OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION;
1307 info.pcszClass = NULL;
1308 hr = SHOpenWithDialog(lpici->hwnd, &info);
1309 }
1310 else
1311 {
1312 /* retrieve menu item info */
1313 MENUITEMINFOW mii;
1314 ZeroMemory(&mii, sizeof(mii));
1315 mii.cbSize = sizeof(mii);
1316 mii.fMask = MIIM_DATA | MIIM_FTYPE;
1317
1318 if (GetMenuItemInfoW(m_hSubMenu, LOWORD(lpici->lpVerb), TRUE, &mii) && mii.dwItemData)
1319 {
1320 /* launch item with specified app */
1321 COpenWithList::SApp *pApp = (COpenWithList::SApp*)mii.dwItemData;
1322 COpenWithList::Execute(pApp, m_wszPath);
1323 hr = S_OK;
1324 }
1325 }
1326 }
1327
1328 return hr;
1329 }
1330
1331 HRESULT WINAPI
1332 COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType,
1333 UINT* pwReserved, LPSTR pszName, UINT cchMax )
1334 {
1335 FIXME("%p %lu %u %p %p %u\n", this,
1336 idCmd, uType, pwReserved, pszName, cchMax );
1337
1338 return E_NOTIMPL;
1339 }
1340
1341 HRESULT WINAPI COpenWithMenu::HandleMenuMsg(
1342 UINT uMsg,
1343 WPARAM wParam,
1344 LPARAM lParam)
1345 {
1346 TRACE("This %p uMsg %x\n", this, uMsg);
1347
1348 return E_NOTIMPL;
1349 }
1350
1351 HRESULT WINAPI
1352 COpenWithMenu::Initialize(LPCITEMIDLIST pidlFolder,
1353 IDataObject *pdtobj,
1354 HKEY hkeyProgID)
1355 {
1356 STGMEDIUM medium;
1357 FORMATETC fmt;
1358 HRESULT hr;
1359 LPIDA pida;
1360 LPCITEMIDLIST pidlFolder2;
1361 LPCITEMIDLIST pidlChild;
1362 LPCITEMIDLIST pidl;
1363 LPCWSTR pwszExt;
1364
1365 TRACE("This %p\n", this);
1366
1367 if (pdtobj == NULL)
1368 return E_INVALIDARG;
1369
1370 fmt.cfFormat = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
1371 fmt.ptd = NULL;
1372 fmt.dwAspect = DVASPECT_CONTENT;
1373 fmt.lindex = -1;
1374 fmt.tymed = TYMED_HGLOBAL;
1375
1376 hr = pdtobj->GetData(&fmt, &medium);
1377
1378 if (FAILED(hr))
1379 {
1380 ERR("pdtobj->GetData failed with 0x%x\n", hr);
1381 return hr;
1382 }
1383
1384 pida = (LPIDA)GlobalLock(medium.hGlobal);
1385 ASSERT(pida->cidl >= 1);
1386
1387 pidlFolder2 = (LPCITEMIDLIST) ((LPBYTE)pida + pida->aoffset[0]);
1388 pidlChild = (LPCITEMIDLIST) ((LPBYTE)pida + pida->aoffset[1]);
1389
1390 if (!_ILIsValue(pidlChild))
1391 {
1392 TRACE("pidl is not a file\n");
1393 GlobalUnlock(medium.hGlobal);
1394 GlobalFree(medium.hGlobal);
1395 return E_FAIL;
1396 }
1397
1398 pidl = ILCombine(pidlFolder2, pidlChild);
1399
1400 GlobalUnlock(medium.hGlobal);
1401 GlobalFree(medium.hGlobal);
1402
1403 if (!pidl)
1404 {
1405 ERR("no mem\n");
1406 return E_OUTOFMEMORY;
1407 }
1408
1409 if (!SHGetPathFromIDListW(pidl, m_wszPath))
1410 {
1411 SHFree((void*)pidl);
1412 ERR("SHGetPathFromIDListW failed\n");
1413 return E_FAIL;
1414 }
1415
1416 SHFree((void*)pidl);
1417 TRACE("szPath %s\n", debugstr_w(m_wszPath));
1418
1419 pwszExt = PathFindExtensionW(m_wszPath);
1420 if (PathIsExeW(pwszExt) || !_wcsicmp(pwszExt, L".lnk"))
1421 {
1422 TRACE("file is a executable or shortcut\n");
1423 return E_FAIL;
1424 }
1425
1426 return S_OK;
1427 }
1428
1429 HRESULT WINAPI
1430 SHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
1431 {
1432 MSG msg;
1433 HWND hwnd;
1434
1435 TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent, poainfo);
1436
1437 InitCommonControls();
1438
1439 if (poainfo->pcszClass == NULL && poainfo->pcszFile == NULL)
1440 return E_FAIL;
1441
1442 COpenWithDialog pDialog(poainfo);
1443
1444 hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_OPEN_WITH), hwndParent, COpenWithDialog::DialogProc, (LPARAM)&pDialog);
1445 if (hwnd == NULL)
1446 {
1447 ERR("Failed to create dialog\n");
1448 return E_FAIL;
1449 }
1450
1451 if (pDialog.IsNoOpen(hwndParent))
1452 return S_OK;
1453
1454 ShowWindow(hwnd, SW_SHOWNORMAL);
1455
1456 while (GetMessage(&msg, NULL, 0, 0) && IsWindow(hwnd))
1457 {
1458 if (!IsDialogMessage(hwnd, &msg))
1459 {
1460 TranslateMessage(&msg);
1461 DispatchMessage(&msg);
1462 }
1463 }
1464
1465 return S_OK;
1466 }