|
|
#include "priv.h"
#include "dspsprt.h"
#include "basesb.h"
#include "cnctnpt.h"
#include "stdenum.h"
#include "winlist.h"
#include <varutil.h>
#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; }
|