Leaked source code of windows server 2003
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.

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