1 #include "precomp.h" 2 #include <shlwapi.h> 3 #include <shlwapi_undoc.h> 4 5 #define PROXY_DESKTOP_CLASS L"Proxy Desktop" 6 7 BOOL g_SeparateFolders = FALSE; 8 HWND g_hwndProxyDesktop = NULL; 9 10 // fields indented more are unknown ;P 11 struct HNFBlock 12 { 13 UINT cbSize; 14 DWORD offset4; 15 DWORD offset8; 16 DWORD offsetC; 17 DWORD offset10; 18 DWORD offset14; 19 DWORD offset18; 20 DWORD offset1C; 21 DWORD offset20; 22 DWORD offset24; 23 DWORD offset28; 24 DWORD offset2C; 25 DWORD offset30; 26 UINT directoryPidlLength; 27 UINT pidlSize7C; 28 UINT pidlSize80; 29 UINT pathLength; 30 }; 31 32 class CProxyDesktop : 33 public CComObjectRootEx<CComMultiThreadModelNoCS>, 34 public CWindowImpl < CProxyDesktop, CWindow, CFrameWinTraits > 35 { 36 37 public: 38 CProxyDesktop(IEThreadParamBlock * parameters) 39 { 40 } 41 42 virtual ~CProxyDesktop() 43 { 44 } 45 46 LRESULT OnMessage1037(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 47 { 48 TRACE("Proxy Desktop message 1037.\n"); 49 bHandled = TRUE; 50 return TRUE; 51 } 52 53 LRESULT OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 54 { 55 TRACE("Proxy Desktop message 1035 received.\n"); 56 bHandled = TRUE; 57 SHOnCWMCommandLine((HANDLE) lParam); 58 return 0; 59 } 60 61 DECLARE_WND_CLASS_EX(PROXY_DESKTOP_CLASS, CS_SAVEBITS | CS_DROPSHADOW, COLOR_3DFACE) 62 63 BEGIN_MSG_MAP(CProxyDesktop) 64 MESSAGE_HANDLER(WM_EXPLORER_1037, OnMessage1037) 65 MESSAGE_HANDLER(WM_EXPLORER_OPEN_NEW_WINDOW, OnOpenNewWindow) 66 END_MSG_MAP() 67 }; 68 69 HWND FindShellProxy(LPITEMIDLIST pidl) 70 { 71 /* If there is a proxy desktop in the current process use it */ 72 if (g_hwndProxyDesktop) 73 return g_hwndProxyDesktop; 74 75 /* Try to find the desktop of the main explorer process */ 76 if (!g_SeparateFolders) 77 { 78 HWND shell = GetShellWindow(); 79 80 if (shell) 81 { 82 TRACE("Found main desktop.\n"); 83 return shell; 84 } 85 } 86 else 87 { 88 TRACE("Separate folders setting enabled. Ignoring main desktop.\n"); 89 } 90 91 /* The main desktop can't be find so try to see if another process has a proxy desktop */ 92 HWND proxy = FindWindow(PROXY_DESKTOP_CLASS, NULL); 93 if (proxy) 94 { 95 TRACE("Found proxy desktop.\n"); 96 return proxy; 97 } 98 99 return NULL; 100 } 101 102 HANDLE MakeSharedPacket(IEThreadParamBlock * threadParams, LPCWSTR strPath, int dwProcessId) 103 { 104 HNFBlock* hnfData; 105 UINT sharedBlockSize = sizeof(*hnfData); 106 UINT directoryPidlLength = 0; 107 UINT pidlSize7C = 0; 108 UINT pidlSize80 = 0; 109 UINT pathLength = 0; 110 LPITEMIDLIST pidl80 = threadParams->offset80; 111 112 // Count the total length of the message packet 113 114 // directory PIDL 115 if (threadParams->directoryPIDL) 116 { 117 directoryPidlLength = ILGetSize(threadParams->directoryPIDL); 118 sharedBlockSize += directoryPidlLength; 119 TRACE("directoryPidlLength=%d\n", directoryPidlLength); 120 } 121 122 // another PIDL 123 if (threadParams->offset7C) 124 { 125 pidlSize7C = ILGetSize(threadParams->offset7C); 126 sharedBlockSize += pidlSize7C; 127 TRACE("pidlSize7C=%d\n", pidlSize7C); 128 } 129 130 // This flag indicates the presence of another pidl? 131 if (!(threadParams->offset84 & 0x8000)) 132 { 133 if (pidl80) 134 { 135 pidlSize80 = ILGetSize(pidl80); 136 sharedBlockSize += pidlSize80; 137 TRACE("pidlSize80=%d\n", pidlSize7C); 138 } 139 } 140 else 141 { 142 TRACE("pidl80 sent by value = %p\n", pidl80); 143 pidlSize80 = 4; 144 sharedBlockSize += pidlSize80; 145 } 146 147 // The path string 148 if (strPath) 149 { 150 pathLength = 2 * lstrlenW(strPath) + 2; 151 sharedBlockSize += pathLength; 152 TRACE("pathLength=%d\n", pidlSize7C); 153 } 154 155 TRACE("sharedBlockSize=%d\n", sharedBlockSize); 156 157 // Allocate and fill the shared section 158 HANDLE hShared = SHAllocShared(0, sharedBlockSize, dwProcessId); 159 if (!hShared) 160 { 161 ERR("Shared section alloc error.\n"); 162 return 0; 163 } 164 165 PBYTE target = (PBYTE) SHLockShared(hShared, dwProcessId); 166 if (!target) 167 { 168 ERR("Shared section lock error. %d\n", GetLastError()); 169 SHFreeShared(hShared, dwProcessId); 170 return 0; 171 } 172 173 // Basic information 174 hnfData = (HNFBlock*) target; 175 hnfData->cbSize = sharedBlockSize; 176 hnfData->offset4 = (DWORD) (threadParams->dwFlags); 177 hnfData->offset8 = (DWORD) (threadParams->offset8); 178 hnfData->offsetC = (DWORD) (threadParams->offset74); 179 hnfData->offset10 = (DWORD) (threadParams->offsetD8); 180 hnfData->offset14 = (DWORD) (threadParams->offset84); 181 hnfData->offset18 = (DWORD) (threadParams->offset88); 182 hnfData->offset1C = (DWORD) (threadParams->offset8C); 183 hnfData->offset20 = (DWORD) (threadParams->offset90); 184 hnfData->offset24 = (DWORD) (threadParams->offset94); 185 hnfData->offset28 = (DWORD) (threadParams->offset98); 186 hnfData->offset2C = (DWORD) (threadParams->offset9C); 187 hnfData->offset30 = (DWORD) (threadParams->offsetA0); 188 hnfData->directoryPidlLength = 0; 189 hnfData->pidlSize7C = 0; 190 hnfData->pidlSize80 = 0; 191 hnfData->pathLength = 0; 192 target += sizeof(*hnfData); 193 194 // Copy the directory pidl contents 195 if (threadParams->directoryPIDL) 196 { 197 memcpy(target, threadParams->directoryPIDL, directoryPidlLength); 198 target += directoryPidlLength; 199 hnfData->directoryPidlLength = directoryPidlLength; 200 } 201 202 // Copy the other pidl contents 203 if (threadParams->offset7C) 204 { 205 memcpy(target, threadParams->offset7C, pidlSize7C); 206 target += pidlSize7C; 207 hnfData->pidlSize7C = pidlSize7C; 208 } 209 210 // copy the third pidl 211 if (threadParams->offset84 & 0x8000) 212 { 213 *(LPITEMIDLIST*) target = pidl80; 214 target += pidlSize80; 215 hnfData->pidlSize80 = pidlSize80; 216 } 217 else if (pidl80) 218 { 219 memcpy(target, pidl80, pidlSize80); 220 target += pidlSize80; 221 hnfData->pidlSize80 = pidlSize80; 222 } 223 224 // and finally the path string 225 if (strPath) 226 { 227 memcpy(target, strPath, pathLength); 228 hnfData->pathLength = pathLength; 229 } 230 231 SHUnlockShared(hnfData); 232 233 return hShared; 234 } 235 236 PIE_THREAD_PARAM_BLOCK ParseSharedPacket(HANDLE hData) 237 { 238 HNFBlock * hnfData; 239 PBYTE block; 240 int pid; 241 PIE_THREAD_PARAM_BLOCK params = NULL; 242 243 if (!hData) 244 goto cleanup0; 245 246 pid = GetCurrentProcessId(); 247 block = (PBYTE) SHLockShared(hData, pid); 248 249 hnfData = (HNFBlock *) block; 250 if (!block) 251 goto cleanup2; 252 253 if (hnfData->cbSize < sizeof(HNFBlock)) 254 goto cleanup2; 255 256 params = SHCreateIETHREADPARAM(0, hnfData->offset8, 0, 0); 257 if (!params) 258 goto cleanup2; 259 260 params->dwFlags = hnfData->offset4; 261 params->offset8 = hnfData->offset8; 262 params->offset74 = hnfData->offsetC; 263 params->offsetD8 = hnfData->offset10; 264 params->offset84 = hnfData->offset14; 265 params->offset88 = hnfData->offset18; 266 params->offset8C = hnfData->offset1C; 267 params->offset90 = hnfData->offset20; 268 params->offset94 = hnfData->offset24; 269 params->offset98 = hnfData->offset28; 270 params->offset9C = hnfData->offset2C; 271 params->offsetA0 = hnfData->offset30; 272 273 block += sizeof(*hnfData); 274 if (hnfData->directoryPidlLength) 275 { 276 LPITEMIDLIST pidl = NULL; 277 if (*block) 278 pidl = ILClone((LPITEMIDLIST) block); 279 params->directoryPIDL = pidl; 280 281 block += hnfData->directoryPidlLength; 282 } 283 284 if (hnfData->pidlSize7C) 285 { 286 LPITEMIDLIST pidl = NULL; 287 if (*block) 288 pidl = ILClone((LPITEMIDLIST) block); 289 params->offset7C = pidl; 290 291 block += hnfData->pidlSize80; 292 } 293 294 if (hnfData->pidlSize80) 295 { 296 if (!(params->offset84 & 0x8000)) 297 { 298 params->offset80 = *(LPITEMIDLIST *) block; 299 } 300 else 301 { 302 LPITEMIDLIST pidl = NULL; 303 if (*block) 304 pidl = ILClone((LPITEMIDLIST) block); 305 params->offset80 = pidl; 306 } 307 308 block += hnfData->pidlSize80; 309 } 310 311 if (hnfData->pathLength) 312 { 313 CComPtr<IShellFolder> psfDesktop; 314 PWSTR strPath = (PWSTR) block; 315 316 if (FAILED(SHGetDesktopFolder(&psfDesktop))) 317 { 318 params->directoryPIDL = NULL; 319 goto cleanup0; 320 } 321 322 if (FAILED(psfDesktop->ParseDisplayName(NULL, NULL, strPath, NULL, ¶ms->directoryPIDL, NULL))) 323 { 324 params->directoryPIDL = NULL; 325 goto cleanup0; 326 } 327 } 328 329 cleanup2: 330 SHUnlockShared(hnfData); 331 SHFreeShared(hData, pid); 332 333 cleanup0: 334 if (!params->directoryPIDL) 335 { 336 SHDestroyIETHREADPARAM(params); 337 return NULL; 338 } 339 340 return params; 341 } 342 343 344 static HRESULT ExplorerMessageLoop(IEThreadParamBlock * parameters) 345 { 346 HRESULT hResult; 347 MSG Msg; 348 BOOL Ret; 349 350 // Tell the thread ref we are using it. 351 if (parameters && parameters->offsetF8) 352 parameters->offsetF8->AddRef(); 353 354 /* Handle /e parameter */ 355 UINT wFlags = 0; 356 if ((parameters->dwFlags & SH_EXPLORER_CMDLINE_FLAG_E)) 357 wFlags |= SBSP_EXPLOREMODE; 358 359 /* Handle /select parameter */ 360 PUITEMID_CHILD pidlSelect = NULL; 361 if ((parameters->dwFlags & SH_EXPLORER_CMDLINE_FLAG_SELECT) && 362 (ILGetNext(parameters->directoryPIDL) != NULL)) 363 { 364 pidlSelect = ILClone(ILFindLastID(parameters->directoryPIDL)); 365 ILRemoveLastID(parameters->directoryPIDL); 366 } 367 368 CComPtr<IShellBrowser> psb; 369 hResult = CShellBrowser_CreateInstance(IID_PPV_ARG(IShellBrowser, &psb)); 370 if (FAILED_UNEXPECTEDLY(hResult)) 371 return hResult; 372 373 hResult = psb->BrowseObject(parameters->directoryPIDL, wFlags); 374 if (FAILED_UNEXPECTEDLY(hResult)) 375 return hResult; 376 377 if (pidlSelect != NULL) 378 { 379 CComPtr<IShellView> shellView; 380 hResult = psb->QueryActiveShellView(&shellView); 381 if (SUCCEEDED(hResult)) 382 { 383 shellView->SelectItem(pidlSelect, SVSI_SELECT | SVSI_FOCUSED | SVSI_ENSUREVISIBLE); 384 } 385 ILFree(pidlSelect); 386 } 387 388 CComPtr<IBrowserService2> browser; 389 hResult = psb->QueryInterface(IID_PPV_ARG(IBrowserService2, &browser)); 390 if (FAILED_UNEXPECTEDLY(hResult)) 391 return hResult; 392 393 psb.Release(); 394 395 while ((Ret = GetMessage(&Msg, NULL, 0, 0)) != 0) 396 { 397 if (Ret == -1) 398 { 399 // Error: continue or exit? 400 break; 401 } 402 403 if (Msg.message == WM_QUIT) 404 break; 405 406 if (browser->v_MayTranslateAccelerator(&Msg) != S_OK) 407 { 408 TranslateMessage(&Msg); 409 DispatchMessage(&Msg); 410 } 411 } 412 413 int nrc = browser->Release(); 414 if (nrc > 0) 415 { 416 DbgPrint("WARNING: There are %d references to the CShellBrowser active or leaked.\n", nrc); 417 } 418 419 browser.Detach(); 420 421 // Tell the thread ref we are not using it anymore. 422 if (parameters && parameters->offsetF8) 423 parameters->offsetF8->Release(); 424 425 return hResult; 426 } 427 428 static DWORD WINAPI BrowserThreadProc(LPVOID lpThreadParameter) 429 { 430 IEThreadParamBlock * parameters = (IEThreadParamBlock *) lpThreadParameter; 431 432 OleInitialize(NULL); 433 ExplorerMessageLoop(parameters); 434 435 /* Destroying the parameters releases the thread reference */ 436 SHDestroyIETHREADPARAM(parameters); 437 438 /* Wake up the proxy desktop thread so it can check whether the last browser thread exited */ 439 /* Use PostMessage in order to force GetMessage to return and check if all browser windows have exited */ 440 PostMessageW(FindShellProxy(NULL), WM_EXPLORER_1037, 0, 0); 441 442 OleUninitialize(); 443 444 return 0; 445 } 446 447 /************************************************************************* 448 * SHCreateIETHREADPARAM [BROWSEUI.123] 449 */ 450 extern "C" IEThreadParamBlock *WINAPI SHCreateIETHREADPARAM( 451 long param8, long paramC, IUnknown *param10, IUnknown *param14) 452 { 453 IEThreadParamBlock *result; 454 455 TRACE("SHCreateIETHREADPARAM\n"); 456 457 result = (IEThreadParamBlock *) LocalAlloc(LMEM_ZEROINIT, sizeof(*result)); 458 if (result == NULL) 459 return NULL; 460 result->offset0 = param8; 461 result->offset8 = paramC; 462 result->offsetC = param10; 463 if (param10 != NULL) 464 param10->AddRef(); 465 result->offset14 = param14; 466 if (param14 != NULL) 467 param14->AddRef(); 468 return result; 469 } 470 471 /************************************************************************* 472 * SHCloneIETHREADPARAM [BROWSEUI.124] 473 */ 474 extern "C" IEThreadParamBlock *WINAPI SHCloneIETHREADPARAM(IEThreadParamBlock *param) 475 { 476 IEThreadParamBlock *result; 477 478 TRACE("SHCloneIETHREADPARAM\n"); 479 480 result = (IEThreadParamBlock *) LocalAlloc(LMEM_FIXED, sizeof(*result)); 481 if (result == NULL) 482 return NULL; 483 *result = *param; 484 if (result->directoryPIDL != NULL) 485 result->directoryPIDL = ILClone(result->directoryPIDL); 486 if (result->offset7C != NULL) 487 result->offset7C = ILClone(result->offset7C); 488 if (result->offset80 != NULL) 489 result->offset80 = ILClone(result->offset80); 490 if (result->offset70 != NULL) 491 result->offset70->AddRef(); 492 #if 0 493 if (result->offsetC != NULL) 494 result->offsetC->Method2C(); 495 #endif 496 return result; 497 } 498 499 /************************************************************************* 500 * SHDestroyIETHREADPARAM [BROWSEUI.126] 501 */ 502 extern "C" void WINAPI SHDestroyIETHREADPARAM(IEThreadParamBlock *param) 503 { 504 TRACE("SHDestroyIETHREADPARAM\n"); 505 506 if (param == NULL) 507 return; 508 if (param->directoryPIDL != NULL) 509 ILFree(param->directoryPIDL); 510 if (param->offset7C != NULL) 511 ILFree(param->offset7C); 512 if ((param->dwFlags & 0x80000) == 0 && param->offset80 != NULL) 513 ILFree(param->offset80); 514 if (param->offset14 != NULL) 515 param->offset14->Release(); 516 if (param->offset70 != NULL) 517 param->offset70->Release(); 518 if (param->offset78 != NULL) 519 param->offset78->Release(); 520 if (param->offsetC != NULL) 521 param->offsetC->Release(); 522 if (param->offsetF8 != NULL) 523 param->offsetF8->Release(); 524 LocalFree(param); 525 } 526 527 /************************************************************************* 528 * SHOnCWMCommandLine [BROWSEUI.127] 529 */ 530 extern "C" BOOL WINAPI SHOnCWMCommandLine(HANDLE hSharedInfo) 531 { 532 TRACE("SHOnCWMCommandLine\n"); 533 534 PIE_THREAD_PARAM_BLOCK params = ParseSharedPacket(hSharedInfo); 535 536 if (params) 537 return SHOpenFolderWindow(params); 538 539 SHDestroyIETHREADPARAM(params); 540 541 return FALSE; 542 } 543 544 /************************************************************************* 545 * SHOpenFolderWindow [BROWSEUI.102] 546 * see SHOpenNewFrame below for remarks 547 */ 548 extern "C" HRESULT WINAPI SHOpenFolderWindow(PIE_THREAD_PARAM_BLOCK parameters) 549 { 550 HANDLE threadHandle; 551 DWORD threadID; 552 553 // Only try to convert the pidl when it is going to be printed 554 if (TRACE_ON(browseui)) 555 { 556 WCHAR debugStr[MAX_PATH + 2] = { 0 }; 557 if (!ILGetDisplayName(parameters->directoryPIDL, debugStr)) 558 { 559 debugStr[0] = UNICODE_NULL; 560 } 561 TRACE("SHOpenFolderWindow %p(%S)\n", parameters->directoryPIDL, debugStr); 562 } 563 564 PIE_THREAD_PARAM_BLOCK paramsCopy = SHCloneIETHREADPARAM(parameters); 565 566 SHGetInstanceExplorer(&(paramsCopy->offsetF8)); 567 threadHandle = CreateThread(NULL, 0x10000, BrowserThreadProc, paramsCopy, 0, &threadID); 568 if (threadHandle != NULL) 569 { 570 CloseHandle(threadHandle); 571 return S_OK; 572 } 573 SHDestroyIETHREADPARAM(paramsCopy); 574 return E_FAIL; 575 } 576 577 // 75FA56C1h 578 // (pidl, 0, -1, 1) 579 // this function should handle creating a new process if needed, but I'm leaving that out for now 580 // this function always opens a new window - it does NOT check for duplicates 581 /************************************************************************* 582 * SHOpenNewFrame [BROWSEUI.103] 583 */ 584 extern "C" HRESULT WINAPI SHOpenNewFrame(LPITEMIDLIST pidl, IUnknown *paramC, long param10, DWORD dwFlags) 585 { 586 IEThreadParamBlock *parameters; 587 588 TRACE("SHOpenNewFrame\n"); 589 590 parameters = SHCreateIETHREADPARAM(0, 1, paramC, NULL); 591 if (parameters == NULL) 592 { 593 ILFree(pidl); 594 return E_OUTOFMEMORY; 595 } 596 if (paramC != NULL) 597 parameters->offset10 = param10; 598 parameters->directoryPIDL = pidl; 599 parameters->dwFlags = dwFlags; 600 601 HRESULT hr = SHOpenFolderWindow(parameters); 602 603 SHDestroyIETHREADPARAM(parameters); 604 605 return hr; 606 } 607 608 /************************************************************************* 609 * SHCreateFromDesktop [BROWSEUI.106] 610 * parameter is a FolderInfo 611 */ 612 BOOL WINAPI SHCreateFromDesktop(ExplorerCommandLineParseResults * parseResults) 613 { 614 TRACE("SHCreateFromDesktop\n"); 615 616 IEThreadParamBlock * parameters = SHCreateIETHREADPARAM(0, 0, 0, 0); 617 if (!parameters) 618 return FALSE; 619 620 PCWSTR strPath = NULL; 621 if (parseResults->dwFlags & SH_EXPLORER_CMDLINE_FLAG_STRING) 622 { 623 if (parseResults->pidlPath) 624 { 625 WARN("strPath and pidlPath are both assigned. This shouldn't happen.\n"); 626 } 627 628 strPath = parseResults->strPath; 629 } 630 631 parameters->dwFlags = parseResults->dwFlags; 632 parameters->offset8 = parseResults->nCmdShow; 633 634 LPITEMIDLIST pidl = parseResults->pidlPath ? ILClone(parseResults->pidlPath) : NULL; 635 if (!pidl && parseResults->dwFlags & SH_EXPLORER_CMDLINE_FLAG_STRING) 636 { 637 if (parseResults->strPath && parseResults->strPath[0]) 638 { 639 pidl = ILCreateFromPathW(parseResults->strPath); 640 } 641 } 642 643 // HACK! This shouldn't happen! SHExplorerParseCmdLine needs fixing. 644 if (!pidl) 645 { 646 SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, NULL, &pidl); 647 } 648 649 parameters->directoryPIDL = pidl; 650 651 // Try to find the owner of the idlist, if we aren't running /SEPARATE 652 HWND desktop = NULL; 653 if (!(parseResults->dwFlags & SH_EXPLORER_CMDLINE_FLAG_SEPARATE)) 654 desktop = FindShellProxy(parameters->directoryPIDL); 655 656 // If found, ask it to open the new window 657 if (desktop) 658 { 659 TRACE("Found desktop hwnd=%p\n", desktop); 660 661 DWORD dwProcessId; 662 663 GetWindowThreadProcessId(desktop, &dwProcessId); 664 AllowSetForegroundWindow(dwProcessId); 665 666 HANDLE hShared = MakeSharedPacket(parameters, strPath, dwProcessId); 667 if (hShared) 668 { 669 TRACE("Sending open message...\n"); 670 671 PostMessageW(desktop, WM_EXPLORER_OPEN_NEW_WINDOW, 0, (LPARAM) hShared); 672 } 673 674 SHDestroyIETHREADPARAM(parameters); 675 return TRUE; 676 } 677 678 TRACE("Desktop not found or separate flag requested.\n"); 679 680 // Else, start our own message loop! 681 HRESULT hr = CoInitialize(NULL); 682 CProxyDesktop * proxy = new CProxyDesktop(parameters); 683 if (proxy) 684 { 685 g_hwndProxyDesktop = proxy->Create(0); 686 687 LONG refCount; 688 CComPtr<IUnknown> thread; 689 if (SHCreateThreadRef(&refCount, &thread) >= 0) 690 { 691 SHSetInstanceExplorer(thread); 692 if (strPath) 693 parameters->directoryPIDL = ILCreateFromPath(strPath); 694 SHOpenFolderWindow(parameters); 695 parameters = NULL; 696 thread.Release(); 697 } 698 699 MSG Msg; 700 while (GetMessageW(&Msg, 0, 0, 0) && refCount) 701 { 702 TranslateMessage(&Msg); 703 DispatchMessageW(&Msg); 704 } 705 706 DestroyWindow(g_hwndProxyDesktop); 707 708 delete proxy; 709 } 710 711 if (SUCCEEDED(hr)) 712 CoUninitialize(); 713 714 SHDestroyIETHREADPARAM(parameters); 715 716 return TRUE; 717 } 718