1206 lines
31 KiB
C++
1206 lines
31 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()
|
|
{
|
|
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;
|
|
}
|