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