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