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