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