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