windows-nt/Source/XPSP1/NT/shell/shdocvw/swindows.cpp
2020-09-26 16:20:57 +08:00

1133 lines
30 KiB
C++

#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()
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
};
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;
};
#ifdef DEBUG // used by DBGetClassSymbolic
EXTERN_C const int SIZEOF_CSDWindows = SIZEOF(CSDWindows);
#endif
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)
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
// 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
DPA_AppendPtr(m_hdpa, pwd);
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
DPA_AppendPtr(m_hdpaPending, pwd);
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 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);
//ASSERT(cwindow==0);
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)
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
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;
}