#include "priv.h" #include "dspsprt.h" #include "basesb.h" #include "cnctnpt.h" #include "stdenum.h" #include "winlist.h" #include #define WM_INVOKE_ON_RIGHT_THREAD (WM_USER) class CSDEnumWindows; class WindowData { private: long m_cRef; public: // Pidl variable is changed on the fly requiring reads/writes to be // protected by critical sections. Pid is also changed after creation but // only by _EnsurePid. So as long code calls _EnsurePid before reading Pid // no critical sections are required to read. LPITEMIDLIST pidl; IDispatch *pid; // The IDispatch for the item long lCookie; // The cookie to make sure that the person releasing is the one that added it HWND hwnd; // The top hwnd, so we can DWORD dwThreadId; // when it is in the pending box... BOOL fActive:1; int swClass; WindowData() { ASSERT(pid == NULL); ASSERT(hwnd == NULL); ASSERT(pidl == NULL); m_cRef = 1; } ~WindowData() { if (pid) { pid->Release(); } ILFree(pidl); // null is OK } ULONG AddRef() { return InterlockedIncrement(&m_cRef); } ULONG Release() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } }; class CSDWindows : public IShellWindows , public IConnectionPointContainer , protected CImpIDispatch { friend CSDEnumWindows; public: CSDWindows(void); BOOL Init(void); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IDispatch STDMETHODIMP GetTypeInfoCount(UINT * pctinfo) { return CImpIDispatch::GetTypeInfoCount(pctinfo); } STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); } STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid) { return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); } STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr) { return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } // IConnectionPointContainer STDMETHODIMP EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum); STDMETHODIMP FindConnectionPoint(REFIID iid, IConnectionPoint ** ppCP); // IShellWindows STDMETHODIMP get_WindowPath (BSTR *pbs); STDMETHODIMP get_Count(long *plCount); STDMETHODIMP Item(VARIANT, IDispatch **ppid); STDMETHODIMP _NewEnum(IUnknown **ppunk); STDMETHODIMP Register(IDispatch *pid, long HWND, int swClass, long *plCookie); STDMETHODIMP RegisterPending(long lThreadId, VARIANT* pvarloc, VARIANT* pvarlocRoot, int swClass, long *plCookie); STDMETHODIMP Revoke(long lCookie); STDMETHODIMP OnNavigate(long lCookie, VARIANT* pvarLoc); STDMETHODIMP OnActivated(long lCookie, VARIANT_BOOL fActive); STDMETHODIMP FindWindowSW(VARIANT* varLoc, VARIANT* varlocRoot, int swClass, long * phwnd, int swfwOptions, IDispatch** ppdispAuto); STDMETHODIMP OnCreated(long lCookie, IUnknown *punk); STDMETHODIMP ProcessAttachDetach(VARIANT_BOOL fAttach); private: ~CSDWindows(void); WindowData* _FindItem(long lCookie); WindowData* _FindAndRemovePendingItem(HWND hwnd, long lCookie); void _EnsurePid(WindowData *pwd); void _DoInvokeCookie(DISPID dispid, long lCookie, BOOL fCheckThread); HRESULT _Item(VARIANT index, IDispatch **ppid, BOOL fRemoveDeadwood); static LRESULT CALLBACK s_ThreadNotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int _NewCookie(); #ifdef DEBUG void _DBDumpList(void); #endif LONG m_cRef; LONG m_cProcessAttach; HDPA m_hdpa; // DPA to hold information about each window HDPA m_hdpaPending; // DPA to hold information about pending windows. LONG m_cTickCount; // used to generate cookies HWND m_hwndHack; DWORD m_dwThreadID; // Embed our Connection Point object - implmentation in cnctnpt.cpp CConnectionPoint m_cpWindowsEvents; }; class CSDEnumWindows : public IEnumVARIANT { public: CSDEnumWindows(CSDWindows *psdw); // IUnknown STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IEnumFORMATETC STDMETHODIMP Next(ULONG, VARIANT *, ULONG *); STDMETHODIMP Skip(ULONG); STDMETHODIMP Reset(void); STDMETHODIMP Clone(IEnumVARIANT **); private: ~CSDEnumWindows(); LONG m_cRef; CSDWindows *m_psdw; int m_iCur; }; STDAPI CSDWindows_CreateInstance(IShellWindows **ppsw) { HRESULT hr = E_OUTOFMEMORY; // assume failure... *ppsw = NULL; CSDWindows* psdf = new CSDWindows(); if (psdf) { if (psdf->Init()) { hr = psdf->QueryInterface(IID_PPV_ARG(IShellWindows, ppsw)); } psdf->Release(); } return hr; } CSDWindows::CSDWindows(void) : CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_IShellWindows) { DllAddRef(); m_cRef = 1; ASSERT(m_hdpa == NULL); ASSERT(m_hdpaPending == NULL); ASSERT(m_cProcessAttach == 0); m_cpWindowsEvents.SetOwner((IUnknown*)SAFECAST(this, IShellWindows*), &DIID_DShellWindowsEvents); } int DPA_SWindowsFree(void *p, void *d) { ((WindowData*)p)->Release(); return 1; } CSDWindows::~CSDWindows(void) { if (m_hdpa) { // We need to release the data associated with all of the items in the list // as well as release our usage of the interfaces... HDPA hdpa = m_hdpa; m_hdpa = NULL; DPA_DestroyCallback(hdpa, DPA_SWindowsFree, 0); hdpa = NULL; } if (m_hdpaPending) { // We need to release the data associated with all of the items in the list // as well as release our usage of the interfaces... HDPA hdpa = m_hdpaPending; m_hdpaPending = NULL; DPA_DestroyCallback(hdpa, DPA_SWindowsFree, 0); hdpa = NULL; } if (m_hwndHack) { DestroyWindow(m_hwndHack); } DllRelease(); } BOOL CSDWindows::Init(void) { m_hdpa = ::DPA_Create(0); m_hdpaPending = ::DPA_Create(0); m_dwThreadID = GetCurrentThreadId(); m_hwndHack = SHCreateWorkerWindow(s_ThreadNotifyWndProc, NULL, 0, 0, (HMENU)0, this); if (!m_hdpa || !m_hdpaPending || !m_hwndHack) return FALSE; return TRUE; } STDMETHODIMP CSDWindows::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CSDWindows, IConnectionPointContainer), QITABENT(CSDWindows, IShellWindows), QITABENTMULTI(CSDWindows, IDispatch, IShellWindows), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CSDWindows::AddRef(void) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CSDWindows::Release(void) { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // IShellWindows implementation STDMETHODIMP CSDWindows::get_Count(long *plCount) { #ifdef DEBUG if (*plCount == -1) _DBDumpList(); #endif *plCount = 0; ENTERCRITICAL; for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--) { WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i); if (pwd->hwnd) { (*plCount)++; // only count those with non NULL hwnd } } LEAVECRITICAL; return S_OK; } #ifdef DEBUG void CSDWindows::_DBDumpList(void) { ENTERCRITICAL; for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--) { TCHAR szClass[32]; WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i); szClass[0] = 0; if (IsWindow(pwd->hwnd)) { GetClassName(pwd->hwnd, szClass, ARRAYSIZE(szClass)); } TraceMsg(DM_TRACE, "csdw.dbdl: i=%d hwnd=%x (class=%s) cookie=%d tid=%d IDisp=%x pidl=%x fActive=%u swClass=%d", i, pwd->hwnd, szClass, pwd->lCookie, pwd->dwThreadId, pwd->pid, pwd->pidl, pwd->fActive, pwd->swClass); } LEAVECRITICAL; } #endif /* * function to ensure that the pid is around and registered. * For delay registered guys, this involves calling back to the registered * window handle via a private message to tell it to give us a marshalled * IDispatch. * * Callers of _EnusrePid must have pwd addref'ed to ensure it will stay * alive. */ #define WAIT_TIME 20000 // 20 seconds void CSDWindows::_EnsurePid(WindowData *pwd) { IDispatch *pid = pwd->pid; if (!pid) { ASSERT(pwd->hwnd); #ifndef NO_MARSHALLING // we can not pass a stream between two processes, so we ask // the other process to create a shared memory block with our // information in it such that we can then create a stream on it... // IDispatch from. They will CoMarshalInterface their IDispatch // into the stream and return TRUE if successful. We then // reset the stream pointer to the head and unmarshal the IDispatch // and store it in our list. DWORD dwProcId = GetCurrentProcessId(); DWORD_PTR dwResult; // Use SendMessageTimeoutA since SendMessageTimeoutW doesn't work on w95. if (SendMessageTimeoutA(pwd->hwnd, WMC_MARSHALIDISPATCHSLOW, 0, (LPARAM)dwProcId, SMTO_ABORTIFHUNG, WAIT_TIME, &dwResult) && dwResult) { // There should be an easier way to get this but for now... DWORD cb; LPBYTE pv = (LPBYTE)SHLockShared((HANDLE)dwResult, dwProcId); // Don't know for sure a good way to get the size so assume that first DWORD // is size of rest of the area if (pv && ((cb = *((DWORD*)pv)) > 0)) { IStream *pIStream; if (SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &pIStream))) { const LARGE_INTEGER li = {0, 0}; pIStream->Write(pv + sizeof(DWORD), cb, NULL); pIStream->Seek(li, STREAM_SEEK_SET, NULL); CoUnmarshalInterface(pIStream, IID_PPV_ARG(IDispatch, &pid)); pIStream->Release(); } } SHUnlockShared(pv); SHFreeShared((HANDLE)dwResult, dwProcId); } #else // UNIX IE has no marshalling capability YET SendMessage(pwd->hwnd, WMC_MARSHALIDISPATCHSLOW, 0, (LPARAM)&(pid)); // Since we don't use CoMarshal... stuff here we need to increment the // reference count. pid->AddRef(); #endif if (pid) { pid->AddRef(); ENTERCRITICAL; // make sure a race on this did not already set pwd->pid if (NULL == pwd->pid) { pwd->pid = pid; } LEAVECRITICAL; pid->Release(); } } } typedef struct { WindowData * pwd; HDPA hdpaWindowList; int swClass; } TMW; BOOL CALLBACK CSDEnumWindowsProc(HWND hwnd, LPARAM lParam) { TMW *ptwm = (TMW *) lParam; BOOL fFound = FALSE; // We walk a global hdpa window list, so we better be in a critical section. ASSERTCRITICAL; ASSERT(ptwm && ptwm->hdpaWindowList); ptwm->pwd = NULL; for (int i = DPA_GetPtrCount(ptwm->hdpaWindowList) - 1; (i >= 0) && !fFound; i--) { WindowData *pwd = (WindowData*)DPA_FastGetPtr(ptwm->hdpaWindowList, i); if (pwd->hwnd == hwnd && (ptwm->swClass == -1 || ptwm->swClass == pwd->swClass)) { ptwm->pwd = pwd; pwd->AddRef(); fFound = TRUE; break; } } return !fFound; } void CSDGetTopMostWindow(TMW* ptmw) { EnumWindows(CSDEnumWindowsProc, (LPARAM)ptmw); } // Just like Item, except caller can specify if error is returned vs window deleted when // window is in enumeration list but can't get idispatch. This permits ::Next // operator to skip bad windows, but still return valid ones. HRESULT CSDWindows::_Item(VARIANT index, IDispatch **ppid, BOOL fRemoveDeadwood) { TMW tmw; tmw.pwd = NULL; tmw.hdpaWindowList = m_hdpa; tmw.swClass = -1; *ppid = NULL; // This is sortof gross, but if we are passed a pointer to another variant, simply // update our copy here... if (index.vt == (VT_BYREF|VT_VARIANT) && index.pvarVal) { index = *index.pvarVal; } ASSERT(!(fRemoveDeadwood && index.vt != VT_I2 && index.vt != VT_I4)); Retry: switch (index.vt) { case VT_UI4: tmw.swClass = index.ulVal; // fall through case VT_ERROR: { HWND hwnd = GetActiveWindow(); if (!hwnd) { hwnd = GetForegroundWindow(); } if (hwnd) { ENTERCRITICAL; if (!CSDEnumWindowsProc(hwnd, (LPARAM)&tmw)) { ASSERT(tmw.pwd); } LEAVECRITICAL; } if (!tmw.pwd) { ENTERCRITICAL; CSDGetTopMostWindow(&tmw); LEAVECRITICAL; } } break; case VT_I2: index.lVal = (long)index.iVal; // And fall through... case VT_I4: if ((index.lVal >= 0)) { ENTERCRITICAL; tmw.pwd = (WindowData*)DPA_GetPtr(m_hdpa, index.lVal); if (tmw.pwd) { tmw.pwd->AddRef(); } LEAVECRITICAL; } break; default: return E_INVALIDARG; } if (tmw.pwd) { _EnsurePid(tmw.pwd); *ppid = tmw.pwd->pid; if (tmw.pwd->hwnd && !IsWindow(tmw.pwd->hwnd)) { *ppid = NULL; } if (*ppid) { (*ppid)->AddRef(); tmw.pwd->Release(); tmw.pwd = NULL; return S_OK; } else if (fRemoveDeadwood) { // In case the window was blown away in a fault we should try to recover... // We can only do this if caller is expecting to have item deleted out from // under it (see CSDEnumWindows::Next, below) Revoke(tmw.pwd->lCookie); tmw.swClass = -1; tmw.pwd->Release(); tmw.pwd = NULL; goto Retry; } else { tmw.pwd->Release(); tmw.pwd = NULL; } } return S_FALSE; // Not a strong error, but a null pointer type of error } /* * This is essentially an array lookup operator for the collection. * Collection.Item by itself the same as the collection itself. * Otherwise you can refer to the item by index or by path, which * shows up in the VARIANT parameter. We have to check the type * of the variant to see if it's VT_I4 (an index) or VT_BSTR (a * path) and do the right thing. */ STDMETHODIMP CSDWindows::Item(VARIANT index, IDispatch **ppid) { return _Item(index, ppid, FALSE); } STDMETHODIMP CSDWindows::_NewEnum(IUnknown **ppunk) { *ppunk = new CSDEnumWindows(this); return *ppunk ? S_OK : E_OUTOFMEMORY; } // IConnectionPointContainer STDMETHODIMP CSDWindows::FindConnectionPoint(REFIID iid, IConnectionPoint **ppCP) { if (IsEqualIID(iid, DIID_DShellWindowsEvents) || IsEqualIID(iid, IID_IDispatch)) { *ppCP = m_cpWindowsEvents.CastToIConnectionPoint(); } else { *ppCP = NULL; return E_NOINTERFACE; } (*ppCP)->AddRef(); return S_OK; } STDMETHODIMP CSDWindows::EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum) { return CreateInstance_IEnumConnectionPoints(ppEnum, 1, m_cpWindowsEvents.CastToIConnectionPoint()); } void CSDWindows::_DoInvokeCookie(DISPID dispid, long lCookie, BOOL fCheckThread) { // if we don't have any sinks, then there's nothing to do. we intentionally // ignore errors here. Note: if we add more notification types we may want to // have this function call the equivelent code as is in iedisp code for DoInvokeParam. // if (m_cpWindowsEvents.IsEmpty()) return; if (fCheckThread && (m_dwThreadID != GetCurrentThreadId())) { PostMessage(m_hwndHack, WM_INVOKE_ON_RIGHT_THREAD, (WPARAM)dispid, (LPARAM)lCookie); return; } VARIANTARG VarArgList[1] = {0}; DISPPARAMS dispparams = {0}; // fill out DISPPARAMS structure dispparams.rgvarg = VarArgList; dispparams.cArgs = 1; VarArgList[0].vt = VT_I4; VarArgList[0].lVal = lCookie; IConnectionPoint_SimpleInvoke(&m_cpWindowsEvents, dispid, &dispparams); } // Guarantee a non-zero cookie, since 0 is used as a NULL value in // various places (eg shbrowse.cpp) int CSDWindows::_NewCookie() { m_cTickCount++; if (0 == m_cTickCount) { m_cTickCount++; } return m_cTickCount; } STDMETHODIMP CSDWindows::Register(IDispatch *pid, long hwnd, int swClass, long *plCookie) { if (!plCookie || (hwnd == NULL && swClass != SWC_CALLBACK) || (swClass == SWC_CALLBACK && pid == NULL)) return E_POINTER; BOOL fAllocatedNewItem = FALSE; // If the pid isn't specified now (delay register), we'll call back later to // get it if we need it. if (pid) { pid->AddRef(); } // We need to be carefull as to not leave a window of opertunity between removing the item from // the pending list till it is on the main list or some other thread could open a different window // up... Also guard m_hdpa // To avoid deadlocks, do not add any callouts to the code below! ENTERCRITICAL; // First see if we have WindowData *pwd = _FindAndRemovePendingItem(IntToPtr_(HWND, hwnd), 0); if (!pwd) { pwd = new WindowData(); if (!pwd) { LEAVECRITICAL; if (pid) { pid->Release(); } return E_OUTOFMEMORY; } pwd->lCookie = _NewCookie(); } pwd->pid = pid; pwd->swClass = swClass; pwd->hwnd = IntToPtr_(HWND, hwnd); if (plCookie) { *plCookie = pwd->lCookie; } // Give our refcount to the DPA if ( -1 == DPA_AppendPtr(m_hdpa, pwd) ) { // Failed to add, free the memory; pwd->Release( ); *plCookie = 0; LEAVECRITICAL; return E_OUTOFMEMORY; } LEAVECRITICAL; // We should now notify anyone waiting that there is a window registered... _DoInvokeCookie(DISPID_WINDOWREGISTERED, pwd->lCookie, TRUE); return S_OK; } STDMETHODIMP CSDWindows::RegisterPending(long lThreadId, VARIANT* pvarloc, VARIANT* pvarlocRoot, int swClass, long *plCookie) { if (plCookie) { *plCookie = 0; } HRESULT hr = E_OUTOFMEMORY; WindowData *pwd = new WindowData(); if (pwd) { // pwd is not in any DPA at this point so it is safe to change // variables outside of critical section pwd->swClass = swClass; pwd->dwThreadId = (DWORD)lThreadId; pwd->pidl = VariantToIDList(pvarloc); if (pwd->pidl) { ASSERT(!pvarlocRoot || pvarlocRoot->vt == VT_EMPTY); ENTERCRITICAL; // guards m_hdpa access pwd->lCookie = _NewCookie(); if (plCookie) { *plCookie = pwd->lCookie; } // Give our refcount to the DPA if ( -1 == DPA_AppendPtr(m_hdpaPending, pwd) ) { pwd->Release(); } LEAVECRITICAL; hr = S_OK; // success } else { pwd->Release(); } } return hr; } WindowData* CSDWindows::_FindItem(long lCookie) { WindowData * pResult = NULL; ENTERCRITICAL; for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--) { WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i); if (pwd->lCookie == lCookie) { pResult = pwd; pResult->AddRef(); } } LEAVECRITICAL; return pResult; } WindowData* CSDWindows::_FindAndRemovePendingItem(HWND hwnd, long lCookie) { WindowData* pwdRet = NULL; // assume error DWORD dwThreadId = hwnd ? GetWindowThreadProcessId(hwnd, NULL) : 0; ENTERCRITICAL; for (int i = DPA_GetPtrCount(m_hdpaPending) - 1;i >= 0; i--) { WindowData* pwd = (WindowData*)DPA_FastGetPtr(m_hdpaPending, i); if ((pwd->dwThreadId == dwThreadId) || (pwd->lCookie == lCookie)) { pwdRet = pwd; DPA_DeletePtr(m_hdpaPending, i); break; } } // Since we are both removing the WindowData from the pending array (Release) // and returning it (AddRef) we can just leave its refcount alone. The // caller should release it when they are done with it. LEAVECRITICAL; return pwdRet; } STDMETHODIMP CSDWindows::Revoke(long lCookie) { WindowData *pwd = NULL; HRESULT hr = S_FALSE; ENTERCRITICAL; // guards m_hdpa for (int i = DPA_GetPtrCount(m_hdpa) - 1; i >= 0; i--) { pwd = (WindowData*)DPA_FastGetPtr(m_hdpa, i); if (pwd->lCookie == lCookie) { // Remove it from the list while in semaphore... // Since we are deleting the WindowData from the array we should not // addref it. We are taking the refcount from the array. DPA_DeletePtr(m_hdpa, i); break; } } LEAVECRITICAL; if ((i >= 0) || (pwd = _FindAndRemovePendingItem(NULL, lCookie))) { // event for window going away _DoInvokeCookie(DISPID_WINDOWREVOKED, pwd->lCookie, TRUE); pwd->Release(); hr = S_OK; } return hr; } STDMETHODIMP CSDWindows::OnNavigate(long lCookie, VARIANT* pvarLoc) { HRESULT hr; WindowData* pwd = _FindItem(lCookie); if (pwd) { // NOTE: this is where we mess with the pidl inside of a WindowData struct. // this is why we need to protect all access to pwd->pidl with a critsec ENTERCRITICAL; ILFree(pwd->pidl); pwd->pidl = VariantToIDList(pvarLoc); hr = pwd->pidl ? S_OK : E_OUTOFMEMORY; LEAVECRITICAL; pwd->Release(); } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CSDWindows::OnActivated(long lCookie, VARIANT_BOOL fActive) { WindowData* pwd = _FindItem(lCookie); if (pwd) { pwd->fActive = (BOOL)fActive; pwd->Release(); } return pwd ? S_OK : E_INVALIDARG; } STDMETHODIMP CSDWindows::OnCreated(long lCookie, IUnknown *punk) { HRESULT hr = E_FAIL; WindowData* pwd = _FindItem(lCookie); if (pwd) { _EnsurePid(pwd); ITargetNotify *ptgn; if (pwd->pid && SUCCEEDED(pwd->pid->QueryInterface(IID_PPV_ARG(ITargetNotify, &ptgn)))) { hr = ptgn->OnCreate(punk, lCookie); ptgn->Release(); } pwd->Release(); } return hr; } void _FreeWindowDataAndPidl(WindowData **ppwd, LPITEMIDLIST *ppidl) { if (*ppidl) { ILFree(*ppidl); *ppidl = NULL; } if (*ppwd) { (*ppwd)->Release(); *ppwd = NULL; } } BOOL _GetWindowDataAndPidl(HDPA hdpa, int i, WindowData **ppwd, LPITEMIDLIST *ppidl) { _FreeWindowDataAndPidl(ppwd, ppidl); ENTERCRITICAL; *ppwd = (WindowData*)DPA_GetPtr(hdpa, i); if (*ppwd) { (*ppwd)->AddRef(); // NOTE: pwd->pidl can change out from under us when we are outside of // the critsec so we must clone it so we can play with it when we don't // hold the critsec *ppidl = ILClone((*ppwd)->pidl); } LEAVECRITICAL; return *ppwd ? TRUE : FALSE; } STDMETHODIMP CSDWindows::FindWindowSW(VARIANT* pvarLoc, VARIANT* pvarLocRoot, int swClass, long *phwnd, int swfwOptions, IDispatch** ppdispOut) { HRESULT hr = S_FALSE; // success, but none found int i; LPITEMIDLIST pidlFree = VariantToIDList(pvarLoc); LPCITEMIDLIST pidl = pidlFree ? pidlFree : &s_idlNULL; ASSERT(!pvarLocRoot || pvarLocRoot->vt == VT_EMPTY); long lCookie = 0; if (pvarLoc && (swfwOptions & SWFO_COOKIEPASSED)) { if (pvarLoc->vt == VT_I4) { lCookie = pvarLoc->lVal; } else if (pvarLoc->vt == VT_I2) { lCookie = (LONG)pvarLoc->iVal; } } if (ppdispOut) { *ppdispOut = NULL; } if (phwnd) { *phwnd = NULL; } if (swfwOptions & SWFO_NEEDDISPATCH) { if (!ppdispOut) { ILFree(pidlFree); return E_POINTER; } } WindowData* pwd = NULL; LPITEMIDLIST pidlCur = NULL; // If no PIDL we will assume an Empty idl... if (swfwOptions & SWFO_INCLUDEPENDING) { for (i = 0; _GetWindowDataAndPidl(m_hdpaPending, i, &pwd, &pidlCur); i++) { if ((pwd->swClass == swClass) && (!lCookie || (lCookie == pwd->lCookie)) && ILIsEqual(pidlCur, pidl)) { if (phwnd) { *phwnd = pwd->lCookie; // Something for them to use... } _FreeWindowDataAndPidl(&pwd, &pidlCur); // found a pending window, return E_PENDING to say that the open is currently pending hr = E_PENDING; break; } _FreeWindowDataAndPidl(&pwd, &pidlCur); } } if (S_FALSE == hr) { for (i = 0; _GetWindowDataAndPidl(m_hdpa, i, &pwd, &pidlCur); i++) { if ((pwd->swClass == swClass) && (!lCookie || (lCookie == pwd->lCookie)) && (pidlCur && ILIsEqual(pidlCur, pidl))) { if (swfwOptions & SWFO_NEEDDISPATCH) { _EnsurePid(pwd); } if (phwnd) { // test the found window to see if it is valid, if not // blow it away and start over if (pwd->hwnd && !IsWindow(pwd->hwnd)) { Revoke(pwd->lCookie); i = 0; // start over in this case _FreeWindowDataAndPidl(&pwd, &pidlCur); continue; } *phwnd = PtrToLong(pwd->hwnd); // windows handles 32b hr = S_OK; // terminate the loop } if (swfwOptions & SWFO_NEEDDISPATCH) { hr = pwd->pid ? pwd->pid->QueryInterface(IID_PPV_ARG(IDispatch, ppdispOut)) : E_NOINTERFACE; } _FreeWindowDataAndPidl(&pwd, &pidlCur); break; } _FreeWindowDataAndPidl(&pwd, &pidlCur); } } ILFree(pidlFree); return hr; } HRESULT CSDWindows::ProcessAttachDetach(VARIANT_BOOL fAttach) { if (fAttach) { InterlockedIncrement(&m_cProcessAttach); } else { ASSERT( 0 != m_cProcessAttach ); if (0 == InterlockedDecrement(&m_cProcessAttach)) { // last process ref, we can now blow away the object in the shell context... if (g_dwWinListCFRegister) { #ifdef DEBUG long cwindow; get_Count(&cwindow); if (cwindow != 0) { TraceMsg(DM_ERROR, "csdw.pad: cwindow=%d (!=0)", cwindow); } #endif CoRevokeClassObject(g_dwWinListCFRegister); g_dwWinListCFRegister = 0; } } } return S_OK; } CSDEnumWindows::CSDEnumWindows(CSDWindows *psdw) { DllAddRef(); m_cRef = 1; m_psdw = psdw; m_psdw->AddRef(); m_iCur = 0; } CSDEnumWindows::~CSDEnumWindows(void) { DllRelease(); m_psdw->Release(); } STDMETHODIMP CSDEnumWindows::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CSDEnumWindows, IEnumVARIANT), // IID_IEnumVARIANT { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CSDEnumWindows::AddRef(void) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CSDEnumWindows::Release(void) { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDMETHODIMP CSDEnumWindows::Next(ULONG cVar, VARIANT *pVar, ULONG *pulVar) { ULONG cReturn = 0; HRESULT hr; if (!pulVar) { if (cVar != 1) return E_POINTER; } else { *pulVar = 0; } VARIANT index; index.vt = VT_I4; while (cVar > 0) { IDispatch *pid; index.lVal = m_iCur++; hr = m_psdw->_Item(index, &pid, TRUE); if (S_OK != hr) break; pVar->pdispVal = pid; pVar->vt = VT_DISPATCH; pVar++; cReturn++; cVar--; } if (NULL != pulVar) { *pulVar = cReturn; } return cReturn ? S_OK : S_FALSE; } STDMETHODIMP CSDEnumWindows::Skip(ULONG cSkip) { long cItems; m_psdw->get_Count(&cItems); if ((int)(m_iCur + cSkip) >= cItems) return S_FALSE; m_iCur += cSkip; return S_OK; } STDMETHODIMP CSDEnumWindows::Reset(void) { m_iCur = 0; return S_OK; } STDMETHODIMP CSDEnumWindows::Clone(LPENUMVARIANT *ppEnum) { CSDEnumWindows *pNew = new CSDEnumWindows(m_psdw); if (pNew) { *ppEnum = SAFECAST(pNew, IEnumVARIANT *); return S_OK; } *ppEnum = NULL; return E_OUTOFMEMORY; } LRESULT CALLBACK CSDWindows::s_ThreadNotifyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CSDWindows* pThis = (CSDWindows*)GetWindowPtr0(hwnd); LRESULT lRes = 0; if (uMsg < WM_USER) { return ::DefWindowProc(hwnd, uMsg, wParam, lParam); } else { switch (uMsg) { case WM_INVOKE_ON_RIGHT_THREAD: pThis->_DoInvokeCookie((DISPID)wParam, (LONG)lParam, FALSE); break; } } return lRes; }