Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

853 lines
24 KiB

  1. //--------------------------------------------------------------------------
  2. // Manage the windows list, such that we can get the IDispatch for each of
  3. // the shell windows to be marshalled to different processes
  4. //---------------------------------------------------------------------------
  5. //---------------------------------------------------------------------------
  6. // Includes...
  7. #include "priv.h"
  8. #include "sccls.h"
  9. #include <varutil.h>
  10. #include "winlist.h"
  11. #include "iedde.h"
  12. #define DM_WINLIST 0
  13. void IEInitializeClassFactoryObject(IUnknown* punkAuto);
  14. void IERevokeClassFactoryObject(void);
  15. class CShellWindowListCF : public IClassFactory
  16. {
  17. public:
  18. // IUnKnown
  19. STDMETHODIMP QueryInterface(REFIID, void **);
  20. STDMETHODIMP_(ULONG) AddRef(void);
  21. STDMETHODIMP_(ULONG) Release(void);
  22. // IClassFactory
  23. STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
  24. STDMETHODIMP LockServer(BOOL fLock);
  25. // constructor
  26. CShellWindowListCF();
  27. BOOL Init(void);
  28. protected:
  29. ~CShellWindowListCF();
  30. // locals
  31. LONG _cRef;
  32. IShellWindows *_pswWinList;
  33. };
  34. DWORD g_dwWinListCFRegister = 0;
  35. DWORD g_fWinListRegistered = FALSE; // Only used in browser only mode...
  36. IShellWindows *g_pswWinList = NULL;
  37. // Function to get called by the tray to create the global window list and register
  38. // it with the system
  39. //=================================== Class Factory implemention ========================
  40. CShellWindowListCF::CShellWindowListCF()
  41. {
  42. _cRef = 1;
  43. DllAddRef();
  44. }
  45. BOOL CShellWindowListCF::Init()
  46. {
  47. HRESULT hr = CSDWindows_CreateInstance(&_pswWinList);
  48. g_pswWinList = _pswWinList;
  49. // First see if there already is one defined...
  50. if (FAILED(hr))
  51. {
  52. TraceMsg(DM_WINLIST, "WinList_Init CoCreateInstance Failed: %x", hr);
  53. return FALSE;
  54. }
  55. // And register our class factory with the system...
  56. hr = CoRegisterClassObject(CLSID_ShellWindows, this,
  57. CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER,
  58. REGCLS_MULTIPLEUSE, &g_dwWinListCFRegister);
  59. // this call governs when we will call CoRevoke on the CF
  60. if (SUCCEEDED(hr) && g_pswWinList)
  61. g_pswWinList->ProcessAttachDetach(TRUE);
  62. // Create an instance of the underlying window list class...
  63. TraceMsg(DM_WINLIST, "WinList_Init CoRegisterClass: %x", hr);
  64. return SUCCEEDED(hr);
  65. }
  66. CShellWindowListCF::~CShellWindowListCF()
  67. {
  68. if (_pswWinList)
  69. {
  70. g_pswWinList = NULL;
  71. _pswWinList->Release();
  72. }
  73. DllRelease();
  74. }
  75. STDMETHODIMP CShellWindowListCF::QueryInterface(REFIID riid, void **ppvObj)
  76. {
  77. static const QITAB qit[] = {
  78. QITABENT(CShellWindowListCF, IClassFactory), // IID_IClassFactory
  79. { 0 },
  80. };
  81. return QISearch(this, qit, riid, ppvObj);
  82. }
  83. STDMETHODIMP_(ULONG) CShellWindowListCF::AddRef()
  84. {
  85. return InterlockedIncrement(&_cRef);
  86. }
  87. STDMETHODIMP_(ULONG) CShellWindowListCF::Release()
  88. {
  89. if (InterlockedDecrement(&_cRef))
  90. return _cRef;
  91. delete this;
  92. return 0;
  93. }
  94. STDMETHODIMP CShellWindowListCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)
  95. {
  96. // aggregation checking is done in class factory
  97. // For now simply use our QueryService to get the dispatch.
  98. // this will do all of the things to create it and the like.
  99. if (!_pswWinList)
  100. {
  101. ASSERT(0);
  102. return E_FAIL;
  103. }
  104. return _pswWinList->QueryInterface(riid, ppvObj);
  105. }
  106. STDMETHODIMP CShellWindowListCF::LockServer(BOOL fLock)
  107. {
  108. return S_OK; // we don't do anything with this...
  109. }
  110. // As this is marshalled over to the main shell process hopefully this will take care of
  111. // most of the serialization problems. Probably still need a way to handle the case better
  112. // where a window is coming up at the same time the last one is going down...
  113. STDAPI CWinListShellProc_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  114. {
  115. *ppunk = NULL;
  116. if (g_dwWinListCFRegister)
  117. return CO_E_OBJISREG;
  118. CShellWindowListCF *pswWinList = new CShellWindowListCF;
  119. if (pswWinList)
  120. {
  121. pswWinList->Init(); // tell it to initialize
  122. *ppunk = SAFECAST(pswWinList, IUnknown *);
  123. return S_OK;
  124. }
  125. return E_OUTOFMEMORY;
  126. }
  127. BOOL WinList_Init(void)
  128. {
  129. // Create our clas factory to register out there...
  130. TraceMsg(DM_WINLIST, "WinList_Init called");
  131. //
  132. // If this is not a browser-only install. Register the class factory
  133. // object now with no instance. Otherwise, do it when the first instance
  134. // is created (see shbrowse.cpp).
  135. //
  136. if (!g_fBrowserOnlyProcess)
  137. {
  138. //
  139. // First, register the class factory object for CLSID_InternetExplorer.
  140. // Note that we pass NULL indicating that subsequent CreateInstance
  141. // should simply create a new instance.
  142. //
  143. IEInitializeClassFactoryObject(NULL);
  144. CShellWindowListCF *pswWinList = new CShellWindowListCF;
  145. if (pswWinList)
  146. {
  147. BOOL fRetVal = pswWinList->Init(); // tell it to initialize
  148. pswWinList->Release(); // Release our handle hopefully init registered
  149. //
  150. // Initialize IEDDE.
  151. //
  152. if (!IsBrowseNewProcessAndExplorer())
  153. IEDDE_Initialize();
  154. return fRetVal;
  155. }
  156. }
  157. else
  158. {
  159. //
  160. // Initialize IEDDE. - Done before cocreate below for timing issues
  161. //
  162. IEDDE_Initialize();
  163. // All of the main processing moved to first call to WinList_GetShellWindows
  164. // as the creating of OLE object across processes messed up DDE timing.
  165. return TRUE;
  166. }
  167. return FALSE;
  168. }
  169. #ifdef UNIX
  170. HRESULT CoCreateShellWindows(REFCLSID rclsid, IUnknown *pUnkOuter, DWORD dwClsContext, REFIID riid, void **ppv)
  171. {
  172. HRESULT hr;
  173. if (!g_pswWinList)
  174. {
  175. hr = CSDWindows_CreateInstance(&g_pswWinList);
  176. if (FAILED(hr))
  177. {
  178. return E_FAIL;
  179. }
  180. }
  181. hr = g_pswWinList->QueryInterface(riid, ppv);
  182. if (SUCCEEDED(hr))
  183. {
  184. g_dwWinListCFRegister = 1;
  185. }
  186. return hr;
  187. }
  188. #endif
  189. // Helper function to get the ShellWindows Object
  190. IShellWindows* WinList_GetShellWindows(BOOL fForceMarshalled)
  191. {
  192. IShellWindows *psw;
  193. if (fForceMarshalled)
  194. psw = NULL;
  195. else
  196. psw = g_pswWinList;
  197. if (psw)
  198. {
  199. // Optimize the inter-thread case by using the global WinList,
  200. // this makes opening folders much faster.
  201. psw->AddRef();
  202. }
  203. else
  204. {
  205. SHCheckRegistry();
  206. #ifndef NO_RPCSS_ON_UNIX
  207. HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL,
  208. CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER,
  209. IID_PPV_ARG(IShellWindows, &psw));
  210. #else
  211. HRESULT hr = CoCreateShellWindows(CLSID_ShellWindows, NULL,
  212. CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER,
  213. IID_PPV_ARG(IShellWindows, &psw));
  214. #endif
  215. if ( (g_fBrowserOnlyProcess || !IsInternetExplorerApp()) && !g_fWinListRegistered)
  216. {
  217. // If it failed and we are not funning in integrated mode, and this is the
  218. // first time for this process, we should then register the Window List with
  219. // the shell process. We moved that from WinList_Init as that caused us to
  220. // do interprocess send/post messages to early which caused DDE to break...
  221. g_fWinListRegistered = TRUE; // only call once
  222. if (FAILED(hr))
  223. {
  224. SHLoadInProc(CLSID_WinListShellProc);
  225. hr = CoCreateInstance(CLSID_ShellWindows, NULL,
  226. CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER,
  227. IID_PPV_ARG(IShellWindows, &psw));
  228. }
  229. if (psw)
  230. {
  231. psw->ProcessAttachDetach(TRUE);
  232. }
  233. }
  234. // hr == REGDB_E_CLASSNOTREG when the shell process isn't running.
  235. // hr == RPC_E_CANTCALLOUT_ININPUTSYNCCALL happens durring DDE launch of IE.
  236. // should investigate, but removing assert for IE5 ship.
  237. if (!(SUCCEEDED(hr) || hr == REGDB_E_CLASSNOTREG || hr == RPC_E_CANTCALLOUT_ININPUTSYNCCALL))
  238. {
  239. TraceMsg(TF_WARNING,
  240. "WinList_GetShellWindows CoCreateInst(CLSID_ShellWindows) failed %x", hr);
  241. }
  242. }
  243. return psw;
  244. }
  245. // Function to terminate our use of the window list.
  246. void WinList_Terminate(void)
  247. {
  248. // Lets release everything in a thread safe way...
  249. TraceMsg(DM_WINLIST, "WinList_Terminate called");
  250. IEDDE_Uninitialize();
  251. // Release our usage of the object to allow the system to clean it up
  252. if (!g_fBrowserOnlyProcess)
  253. {
  254. // this is the explorer process, and we control the vertical
  255. if (g_dwWinListCFRegister) {
  256. IShellWindows* psw = WinList_GetShellWindows(FALSE);
  257. if (psw)
  258. {
  259. #ifdef DEBUG
  260. long cwindow = -1;
  261. psw->get_Count(&cwindow);
  262. //ASSERT(cwindow==0);
  263. if (cwindow != 0)
  264. TraceMsg(DM_ERROR, "wl_t: cwindow=%d (!=0)", cwindow);
  265. #endif
  266. psw->ProcessAttachDetach(FALSE);
  267. psw->Release();
  268. }
  269. // the processattachdetach() should kill the CF in our process
  270. if (g_dwWinListCFRegister != 0)
  271. TraceMsg(DM_ERROR, "wl_t: g_dwWinListCFRegister=%d (!=0)", g_dwWinListCFRegister);
  272. }
  273. IERevokeClassFactoryObject();
  274. CUrlHistory_CleanUp();
  275. }
  276. else
  277. {
  278. if (g_fWinListRegistered)
  279. {
  280. // only do this if we actually registered...
  281. IShellWindows* psw = WinList_GetShellWindows(TRUE);
  282. if (psw)
  283. {
  284. psw->ProcessAttachDetach(FALSE); // Tell it we are going away...
  285. psw->Release();
  286. }
  287. }
  288. }
  289. #ifdef UNIX
  290. if (g_pswWinList)
  291. {
  292. g_pswWinList->Release();
  293. }
  294. #endif
  295. }
  296. // chrisfra 10/17/96 - mike schmidt needs to look at why delayed
  297. // register causes death in OleUnitialize accessing freed vtable
  298. // in winlist, under ifdef, i've also made WinList_GetShellWindows ignore
  299. // BOOLEAN parameter
  300. STDAPI WinList_Revoke(long dwRegister)
  301. {
  302. #ifndef DELAY_REGISTER
  303. IShellWindows* psw = WinList_GetShellWindows(TRUE);
  304. #else
  305. IShellWindows* psw = WinList_GetShellWindows(FALSE);
  306. #endif
  307. HRESULT hr = E_FAIL;
  308. TraceMsg(DM_WINLIST, "WinList_Reevoke called on %x", dwRegister);
  309. if (psw)
  310. {
  311. hr = psw->Revoke((long)dwRegister);
  312. if (FAILED(hr))
  313. {
  314. TraceMsg(TF_WARNING,
  315. "WinList_Revoke(%x) failed. hresult = %x", dwRegister, hr);
  316. }
  317. psw->Release();
  318. }
  319. return hr;
  320. }
  321. STDAPI WinList_NotifyNewLocation(IShellWindows* psw, long dwRegister, LPCITEMIDLIST pidl)
  322. {
  323. HRESULT hr = E_UNEXPECTED;
  324. if (pidl)
  325. {
  326. VARIANT var;
  327. hr = InitVariantFromIDList(&var, pidl);
  328. if (SUCCEEDED(hr))
  329. {
  330. hr = psw->OnNavigate(dwRegister, &var);
  331. VariantClearLazy(&var);
  332. }
  333. }
  334. return hr;
  335. }
  336. // Register with the window list that we have a pidl that we are starting up.
  337. STDAPI WinList_RegisterPending(DWORD dwThread, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlRoot, long *pdwRegister)
  338. {
  339. HRESULT hr = E_UNEXPECTED;
  340. ASSERT(!pidlRoot);
  341. if (pidl)
  342. {
  343. IShellWindows* psw = WinList_GetShellWindows(FALSE);
  344. if (psw)
  345. {
  346. VARIANT var;
  347. hr = InitVariantFromIDList(&var, pidl);
  348. if (SUCCEEDED(hr))
  349. {
  350. hr = psw->RegisterPending(dwThread, &var, PVAREMPTY, SWC_BROWSER, pdwRegister);
  351. VariantClearLazy(&var);
  352. }
  353. }
  354. }
  355. return hr;
  356. }
  357. /*
  358. * PERFORMANCE note - getting back the automation object (ppauto) is really
  359. * expensive due to the marshalling overhead. Don't query for it unless you
  360. * absolutely need it!
  361. */
  362. STDAPI WinList_FindFolderWindow(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlRoot, HWND *phwnd, IWebBrowserApp **ppauto)
  363. {
  364. HRESULT hr = E_UNEXPECTED;
  365. ASSERT(!pidlRoot);
  366. if (ppauto)
  367. *ppauto = NULL;
  368. if (phwnd)
  369. *phwnd = NULL;
  370. if (pidl)
  371. {
  372. // Try a cached psw if we don't need ppauto
  373. IShellWindows* psw = WinList_GetShellWindows(ppauto != NULL);
  374. if (psw)
  375. {
  376. VARIANT var;
  377. hr = InitVariantFromIDList(&var, pidl);
  378. if (SUCCEEDED(hr))
  379. {
  380. IDispatch* pdisp = NULL;
  381. hr = psw->FindWindowSW(&var, PVAREMPTY, SWC_BROWSER, (long *)phwnd,
  382. ppauto ? (SWFO_NEEDDISPATCH | SWFO_INCLUDEPENDING) : SWFO_INCLUDEPENDING,
  383. &pdisp);
  384. if (pdisp)
  385. {
  386. // if this fails it's because we are inside SendMessage loop and ole doesn't like it
  387. if (ppauto)
  388. hr = pdisp->QueryInterface(IID_PPV_ARG(IWebBrowserApp, ppauto));
  389. pdisp->Release();
  390. }
  391. VariantClearLazy(&var);
  392. }
  393. psw->Release();
  394. }
  395. }
  396. return hr;
  397. }
  398. // Support for Being able to open a folder and get it's idispatch...
  399. //
  400. class CWaitForWindow
  401. {
  402. public:
  403. ULONG AddRef(void);
  404. ULONG Release(void);
  405. BOOL Init(IShellWindows *psw, LPCITEMIDLIST pidl, DWORD dwPending);
  406. void CleanUp(void);
  407. HRESULT WaitForWindowToOpen(DWORD dwTimeout);
  408. CWaitForWindow(void);
  409. private:
  410. ~CWaitForWindow(void);
  411. // internal class to watch for events...
  412. class CWindowEvents : public DShellWindowsEvents
  413. {
  414. public:
  415. // IUnknown
  416. STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
  417. STDMETHODIMP_(ULONG) AddRef(void) ;
  418. STDMETHODIMP_(ULONG) Release(void);
  419. // IDispatch
  420. STDMETHOD(GetTypeInfoCount)(THIS_ UINT * pctinfo);
  421. STDMETHOD(GetTypeInfo)(THIS_ UINT itinfo, LCID lcid, ITypeInfo * * pptinfo);
  422. STDMETHOD(GetIDsOfNames)(THIS_ REFIID riid, OLECHAR * * rgszNames,
  423. UINT cNames, LCID lcid, DISPID * rgdispid);
  424. STDMETHOD(Invoke)(THIS_ DISPID dispidMember, REFIID riid,
  425. LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult,
  426. EXCEPINFO * pexcepinfo, UINT * puArgErr);
  427. } m_EventHandler;
  428. friend class CWindowEvents;
  429. LONG m_cRef;
  430. DWORD m_dwCookie;
  431. IShellWindows *m_psw;
  432. IConnectionPoint *m_picp;
  433. DWORD m_dwPending;
  434. LPITEMIDLIST m_pidl;
  435. HANDLE m_hevent;
  436. BOOL m_fAdvised;
  437. };
  438. ULONG CWaitForWindow::AddRef(void)
  439. {
  440. return InterlockedIncrement(&m_cRef);
  441. }
  442. ULONG CWaitForWindow::Release(void)
  443. {
  444. if (InterlockedDecrement(&m_cRef))
  445. return m_cRef;
  446. delete this;
  447. return 0;
  448. }
  449. CWaitForWindow::CWaitForWindow(void) : m_cRef(1)
  450. {
  451. ASSERT(m_psw == NULL);
  452. ASSERT(m_picp == NULL);
  453. ASSERT(m_hevent == NULL);
  454. ASSERT(m_dwCookie == 0);
  455. ASSERT(m_fAdvised == FALSE);
  456. }
  457. CWaitForWindow::~CWaitForWindow(void)
  458. {
  459. ATOMICRELEASE(m_psw);
  460. CleanUp();
  461. if (m_hevent)
  462. CloseHandle(m_hevent);
  463. if (m_pidl)
  464. ILFree(m_pidl);
  465. }
  466. BOOL CWaitForWindow::Init(IShellWindows *psw, LPCITEMIDLIST pidl, DWORD dwPending)
  467. {
  468. // First try to create an event object
  469. m_hevent = CreateEvent(NULL, TRUE, FALSE, NULL);
  470. if (!m_hevent)
  471. return FALSE;
  472. // We do not have a window or it is pending...
  473. // first lets setup that we want to be notified of new windows.
  474. if (FAILED(ConnectToConnectionPoint(SAFECAST(&m_EventHandler, IDispatch*), DIID_DShellWindowsEvents, TRUE, psw, &m_dwCookie, &m_picp)))
  475. return FALSE;
  476. // Save away passed in stuff that we care about.
  477. m_psw = psw;
  478. psw->AddRef();
  479. m_pidl = ILClone(pidl);
  480. m_dwPending = dwPending;
  481. return TRUE;
  482. }
  483. void CWaitForWindow::CleanUp(void)
  484. {
  485. // Don't need to listen anmore.
  486. if (m_dwCookie)
  487. {
  488. m_picp->Unadvise(m_dwCookie);
  489. m_dwCookie = 0;
  490. }
  491. ATOMICRELEASE(m_picp);
  492. }
  493. HRESULT CWaitForWindow::WaitForWindowToOpen(DWORD dwTimeOut)
  494. {
  495. if (!m_hevent || !m_dwCookie)
  496. return E_FAIL;
  497. ENTERCRITICAL;
  498. if (!m_fAdvised)
  499. ResetEvent(m_hevent);
  500. LEAVECRITICAL;
  501. DWORD dwStart = GetTickCount();
  502. DWORD dwWait = dwTimeOut;
  503. DWORD dwWaitResult;
  504. do
  505. {
  506. dwWaitResult = MsgWaitForMultipleObjects(1, &m_hevent, FALSE, // fWaitAll, wait for any one
  507. dwWait, QS_ALLINPUT);
  508. // Check if we are signaled for a send message.
  509. if (dwWaitResult != WAIT_OBJECT_0 + 1)
  510. {
  511. break; // No. Break out of the loop.
  512. }
  513. // We may need to dispatch stuff here.
  514. MSG msg;
  515. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  516. {
  517. TranslateMessage(&msg);
  518. DispatchMessage(&msg);
  519. }
  520. // than MSEC_MAXWAIT if we wait more than that.
  521. dwWait = dwStart+dwTimeOut - GetTickCount();
  522. } while (dwWait <= dwTimeOut);
  523. BOOL fAdvised;
  524. {
  525. ENTERCRITICAL;
  526. fAdvised = m_fAdvised;
  527. m_fAdvised = FALSE;
  528. LEAVECRITICAL;
  529. }
  530. return fAdvised ? S_OK : E_FAIL;
  531. }
  532. STDMETHODIMP CWaitForWindow::CWindowEvents::QueryInterface(REFIID riid, void **ppv)
  533. {
  534. static const QITAB qit[] = {
  535. QITABENTMULTI2(CWaitForWindow::CWindowEvents, DIID_DShellWindowsEvents, DShellWindowsEvents),
  536. QITABENTMULTI(CWaitForWindow::CWindowEvents, IDispatch, DShellWindowsEvents),
  537. { 0 },
  538. };
  539. return QISearch(this, qit, riid, ppv);
  540. }
  541. ULONG CWaitForWindow::CWindowEvents::AddRef(void)
  542. {
  543. CWaitForWindow* pdfwait = IToClass(CWaitForWindow, m_EventHandler, this);
  544. return pdfwait->AddRef();
  545. }
  546. ULONG CWaitForWindow::CWindowEvents::Release(void)
  547. {
  548. CWaitForWindow* pdfwait = IToClass(CWaitForWindow, m_EventHandler, this);
  549. return pdfwait->Release();
  550. }
  551. HRESULT CWaitForWindow::CWindowEvents::GetTypeInfoCount(UINT *pctinfo)
  552. {
  553. return E_NOTIMPL;
  554. }
  555. HRESULT CWaitForWindow::CWindowEvents::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  556. {
  557. return E_NOTIMPL;
  558. }
  559. HRESULT CWaitForWindow::CWindowEvents::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
  560. {
  561. return E_NOTIMPL;
  562. }
  563. HRESULT CWaitForWindow::CWindowEvents::Invoke(DISPID dispid, REFIID riid,
  564. LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult,
  565. EXCEPINFO * pexcepinfo, UINT * puArgErr)
  566. {
  567. CWaitForWindow* pdfwait = IToClass(CWaitForWindow, m_EventHandler, this);
  568. if (dispid == DISPID_WINDOWREGISTERED)
  569. {
  570. ENTERCRITICAL;
  571. // Signal the event
  572. pdfwait->m_fAdvised = TRUE;
  573. ::SetEvent(pdfwait->m_hevent);
  574. LEAVECRITICAL;
  575. }
  576. return S_OK;
  577. }
  578. // WARNING:: this assumes not rooted
  579. STDAPI SHGetIDispatchForFolder(LPCITEMIDLIST pidl, IWebBrowserApp **ppauto)
  580. {
  581. HRESULT hr = E_UNEXPECTED;
  582. if (ppauto)
  583. *ppauto = NULL;
  584. if (!pidl)
  585. return E_POINTER;
  586. // Try a cached psw if we don't need ppauto
  587. IShellWindows* psw = WinList_GetShellWindows(ppauto != NULL);
  588. if (psw)
  589. {
  590. VARIANT var;
  591. hr = InitVariantFromIDList(&var, pidl);
  592. if (SUCCEEDED(hr))
  593. {
  594. LONG lhwnd;
  595. IDispatch* pdisp;
  596. hr = psw->FindWindowSW(&var, PVAREMPTY, SWC_BROWSER, &lhwnd,
  597. ppauto ? (SWFO_NEEDDISPATCH | SWFO_INCLUDEPENDING) : SWFO_INCLUDEPENDING,
  598. &pdisp);
  599. if ((hr == E_PENDING) || (hr == S_FALSE))
  600. {
  601. HRESULT hrOld = hr;
  602. hr = E_FAIL;
  603. CWaitForWindow *pdfwait = new CWaitForWindow(); // Setup a wait object...
  604. if (pdfwait)
  605. {
  606. if (pdfwait->Init(psw, pidl, 0))
  607. {
  608. if (hrOld == S_FALSE)
  609. {
  610. // Startup opening a new window
  611. SHELLEXECUTEINFO sei = {sizeof(sei)};
  612. sei.lpIDList = (void *)pidl;
  613. //
  614. // WARNING - old versions of ShellExec() didnt pay attention - ZekeL - 30-DEC-98
  615. // to whether the hwnd is in the same process or not,
  616. // and so could fault in TryDDEShortcut().
  617. // only pass the hwnd if the shell window shares
  618. // the same process.
  619. //
  620. sei.hwnd = GetShellWindow();
  621. DWORD idProcess;
  622. GetWindowThreadProcessId(sei.hwnd, &idProcess);
  623. if (idProcess != GetCurrentProcessId())
  624. sei.hwnd = NULL;
  625. // Everything should have been initialize to NULL(0)
  626. sei.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_DDEWAIT;
  627. sei.nShow = SW_SHOWNORMAL;
  628. hr = ShellExecuteEx(&sei) ? S_OK : S_FALSE;
  629. }
  630. while ((hr = psw->FindWindowSW(&var, PVAREMPTY, SWC_BROWSER, &lhwnd,
  631. ppauto ? (SWFO_NEEDDISPATCH | SWFO_INCLUDEPENDING) : SWFO_INCLUDEPENDING,
  632. &pdisp)) != S_OK)
  633. {
  634. if (FAILED(pdfwait->WaitForWindowToOpen(20 * 1000)))
  635. {
  636. hr = E_ABORT;
  637. break;
  638. }
  639. }
  640. }
  641. pdfwait->CleanUp(); // No need to watch things any more...
  642. pdfwait->Release(); // release our use of this object...
  643. }
  644. }
  645. if (hr == S_OK && ppauto)
  646. {
  647. // if this fails this is because we are inside SendMessage loop
  648. hr = pdisp->QueryInterface(IID_PPV_ARG(IWebBrowserApp, ppauto));
  649. }
  650. if (pdisp)
  651. pdisp->Release();
  652. VariantClear(&var);
  653. }
  654. psw->Release();
  655. }
  656. return hr;
  657. }
  658. #undef VariantCopy
  659. WINOLEAUTAPI VariantCopyLazy(VARIANTARG * pvargDest, VARIANTARG * pvargSrc)
  660. {
  661. VariantClearLazy(pvargDest);
  662. switch(pvargSrc->vt) {
  663. case VT_I4:
  664. case VT_UI4:
  665. case VT_BOOL:
  666. // we can add more
  667. *pvargDest = *pvargSrc;
  668. return S_OK;
  669. case VT_UNKNOWN:
  670. if (pvargDest) {
  671. *pvargDest = *pvargSrc;
  672. if (pvargDest->punkVal)
  673. pvargDest->punkVal->AddRef();
  674. return S_OK;
  675. }
  676. ASSERT(0);
  677. return E_INVALIDARG;
  678. }
  679. return VariantCopy(pvargDest, pvargSrc);
  680. }
  681. //
  682. // WARNING: This function must be placed at the end because we #undef
  683. // VariantClear
  684. //
  685. #undef VariantClear
  686. HRESULT VariantClearLazy(VARIANTARG *pvarg)
  687. {
  688. switch(pvarg->vt)
  689. {
  690. case VT_I4:
  691. case VT_UI4:
  692. case VT_EMPTY:
  693. case VT_BOOL:
  694. // No operation
  695. break;
  696. case VT_UNKNOWN:
  697. if(V_UNKNOWN(pvarg) != NULL)
  698. V_UNKNOWN(pvarg)->Release();
  699. break;
  700. case VT_DISPATCH:
  701. if(V_DISPATCH(pvarg) != NULL)
  702. V_DISPATCH(pvarg)->Release();
  703. break;
  704. case VT_SAFEARRAY:
  705. THR(SafeArrayDestroy(V_ARRAY(pvarg)));
  706. break;
  707. default:
  708. return VariantClear(pvarg);
  709. }
  710. V_VT(pvarg) = VT_EMPTY;
  711. return S_OK;
  712. }