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 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 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, 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1136 COpenWithMenu::COpenWithMenu() 1137 { 1138 m_idCmdFirst = 0; 1139 m_idCmdLast = 0; 1140 m_pAppList = new COpenWithList; 1141 } 1142 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 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 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 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 1253 static const CMVERBMAP g_VerbMap[] = { 1254 { "openas", 0 }, 1255 { NULL } 1256 }; 1257 1258 HRESULT WINAPI COpenWithMenu::QueryContextMenu( 1259 HMENU hMenu, 1260 UINT indexMenu, 1261 UINT idCmdFirst, 1262 UINT idCmdLast, 1263 UINT uFlags) 1264 { 1265 TRACE("hMenu %p indexMenu %u idFirst %u idLast %u uFlags %u\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 1266 1267 INT DefaultPos = GetMenuDefaultItem(hMenu, TRUE, 0); 1268 1269 WCHAR wszName[100]; 1270 UINT NameId = (DefaultPos == -1 ? IDS_OPEN : IDS_OPEN_WITH); 1271 if (!LoadStringW(shell32_hInstance, NameId, wszName, _countof(wszName))) 1272 { 1273 ERR("Failed to load string\n"); 1274 return E_FAIL; 1275 } 1276 1277 /* Init first cmd id and submenu */ 1278 m_idCmdFirst = m_idCmdLast = idCmdFirst; 1279 m_hSubMenu = NULL; 1280 1281 /* We can only be a submenu if we are not the default */ 1282 if (DefaultPos != -1) 1283 { 1284 /* Load applications list */ 1285 m_pAppList->Load(); 1286 m_pAppList->LoadRecommended(m_wszPath); 1287 1288 /* Create submenu only if there is more than one application and menu has a default item */ 1289 if (m_pAppList->GetRecommendedCount() > 1) 1290 { 1291 m_hSubMenu = CreatePopupMenu(); 1292 1293 for(UINT i = 0; i < m_pAppList->GetCount(); ++i) 1294 { 1295 COpenWithList::SApp *pApp = m_pAppList->GetList() + i; 1296 if (pApp->bRecommended) 1297 AddApp(pApp); 1298 } 1299 1300 AddChooseProgramItem(); 1301 } 1302 } 1303 1304 /* Insert menu item */ 1305 MENUITEMINFOW mii; 1306 ZeroMemory(&mii, sizeof(mii)); 1307 mii.cbSize = sizeof(mii); 1308 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; 1309 if (m_hSubMenu) 1310 { 1311 mii.fMask |= MIIM_SUBMENU; 1312 mii.hSubMenu = m_hSubMenu; 1313 mii.wID = -1; 1314 } 1315 else 1316 mii.wID = m_idCmdLast; 1317 1318 mii.fType = MFT_STRING; 1319 mii.dwTypeData = (LPWSTR)wszName; 1320 mii.fState = MFS_ENABLED; 1321 if (DefaultPos == -1) 1322 { 1323 mii.fState |= MFS_DEFAULT; 1324 indexMenu = 0; 1325 } 1326 1327 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) 1328 return E_FAIL; 1329 1330 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, m_idCmdLast - m_idCmdFirst + 1); 1331 } 1332 1333 HRESULT WINAPI 1334 COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 1335 { 1336 const SIZE_T idChooseApp = m_idCmdLast; 1337 HRESULT hr = E_FAIL; 1338 1339 TRACE("This %p idFirst %u idLast %u idCmd %u\n", this, m_idCmdFirst, m_idCmdLast, m_idCmdFirst + LOWORD(lpici->lpVerb)); 1340 1341 if (!IS_INTRESOURCE(lpici->lpVerb) && SHELL_MapContextMenuVerbToCmdId(lpici, g_VerbMap) == 0) 1342 goto DoChooseApp; 1343 1344 if (IS_INTRESOURCE(lpici->lpVerb) && m_idCmdFirst + LOWORD(lpici->lpVerb) <= m_idCmdLast) 1345 { 1346 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp) 1347 { 1348 DoChooseApp: 1349 OPENASINFO info; 1350 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 1351 1352 info.pcszFile = m_wszPath; 1353 info.oaifInFlags = OAIF_EXEC; 1354 if (pwszExt[0]) 1355 info.oaifInFlags |= OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION; 1356 info.pcszClass = NULL; 1357 hr = SHOpenWithDialog(lpici->hwnd, &info); 1358 } 1359 else 1360 { 1361 /* retrieve menu item info */ 1362 MENUITEMINFOW mii; 1363 ZeroMemory(&mii, sizeof(mii)); 1364 mii.cbSize = sizeof(mii); 1365 mii.fMask = MIIM_DATA | MIIM_FTYPE; 1366 1367 if (GetMenuItemInfoW(m_hSubMenu, LOWORD(lpici->lpVerb), TRUE, &mii) && mii.dwItemData) 1368 { 1369 /* launch item with specified app */ 1370 COpenWithList::SApp *pApp = (COpenWithList::SApp*)mii.dwItemData; 1371 COpenWithList::Execute(pApp, m_wszPath); 1372 hr = S_OK; 1373 } 1374 } 1375 } 1376 1377 return hr; 1378 } 1379 1380 HRESULT WINAPI 1381 COpenWithMenu::GetCommandString(UINT_PTR idCmd, UINT uType, 1382 UINT* pwReserved, LPSTR pszName, UINT cchMax ) 1383 { 1384 TRACE("%p %lu %u %p %p %u\n", this, 1385 idCmd, uType, pwReserved, pszName, cchMax ); 1386 1387 const SIZE_T idChooseApp = m_idCmdLast; 1388 if (m_idCmdFirst + idCmd == idChooseApp) 1389 return SHELL_GetCommandStringImpl(0, uType, pszName, cchMax, g_VerbMap); 1390 1391 return E_NOTIMPL; 1392 } 1393 1394 HRESULT WINAPI COpenWithMenu::HandleMenuMsg( 1395 UINT uMsg, 1396 WPARAM wParam, 1397 LPARAM lParam) 1398 { 1399 TRACE("This %p uMsg %x\n", this, uMsg); 1400 1401 return E_NOTIMPL; 1402 } 1403 1404 HRESULT WINAPI 1405 COpenWithMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, 1406 IDataObject *pdtobj, 1407 HKEY hkeyProgID) 1408 { 1409 LPCITEMIDLIST pidlFolder2; 1410 LPCITEMIDLIST pidlChild; 1411 1412 TRACE("This %p\n", this); 1413 1414 if (pdtobj == NULL) 1415 return E_INVALIDARG; 1416 1417 CDataObjectHIDA pida(pdtobj); 1418 if (FAILED(pida.hr())) 1419 { 1420 ERR("pdtobj->GetData failed with 0x%x\n", pida.hr()); 1421 return pida.hr(); 1422 } 1423 1424 ASSERT(pida->cidl >= 1); 1425 1426 pidlFolder2 = HIDA_GetPIDLFolder(pida); 1427 pidlChild = HIDA_GetPIDLItem(pida, 0); 1428 1429 if (!_ILIsValue(pidlChild)) 1430 { 1431 TRACE("pidl is not a file\n"); 1432 return E_FAIL; 1433 } 1434 1435 CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlFolder2, pidlChild)); 1436 if (!pidl) 1437 { 1438 ERR("no mem\n"); 1439 return E_OUTOFMEMORY; 1440 } 1441 1442 if (!SHGetPathFromIDListW(pidl, m_wszPath)) 1443 { 1444 ERR("SHGetPathFromIDListW failed\n"); 1445 return E_FAIL; 1446 } 1447 1448 TRACE("szPath %s\n", debugstr_w(m_wszPath)); 1449 1450 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); 1451 if (PathIsExeW(pwszExt) || !_wcsicmp(pwszExt, L".lnk")) 1452 { 1453 TRACE("file is a executable or shortcut\n"); 1454 return E_FAIL; 1455 } 1456 1457 return S_OK; 1458 } 1459 1460 HRESULT WINAPI 1461 SHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo) 1462 { 1463 INT_PTR ret; 1464 1465 TRACE("SHOpenWithDialog hwndParent %p poainfo %p\n", hwndParent, poainfo); 1466 1467 InitCommonControls(); 1468 1469 if (poainfo->pcszClass == NULL && poainfo->pcszFile == NULL) 1470 return E_FAIL; 1471 1472 COpenWithDialog pDialog(poainfo); 1473 1474 if (pDialog.IsNoOpen(hwndParent)) 1475 return S_OK; 1476 1477 ret = DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCE(IDD_OPEN_WITH), hwndParent, 1478 COpenWithDialog::DialogProc, (LPARAM)&pDialog); 1479 1480 if (ret == (INT_PTR)-1) 1481 { 1482 ERR("Failed to create dialog: %u\n", GetLastError()); 1483 return E_FAIL; 1484 } 1485 1486 return S_OK; 1487 } 1488