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