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