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