swindows.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 35k
Category:

Windows Kernel

Development Platform:

Visual C++

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