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.

1132 lines
30 KiB

  1. #include "priv.h"
  2. #include "dspsprt.h"
  3. #include "basesb.h"
  4. #include "cnctnpt.h"
  5. #include "stdenum.h"
  6. #include "winlist.h"
  7. #include <varutil.h>
  8. #define WM_INVOKE_ON_RIGHT_THREAD (WM_USER)
  9. class CSDEnumWindows;
  10. class WindowData
  11. {
  12. private:
  13. long m_cRef;
  14. public:
  15. // Pidl variable is changed on the fly requiring reads/writes to be
  16. // protected by critical sections. Pid is also changed after creation but
  17. // only by _EnsurePid. So as long code calls _EnsurePid before reading Pid
  18. // no critical sections are required to read.
  19. LPITEMIDLIST pidl;
  20. IDispatch *pid; // The IDispatch for the item
  21. long lCookie; // The cookie to make sure that the person releasing is the one that added it
  22. HWND hwnd; // The top hwnd, so we can
  23. DWORD dwThreadId; // when it is in the pending box...
  24. BOOL fActive:1;
  25. int swClass;
  26. WindowData()
  27. {
  28. ASSERT(pid == NULL);
  29. ASSERT(hwnd == NULL);
  30. ASSERT(pidl == NULL);
  31. m_cRef = 1;
  32. }
  33. ~WindowData()
  34. {
  35. if (pid)
  36. pid->Release();
  37. ILFree(pidl); // null is OK
  38. }
  39. ULONG AddRef()
  40. {
  41. return InterlockedIncrement(&m_cRef);
  42. }
  43. ULONG Release()
  44. {
  45. if (InterlockedDecrement(&m_cRef))
  46. return m_cRef;
  47. delete this;
  48. return 0;
  49. }
  50. };
  51. class CSDWindows : public IShellWindows
  52. , public IConnectionPointContainer
  53. , protected CImpIDispatch
  54. {
  55. friend CSDEnumWindows;
  56. public:
  57. CSDWindows(void);
  58. BOOL Init(void);
  59. // IUnknown
  60. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  61. STDMETHODIMP_(ULONG) AddRef(void);
  62. STDMETHODIMP_(ULONG) Release(void);
  63. // IDispatch
  64. STDMETHODIMP GetTypeInfoCount(UINT * pctinfo)
  65. { return CImpIDispatch::GetTypeInfoCount(pctinfo); }
  66. STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  67. { return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
  68. STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
  69. { return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
  70. STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr)
  71. { return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
  72. // IConnectionPointContainer
  73. STDMETHODIMP EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum);
  74. STDMETHODIMP FindConnectionPoint(REFIID iid, IConnectionPoint ** ppCP);
  75. // IShellWindows
  76. STDMETHODIMP get_WindowPath (BSTR *pbs);
  77. STDMETHODIMP get_Count(long *plCount);
  78. STDMETHODIMP Item(VARIANT, IDispatch **ppid);
  79. STDMETHODIMP _NewEnum(IUnknown **ppunk);
  80. STDMETHODIMP Register(IDispatch *pid, long HWND, int swClass, long *plCookie);
  81. STDMETHODIMP RegisterPending(long lThreadId, VARIANT* pvarloc, VARIANT* pvarlocRoot, int swClass, long *plCookie);
  82. STDMETHODIMP Revoke(long lCookie);
  83. STDMETHODIMP OnNavigate(long lCookie, VARIANT* pvarLoc);
  84. STDMETHODIMP OnActivated(long lCookie, VARIANT_BOOL fActive);
  85. STDMETHODIMP FindWindowSW(VARIANT* varLoc, VARIANT* varlocRoot, int swClass, long * phwnd, int swfwOptions, IDispatch** ppdispAuto);
  86. STDMETHODIMP OnCreated(long lCookie, IUnknown *punk);
  87. STDMETHODIMP ProcessAttachDetach(VARIANT_BOOL fAttach);
  88. private:
  89. ~CSDWindows(void);
  90. WindowData* _FindItem(long lCookie);
  91. WindowData* _FindAndRemovePendingItem(HWND hwnd, long lCookie);
  92. void _EnsurePid(WindowData *pwd);
  93. void _DoInvokeCookie(DISPID dispid, long lCookie, BOOL fCheckThread);
  94. HRESULT _Item(VARIANT index, IDispatch **ppid, BOOL fRemoveDeadwood);
  95. static LRESULT CALLBACK s_ThreadNotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  96. int _NewCookie();
  97. #ifdef DEBUG
  98. void _DBDumpList(void);
  99. #endif
  100. LONG m_cRef;
  101. LONG m_cProcessAttach;
  102. HDPA m_hdpa; // DPA to hold information about each window
  103. HDPA m_hdpaPending; // DPA to hold information about pending windows.
  104. LONG m_cTickCount; // used to generate cookies
  105. HWND m_hwndHack;
  106. DWORD m_dwThreadID;
  107. // Embed our Connection Point object - implmentation in cnctnpt.cpp
  108. CConnectionPoint m_cpWindowsEvents;
  109. };
  110. #ifdef DEBUG // used by DBGetClassSymbolic
  111. EXTERN_C const int SIZEOF_CSDWindows = SIZEOF(CSDWindows);
  112. #endif
  113. class CSDEnumWindows : public IEnumVARIANT
  114. {
  115. public:
  116. CSDEnumWindows(CSDWindows *psdw);
  117. // IUnknown
  118. STDMETHODIMP QueryInterface(REFIID, void **);
  119. STDMETHODIMP_(ULONG) AddRef(void);
  120. STDMETHODIMP_(ULONG) Release(void);
  121. // IEnumFORMATETC
  122. STDMETHODIMP Next(ULONG, VARIANT *, ULONG *);
  123. STDMETHODIMP Skip(ULONG);
  124. STDMETHODIMP Reset(void);
  125. STDMETHODIMP Clone(IEnumVARIANT **);
  126. private:
  127. ~CSDEnumWindows();
  128. LONG m_cRef;
  129. CSDWindows *m_psdw;
  130. int m_iCur;
  131. };
  132. STDAPI CSDWindows_CreateInstance(IShellWindows **ppsw)
  133. {
  134. HRESULT hr = E_OUTOFMEMORY; // assume failure...
  135. *ppsw = NULL;
  136. CSDWindows* psdf = new CSDWindows();
  137. if (psdf)
  138. {
  139. if (psdf->Init())
  140. hr = psdf->QueryInterface(IID_PPV_ARG(IShellWindows, ppsw));
  141. psdf->Release();
  142. }
  143. return hr;
  144. }
  145. CSDWindows::CSDWindows(void) :
  146. CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_IShellWindows)
  147. {
  148. DllAddRef();
  149. m_cRef = 1;
  150. ASSERT(m_hdpa == NULL);
  151. ASSERT(m_hdpaPending == NULL);
  152. ASSERT(m_cProcessAttach == 0);
  153. m_cpWindowsEvents.SetOwner((IUnknown*)SAFECAST(this, IShellWindows*), &DIID_DShellWindowsEvents);
  154. }
  155. int DPA_SWindowsFree(void *p, void *d)
  156. {
  157. ((WindowData*)p)->Release();
  158. return 1;
  159. }
  160. CSDWindows::~CSDWindows(void)
  161. {
  162. if (m_hdpa)
  163. {
  164. // We need to release the data associated with all of the items in the list
  165. // as well as release our usage of the interfaces...
  166. HDPA hdpa = m_hdpa;
  167. m_hdpa = NULL;
  168. DPA_DestroyCallback(hdpa, DPA_SWindowsFree, 0);
  169. hdpa = NULL;
  170. }
  171. if (m_hdpaPending)
  172. {
  173. // We need to release the data associated with all of the items in the list
  174. // as well as release our usage of the interfaces...
  175. HDPA hdpa = m_hdpaPending;
  176. m_hdpaPending = NULL;
  177. DPA_DestroyCallback(hdpa, DPA_SWindowsFree, 0);
  178. hdpa = NULL;
  179. }
  180. if (m_hwndHack)
  181. DestroyWindow(m_hwndHack);
  182. DllRelease();
  183. }
  184. BOOL CSDWindows::Init(void)
  185. {
  186. m_hdpa = ::DPA_Create(0);
  187. m_hdpaPending = ::DPA_Create(0);
  188. m_dwThreadID = GetCurrentThreadId();
  189. m_hwndHack = SHCreateWorkerWindow(s_ThreadNotifyWndProc, NULL, 0, 0, (HMENU)0, this);
  190. if (!m_hdpa || !m_hdpaPending || !m_hwndHack)
  191. return FALSE;
  192. return TRUE;
  193. }
  194. STDMETHODIMP CSDWindows::QueryInterface(REFIID riid, void **ppv)
  195. {
  196. static const QITAB qit[] = {
  197. QITABENT(CSDWindows, IConnectionPointContainer),
  198. QITABENT(CSDWindows, IShellWindows),
  199. QITABENTMULTI(CSDWindows, IDispatch, IShellWindows),
  200. { 0 },
  201. };
  202. return QISearch(this, qit, riid, ppv);
  203. }
  204. STDMETHODIMP_(ULONG) CSDWindows::AddRef(void)
  205. {
  206. return InterlockedIncrement(&m_cRef);
  207. }
  208. STDMETHODIMP_(ULONG) CSDWindows::Release(void)
  209. {
  210. if (InterlockedDecrement(&m_cRef))
  211. return m_cRef;
  212. delete this;
  213. return 0;
  214. }
  215. // IShellWindows implementation
  216. STDMETHODIMP CSDWindows::get_Count(long *plCount)
  217. {
  218. #ifdef DEBUG
  219. if (*plCount == -1)
  220. _DBDumpList();
  221. #endif
  222. *plCount = 0;
  223. ENTERCRITICAL;
  224. for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--)
  225. {
  226. WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i);
  227. if (pwd->hwnd)
  228. (*plCount)++; // only count those with non NULL hwnd
  229. }
  230. LEAVECRITICAL;
  231. return S_OK;
  232. }
  233. #ifdef DEBUG
  234. void CSDWindows::_DBDumpList(void)
  235. {
  236. ENTERCRITICAL;
  237. for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--)
  238. {
  239. TCHAR szClass[32];
  240. WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i);
  241. szClass[0] = 0;
  242. if (IsWindow(pwd->hwnd))
  243. GetClassName(pwd->hwnd, szClass, ARRAYSIZE(szClass));
  244. TraceMsg(DM_TRACE, "csdw.dbdl: i=%d hwnd=%x (class=%s) cookie=%d tid=%d IDisp=%x pidl=%x fActive=%u swClass=%d", i,
  245. pwd->hwnd, szClass, pwd->lCookie, pwd->dwThreadId,
  246. pwd->pid, pwd->pidl, pwd->fActive, pwd->swClass);
  247. }
  248. LEAVECRITICAL;
  249. }
  250. #endif
  251. /*
  252. * function to ensure that the pid is around and registered.
  253. * For delay registered guys, this involves calling back to the registered
  254. * window handle via a private message to tell it to give us a marshalled
  255. * IDispatch.
  256. *
  257. * Callers of _EnusrePid must have pwd addref'ed to ensure it will stay
  258. * alive.
  259. */
  260. #define WAIT_TIME 20000 // 20 seconds
  261. void CSDWindows::_EnsurePid(WindowData *pwd)
  262. {
  263. IDispatch *pid = pwd->pid;
  264. if (!pid)
  265. {
  266. ASSERT(pwd->hwnd);
  267. #ifndef NO_MARSHALLING
  268. // we can not pass a stream between two processes, so we ask
  269. // the other process to create a shared memory block with our
  270. // information in it such that we can then create a stream on it...
  271. // IDispatch from. They will CoMarshalInterface their IDispatch
  272. // into the stream and return TRUE if successful. We then
  273. // reset the stream pointer to the head and unmarshal the IDispatch
  274. // and store it in our list.
  275. DWORD dwProcId = GetCurrentProcessId();
  276. DWORD_PTR dwResult;
  277. // Use SendMessageTimeoutA since SendMessageTimeoutW doesn't work on w95.
  278. if (SendMessageTimeoutA(pwd->hwnd, WMC_MARSHALIDISPATCHSLOW, 0,
  279. (LPARAM)dwProcId, SMTO_ABORTIFHUNG, WAIT_TIME, &dwResult) && dwResult)
  280. {
  281. // There should be an easier way to get this but for now...
  282. DWORD cb;
  283. LPBYTE pv = (LPBYTE)SHLockShared((HANDLE)dwResult, dwProcId);
  284. // Don't know for sure a good way to get the size so assume that first DWORD
  285. // is size of rest of the area
  286. if (pv && ((cb = *((DWORD*)pv)) > 0))
  287. {
  288. IStream *pIStream;
  289. if (SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &pIStream)))
  290. {
  291. const LARGE_INTEGER li = {0, 0};
  292. pIStream->Write(pv + sizeof(DWORD), cb, NULL);
  293. pIStream->Seek(li, STREAM_SEEK_SET, NULL);
  294. CoUnmarshalInterface(pIStream, IID_PPV_ARG(IDispatch, &pid));
  295. pIStream->Release();
  296. }
  297. }
  298. SHUnlockShared(pv);
  299. SHFreeShared((HANDLE)dwResult, dwProcId);
  300. }
  301. #else
  302. // UNIX IE has no marshalling capability YET
  303. SendMessage(pwd->hwnd, WMC_MARSHALIDISPATCHSLOW, 0, (LPARAM)&(pid));
  304. // Since we don't use CoMarshal... stuff here we need to increment the
  305. // reference count.
  306. pid->AddRef();
  307. #endif
  308. if (pid)
  309. {
  310. pid->AddRef();
  311. ENTERCRITICAL;
  312. // make sure a race on this did not already set pwd->pid
  313. if (NULL == pwd->pid)
  314. pwd->pid = pid;
  315. LEAVECRITICAL;
  316. pid->Release();
  317. }
  318. }
  319. }
  320. typedef struct
  321. {
  322. WindowData * pwd;
  323. HDPA hdpaWindowList;
  324. int swClass;
  325. } TMW;
  326. BOOL CALLBACK CSDEnumWindowsProc(HWND hwnd, LPARAM lParam)
  327. {
  328. TMW *ptwm = (TMW *) lParam;
  329. BOOL fFound = FALSE;
  330. // We walk a global hdpa window list, so we better be in a critical section.
  331. ASSERTCRITICAL;
  332. ASSERT(ptwm && ptwm->hdpaWindowList);
  333. ptwm->pwd = NULL;
  334. for (int i = DPA_GetPtrCount(ptwm->hdpaWindowList) - 1; (i >= 0) && !fFound; i--)
  335. {
  336. WindowData *pwd = (WindowData*)DPA_FastGetPtr(ptwm->hdpaWindowList, i);
  337. if (pwd->hwnd == hwnd && (ptwm->swClass == -1 || ptwm->swClass == pwd->swClass))
  338. {
  339. ptwm->pwd = pwd;
  340. pwd->AddRef();
  341. fFound = TRUE;
  342. break;
  343. }
  344. }
  345. return !fFound;
  346. }
  347. void CSDGetTopMostWindow(TMW* ptmw)
  348. {
  349. EnumWindows(CSDEnumWindowsProc, (LPARAM)ptmw);
  350. }
  351. // Just like Item, except caller can specify if error is returned vs window deleted when
  352. // window is in enumeration list but can't get idispatch. This permits ::Next
  353. // operator to skip bad windows, but still return valid ones.
  354. HRESULT CSDWindows::_Item(VARIANT index, IDispatch **ppid, BOOL fRemoveDeadwood)
  355. {
  356. TMW tmw;
  357. tmw.pwd = NULL;
  358. tmw.hdpaWindowList = m_hdpa;
  359. tmw.swClass = -1;
  360. *ppid = NULL;
  361. // This is sortof gross, but if we are passed a pointer to another variant, simply
  362. // update our copy here...
  363. if (index.vt == (VT_BYREF|VT_VARIANT) && index.pvarVal)
  364. index = *index.pvarVal;
  365. ASSERT(!(fRemoveDeadwood && index.vt != VT_I2 && index.vt != VT_I4));
  366. Retry:
  367. switch (index.vt)
  368. {
  369. case VT_UI4:
  370. tmw.swClass = index.ulVal;
  371. // fall through
  372. case VT_ERROR:
  373. {
  374. HWND hwnd = GetActiveWindow();
  375. if (!hwnd)
  376. hwnd = GetForegroundWindow();
  377. if (hwnd)
  378. {
  379. ENTERCRITICAL;
  380. if (!CSDEnumWindowsProc(hwnd, (LPARAM)&tmw))
  381. {
  382. ASSERT(tmw.pwd);
  383. }
  384. LEAVECRITICAL;
  385. }
  386. if (!tmw.pwd)
  387. {
  388. ENTERCRITICAL;
  389. CSDGetTopMostWindow(&tmw);
  390. LEAVECRITICAL;
  391. }
  392. }
  393. break;
  394. case VT_I2:
  395. index.lVal = (long)index.iVal;
  396. // And fall through...
  397. case VT_I4:
  398. if ((index.lVal >= 0))
  399. {
  400. ENTERCRITICAL;
  401. tmw.pwd = (WindowData*)DPA_GetPtr(m_hdpa, index.lVal);
  402. if (tmw.pwd)
  403. tmw.pwd->AddRef();
  404. LEAVECRITICAL;
  405. }
  406. break;
  407. default:
  408. return E_INVALIDARG;
  409. }
  410. if (tmw.pwd)
  411. {
  412. _EnsurePid(tmw.pwd);
  413. *ppid = tmw.pwd->pid;
  414. if (tmw.pwd->hwnd && !IsWindow(tmw.pwd->hwnd))
  415. {
  416. *ppid = NULL;
  417. }
  418. if (*ppid)
  419. {
  420. (*ppid)->AddRef();
  421. tmw.pwd->Release();
  422. tmw.pwd = NULL;
  423. return S_OK;
  424. }
  425. else if (fRemoveDeadwood)
  426. {
  427. // In case the window was blown away in a fault we should try to recover...
  428. // We can only do this if caller is expecting to have item deleted out from
  429. // under it (see CSDEnumWindows::Next, below)
  430. Revoke(tmw.pwd->lCookie);
  431. tmw.swClass = -1;
  432. tmw.pwd->Release();
  433. tmw.pwd = NULL;
  434. goto Retry;
  435. }
  436. else
  437. {
  438. tmw.pwd->Release();
  439. tmw.pwd = NULL;
  440. }
  441. }
  442. return S_FALSE; // Not a strong error, but a null pointer type of error
  443. }
  444. /*
  445. * This is essentially an array lookup operator for the collection.
  446. * Collection.Item by itself the same as the collection itself.
  447. * Otherwise you can refer to the item by index or by path, which
  448. * shows up in the VARIANT parameter. We have to check the type
  449. * of the variant to see if it's VT_I4 (an index) or VT_BSTR (a
  450. * path) and do the right thing.
  451. */
  452. STDMETHODIMP CSDWindows::Item(VARIANT index, IDispatch **ppid)
  453. {
  454. return _Item(index, ppid, FALSE);
  455. }
  456. STDMETHODIMP CSDWindows::_NewEnum(IUnknown **ppunk)
  457. {
  458. *ppunk = new CSDEnumWindows(this);
  459. return *ppunk ? S_OK : E_OUTOFMEMORY;
  460. }
  461. // IConnectionPointContainer
  462. STDMETHODIMP CSDWindows::FindConnectionPoint(REFIID iid, IConnectionPoint **ppCP)
  463. {
  464. if (IsEqualIID(iid, DIID_DShellWindowsEvents) ||
  465. IsEqualIID(iid, IID_IDispatch))
  466. {
  467. *ppCP = m_cpWindowsEvents.CastToIConnectionPoint();
  468. }
  469. else
  470. {
  471. *ppCP = NULL;
  472. return E_NOINTERFACE;
  473. }
  474. (*ppCP)->AddRef();
  475. return S_OK;
  476. }
  477. STDMETHODIMP CSDWindows::EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum)
  478. {
  479. return CreateInstance_IEnumConnectionPoints(ppEnum, 1, m_cpWindowsEvents.CastToIConnectionPoint());
  480. }
  481. void CSDWindows::_DoInvokeCookie(DISPID dispid, long lCookie, BOOL fCheckThread)
  482. {
  483. // if we don't have any sinks, then there's nothing to do. we intentionally
  484. // ignore errors here. Note: if we add more notification types we may want to
  485. // have this function call the equivelent code as is in iedisp code for DoInvokeParam.
  486. //
  487. if (m_cpWindowsEvents.IsEmpty())
  488. return;
  489. if (fCheckThread && (m_dwThreadID != GetCurrentThreadId()))
  490. {
  491. PostMessage(m_hwndHack, WM_INVOKE_ON_RIGHT_THREAD, (WPARAM)dispid, (LPARAM)lCookie);
  492. return;
  493. }
  494. VARIANTARG VarArgList[1] = {0};
  495. DISPPARAMS dispparams = {0};
  496. // fill out DISPPARAMS structure
  497. dispparams.rgvarg = VarArgList;
  498. dispparams.cArgs = 1;
  499. VarArgList[0].vt = VT_I4;
  500. VarArgList[0].lVal = lCookie;
  501. IConnectionPoint_SimpleInvoke(&m_cpWindowsEvents, dispid, &dispparams);
  502. }
  503. // Guarantee a non-zero cookie, since 0 is used as a NULL value in
  504. // various places (eg shbrowse.cpp)
  505. int CSDWindows::_NewCookie()
  506. {
  507. m_cTickCount++;
  508. if (0 == m_cTickCount)
  509. m_cTickCount++;
  510. return m_cTickCount;
  511. }
  512. STDMETHODIMP CSDWindows::Register(IDispatch *pid, long hwnd, int swClass, long *plCookie)
  513. {
  514. if (!plCookie || (hwnd == NULL && swClass != SWC_CALLBACK) || (swClass == SWC_CALLBACK && pid == NULL))
  515. return E_POINTER;
  516. BOOL fAllocatedNewItem = FALSE;
  517. // If the pid isn't specified now (delay register), we'll call back later to
  518. // get it if we need it.
  519. if (pid)
  520. pid->AddRef();
  521. // We need to be carefull as to not leave a window of opertunity between removing the item from
  522. // the pending list till it is on the main list or some other thread could open a different window
  523. // up... Also guard m_hdpa
  524. // To avoid deadlocks, do not add any callouts to the code below!
  525. ENTERCRITICAL;
  526. // First see if we have
  527. WindowData *pwd = _FindAndRemovePendingItem(IntToPtr_(HWND, hwnd), 0);
  528. if (!pwd)
  529. {
  530. pwd = new WindowData();
  531. if (!pwd)
  532. {
  533. LEAVECRITICAL;
  534. if (pid)
  535. pid->Release();
  536. return E_OUTOFMEMORY;
  537. }
  538. pwd->lCookie = _NewCookie();
  539. }
  540. pwd->pid = pid;
  541. pwd->swClass = swClass;
  542. pwd->hwnd = IntToPtr_(HWND, hwnd);
  543. if (plCookie)
  544. *plCookie = pwd->lCookie;
  545. // Give our refcount to the DPA
  546. DPA_AppendPtr(m_hdpa, pwd);
  547. LEAVECRITICAL;
  548. // We should now notify anyone waiting that there is a window registered...
  549. _DoInvokeCookie(DISPID_WINDOWREGISTERED, pwd->lCookie, TRUE);
  550. return S_OK;
  551. }
  552. STDMETHODIMP CSDWindows::RegisterPending(long lThreadId, VARIANT* pvarloc, VARIANT* pvarlocRoot, int swClass, long *plCookie)
  553. {
  554. if (plCookie)
  555. *plCookie = 0;
  556. HRESULT hr = E_OUTOFMEMORY;
  557. WindowData *pwd = new WindowData();
  558. if (pwd)
  559. {
  560. // pwd is not in any DPA at this point so it is safe to change
  561. // variables outside of critical section
  562. pwd->swClass = swClass;
  563. pwd->dwThreadId = (DWORD)lThreadId;
  564. pwd->pidl = VariantToIDList(pvarloc);
  565. if (pwd->pidl)
  566. {
  567. ASSERT(!pvarlocRoot || pvarlocRoot->vt == VT_EMPTY);
  568. ENTERCRITICAL; // guards m_hdpa access
  569. pwd->lCookie = _NewCookie();
  570. if (plCookie)
  571. *plCookie = pwd->lCookie;
  572. // Give our refcount to the DPA
  573. DPA_AppendPtr(m_hdpaPending, pwd);
  574. LEAVECRITICAL;
  575. hr = S_OK; // success
  576. }
  577. else
  578. pwd->Release();
  579. }
  580. return hr;
  581. }
  582. WindowData* CSDWindows::_FindItem(long lCookie)
  583. {
  584. WindowData * pResult = NULL;
  585. ENTERCRITICAL;
  586. for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--)
  587. {
  588. WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i);
  589. if (pwd->lCookie == lCookie)
  590. {
  591. pResult = pwd;
  592. pResult->AddRef();
  593. }
  594. }
  595. LEAVECRITICAL;
  596. return pResult;
  597. }
  598. WindowData* CSDWindows::_FindAndRemovePendingItem(HWND hwnd, long lCookie)
  599. {
  600. WindowData* pwdRet = NULL; // assume error
  601. DWORD dwThreadId = hwnd ? GetWindowThreadProcessId(hwnd, NULL) : 0;
  602. ENTERCRITICAL;
  603. for (int i = DPA_GetPtrCount(m_hdpaPending) - 1;i >= 0; i--)
  604. {
  605. WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpaPending, i);
  606. if ((pwd->dwThreadId == dwThreadId) || (pwd->lCookie == lCookie))
  607. {
  608. pwdRet = pwd;
  609. DPA_DeletePtr(m_hdpaPending, i);
  610. break;
  611. }
  612. }
  613. // Since we are both removing the WindowData from the pending array (Release)
  614. // and returning it (AddRef) we can just leave its refcount alone. The
  615. // caller should release it when they are done with it.
  616. LEAVECRITICAL;
  617. return pwdRet;
  618. }
  619. STDMETHODIMP CSDWindows::Revoke(long lCookie)
  620. {
  621. WindowData *pwd = NULL;
  622. HRESULT hr = S_FALSE;
  623. ENTERCRITICAL; // guards m_hdpa
  624. for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--)
  625. {
  626. pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i);
  627. if (pwd->lCookie == lCookie)
  628. {
  629. // Remove it from the list while in semaphore...
  630. // Since we are deleting the WindowData from the array we should not
  631. // addref it. We are taking the refcount from the array.
  632. DPA_DeletePtr(m_hdpa, i);
  633. break;
  634. }
  635. }
  636. LEAVECRITICAL;
  637. if ((i >= 0) || (pwd = _FindAndRemovePendingItem(NULL, lCookie)))
  638. {
  639. // event for window going away
  640. _DoInvokeCookie(DISPID_WINDOWREVOKED, pwd->lCookie, TRUE);
  641. pwd->Release();
  642. hr = S_OK;
  643. }
  644. return hr;
  645. }
  646. STDMETHODIMP CSDWindows::OnNavigate(long lCookie, VARIANT* pvarLoc)
  647. {
  648. HRESULT hr;
  649. WindowData* pwd = _FindItem(lCookie);
  650. if (pwd)
  651. {
  652. // NOTE: this is where we mess with the pidl inside of a WindowData struct.
  653. // this is why we need to protect all access to pwd->pidl with a critsec
  654. ENTERCRITICAL;
  655. ILFree(pwd->pidl);
  656. pwd->pidl = VariantToIDList(pvarLoc);
  657. hr = pwd->pidl ? S_OK : E_OUTOFMEMORY;
  658. LEAVECRITICAL;
  659. pwd->Release();
  660. }
  661. else
  662. hr = E_INVALIDARG;
  663. return hr;
  664. }
  665. STDMETHODIMP CSDWindows::OnActivated(long lCookie, VARIANT_BOOL fActive)
  666. {
  667. WindowData* pwd = _FindItem(lCookie);
  668. if (pwd)
  669. {
  670. pwd->fActive = (BOOL)fActive;
  671. pwd->Release();
  672. }
  673. return pwd ? S_OK : E_INVALIDARG;
  674. }
  675. STDMETHODIMP CSDWindows::OnCreated(long lCookie, IUnknown *punk)
  676. {
  677. HRESULT hr = E_FAIL;
  678. WindowData* pwd = _FindItem(lCookie);
  679. if (pwd)
  680. {
  681. _EnsurePid(pwd);
  682. ITargetNotify *ptgn;
  683. if (pwd->pid && SUCCEEDED(pwd->pid->QueryInterface(IID_PPV_ARG(ITargetNotify, &ptgn))))
  684. {
  685. hr = ptgn->OnCreate(punk, lCookie);
  686. ptgn->Release();
  687. }
  688. pwd->Release();
  689. }
  690. return hr;
  691. }
  692. void _FreeWindowDataAndPidl(WindowData **ppwd, LPITEMIDLIST *ppidl)
  693. {
  694. if (*ppidl)
  695. {
  696. ILFree(*ppidl);
  697. *ppidl = NULL;
  698. }
  699. if (*ppwd)
  700. {
  701. (*ppwd)->Release();
  702. *ppwd = NULL;
  703. }
  704. }
  705. BOOL _GetWindowDataAndPidl(HDPA hdpa, int i, WindowData **ppwd, LPITEMIDLIST *ppidl)
  706. {
  707. _FreeWindowDataAndPidl(ppwd, ppidl);
  708. ENTERCRITICAL;
  709. *ppwd = (WindowData*)DPA_GetPtr(hdpa, i);
  710. if (*ppwd)
  711. {
  712. (*ppwd)->AddRef();
  713. // NOTE: pwd->pidl can change out from under us when we are outside of
  714. // the critsec so we must clone it so we can play with it when we don't
  715. // hold the critsec
  716. *ppidl = ILClone((*ppwd)->pidl);
  717. }
  718. LEAVECRITICAL;
  719. return *ppwd ? TRUE : FALSE;
  720. }
  721. STDMETHODIMP CSDWindows::FindWindowSW(VARIANT* pvarLoc, VARIANT* pvarLocRoot, int swClass,
  722. long *phwnd, int swfwOptions, IDispatch** ppdispOut)
  723. {
  724. HRESULT hr = S_FALSE; // success, but none found
  725. int i;
  726. LPITEMIDLIST pidlFree = VariantToIDList(pvarLoc);
  727. LPCITEMIDLIST pidl = pidlFree ? pidlFree : &s_idlNULL;
  728. ASSERT(!pvarLocRoot || pvarLocRoot->vt == VT_EMPTY);
  729. long lCookie = 0;
  730. if (pvarLoc && (swfwOptions & SWFO_COOKIEPASSED))
  731. {
  732. if (pvarLoc->vt == VT_I4)
  733. lCookie = pvarLoc->lVal;
  734. else if (pvarLoc->vt == VT_I2)
  735. lCookie = (LONG)pvarLoc->iVal;
  736. }
  737. if (ppdispOut)
  738. *ppdispOut = NULL;
  739. if (phwnd)
  740. *phwnd = NULL;
  741. if (swfwOptions & SWFO_NEEDDISPATCH)
  742. {
  743. if (!ppdispOut)
  744. {
  745. ILFree(pidlFree);
  746. return E_POINTER;
  747. }
  748. }
  749. WindowData* pwd = NULL;
  750. LPITEMIDLIST pidlCur = NULL;
  751. // If no PIDL we will assume an Empty idl...
  752. if (swfwOptions & SWFO_INCLUDEPENDING)
  753. {
  754. for (i = 0; _GetWindowDataAndPidl(m_hdpaPending, i, &pwd, &pidlCur); i++)
  755. {
  756. if ((pwd->swClass == swClass) &&
  757. (!lCookie || (lCookie == pwd->lCookie)) &&
  758. ILIsEqual(pidlCur, pidl))
  759. {
  760. if (phwnd)
  761. *phwnd = pwd->lCookie; // Something for them to use...
  762. _FreeWindowDataAndPidl(&pwd, &pidlCur);
  763. // found a pending window, return E_PENDING to say that the open is currently pending
  764. hr = E_PENDING;
  765. break;
  766. }
  767. _FreeWindowDataAndPidl(&pwd, &pidlCur);
  768. }
  769. }
  770. if (S_FALSE == hr)
  771. {
  772. for (i = 0; _GetWindowDataAndPidl(m_hdpa, i, &pwd, &pidlCur); i++)
  773. {
  774. if ((pwd->swClass == swClass) &&
  775. (!lCookie || (lCookie == pwd->lCookie)) &&
  776. (pidlCur && ILIsEqual(pidlCur, pidl)))
  777. {
  778. if (swfwOptions & SWFO_NEEDDISPATCH)
  779. _EnsurePid(pwd);
  780. if (phwnd)
  781. {
  782. // test the found window to see if it is valid, if not
  783. // blow it away and start over
  784. if (pwd->hwnd && !IsWindow(pwd->hwnd))
  785. {
  786. Revoke(pwd->lCookie);
  787. i = 0; // start over in this case
  788. _FreeWindowDataAndPidl(&pwd, &pidlCur);
  789. continue;
  790. }
  791. *phwnd = PtrToLong(pwd->hwnd); // windows handles 32b
  792. hr = S_OK; // terminate the loop
  793. }
  794. if (swfwOptions & SWFO_NEEDDISPATCH)
  795. {
  796. hr = pwd->pid ? pwd->pid->QueryInterface(IID_PPV_ARG(IDispatch, ppdispOut)) : E_NOINTERFACE;
  797. }
  798. _FreeWindowDataAndPidl(&pwd, &pidlCur);
  799. break;
  800. }
  801. _FreeWindowDataAndPidl(&pwd, &pidlCur);
  802. }
  803. }
  804. ILFree(pidlFree);
  805. return hr;
  806. }
  807. HRESULT CSDWindows::ProcessAttachDetach(VARIANT_BOOL fAttach)
  808. {
  809. if (fAttach)
  810. InterlockedIncrement(&m_cProcessAttach);
  811. else if (0 == InterlockedDecrement(&m_cProcessAttach))
  812. {
  813. // last process ref, we can now blow away the object in the shell context...
  814. if (g_dwWinListCFRegister)
  815. {
  816. #ifdef DEBUG
  817. long cwindow;
  818. get_Count(&cwindow);
  819. //ASSERT(cwindow==0);
  820. if (cwindow != 0)
  821. TraceMsg(DM_ERROR, "csdw.pad: cwindow=%d (!=0)", cwindow);
  822. #endif
  823. CoRevokeClassObject(g_dwWinListCFRegister);
  824. g_dwWinListCFRegister = 0;
  825. }
  826. }
  827. return S_OK;
  828. }
  829. CSDEnumWindows::CSDEnumWindows(CSDWindows *psdw)
  830. {
  831. DllAddRef();
  832. m_cRef = 1;
  833. m_psdw = psdw;
  834. m_psdw->AddRef();
  835. m_iCur = 0;
  836. }
  837. CSDEnumWindows::~CSDEnumWindows(void)
  838. {
  839. DllRelease();
  840. m_psdw->Release();
  841. }
  842. STDMETHODIMP CSDEnumWindows::QueryInterface(REFIID riid, void **ppv)
  843. {
  844. static const QITAB qit[] = {
  845. QITABENT(CSDEnumWindows, IEnumVARIANT), // IID_IEnumVARIANT
  846. { 0 },
  847. };
  848. return QISearch(this, qit, riid, ppv);
  849. }
  850. STDMETHODIMP_(ULONG) CSDEnumWindows::AddRef(void)
  851. {
  852. return InterlockedIncrement(&m_cRef);
  853. }
  854. STDMETHODIMP_(ULONG) CSDEnumWindows::Release(void)
  855. {
  856. if (InterlockedDecrement(&m_cRef))
  857. return m_cRef;
  858. delete this;
  859. return 0;
  860. }
  861. STDMETHODIMP CSDEnumWindows::Next(ULONG cVar, VARIANT *pVar, ULONG *pulVar)
  862. {
  863. ULONG cReturn = 0;
  864. HRESULT hr;
  865. if (!pulVar)
  866. {
  867. if (cVar != 1)
  868. return E_POINTER;
  869. }
  870. else
  871. *pulVar = 0;
  872. VARIANT index;
  873. index.vt = VT_I4;
  874. while (cVar > 0)
  875. {
  876. IDispatch *pid;
  877. index.lVal = m_iCur++;
  878. hr = m_psdw->_Item(index, &pid, TRUE);
  879. if (S_OK != hr)
  880. break;
  881. pVar->pdispVal = pid;
  882. pVar->vt = VT_DISPATCH;
  883. pVar++;
  884. cReturn++;
  885. cVar--;
  886. }
  887. if (NULL != pulVar)
  888. *pulVar = cReturn;
  889. return cReturn ? S_OK : S_FALSE;
  890. }
  891. STDMETHODIMP CSDEnumWindows::Skip(ULONG cSkip)
  892. {
  893. long cItems;
  894. m_psdw->get_Count(&cItems);
  895. if ((int)(m_iCur + cSkip) >= cItems)
  896. return S_FALSE;
  897. m_iCur += cSkip;
  898. return S_OK;
  899. }
  900. STDMETHODIMP CSDEnumWindows::Reset(void)
  901. {
  902. m_iCur = 0;
  903. return S_OK;
  904. }
  905. STDMETHODIMP CSDEnumWindows::Clone(LPENUMVARIANT *ppEnum)
  906. {
  907. CSDEnumWindows *pNew = new CSDEnumWindows(m_psdw);
  908. if (pNew)
  909. {
  910. *ppEnum = SAFECAST(pNew, IEnumVARIANT *);
  911. return S_OK;
  912. }
  913. *ppEnum = NULL;
  914. return E_OUTOFMEMORY;
  915. }
  916. LRESULT CALLBACK CSDWindows::s_ThreadNotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  917. {
  918. CSDWindows* pThis = (CSDWindows*)GetWindowPtr0(hwnd);
  919. LRESULT lRes = 0;
  920. if (uMsg < WM_USER)
  921. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  922. else
  923. {
  924. switch (uMsg)
  925. {
  926. case WM_INVOKE_ON_RIGHT_THREAD:
  927. pThis->_DoInvokeCookie((DISPID)wParam, (LONG)lParam, FALSE);
  928. break;
  929. }
  930. }
  931. return lRes;
  932. }