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