Detecting change in folder view mode
See the question and my original answer on StackOverflowIt's indeed DISPID_VIEWMODECHANGED
for DShellFolderViewEvents
, but it's quite complex to get it.
Here is a Console App sample code that monitors all view mode changes for all currently open Explorer views. So to test it you must:
- open some Explorer view(s);
- run the program. When you change any of the opened folder view mode, you should see the event printed.
Some reference links:
Receiving a notification any time the selection changes in an Explorer window
Dispatch interfaces as connection point interfaces
int main()
{
CoInitialize(NULL);
{
CComPtr<IShellWindows> windows;
windows.CoCreateInstance(CLSID_ShellWindows);
long count = 0;
windows->get_Count(&count);
// array to remember one sink for one view
CAtlArray<CComPtr<CShellFolderViewEventsSink>> sinks;
for (int i = 0; i < count; i++)
{
// get window #i
CComPtr<IDispatch> disp;
windows->Item(CComVariant(i), &disp);
if (disp)
{
// get top level browser
CComPtr<IShellBrowser> browser;
CComQIPtr<IServiceProvider>(disp)->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&browser));
if (browser)
{
// get shell view
CComPtr<IShellView> view;
browser->QueryActiveShellView(&view);
if (view)
{
// get the ShellViewFolder (scripting) object
CComPtr<IDispatch> viewDisp;
view->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&viewDisp));
if (viewDisp)
{
CComPtr<IShellFolderViewDual> fview;
viewDisp->QueryInterface(&fview);
if (fview)
{
CComPtr<CShellFolderViewEventsSink> sink;
sink.Attach(new CShellFolderViewEventsSink());
sinks.Add(sink);
sink->Connect(fview);
}
}
}
}
}
}
// wait for user input (and pump messages)
MessageBox(nullptr, L"Click to stop listening", L"View Events", MB_OK);
for (int i = 0; i < sinks.GetCount(); i++)
{
sinks.GetAt(i)->Disconnect();
}
}
CoUninitialize();
return 0;
}
// class that checks for DISPID_VIEWMODECHANGED
class CShellFolderViewEventsSink : public CDispInterfaceBase<DShellFolderViewEvents>
{
public:
CShellFolderViewEventsSink() { }
HRESULT Invoke( DISPID dispid, DISPPARAMS* pdispparams, VARIANT* pvarResult)
{
switch (dispid)
{
case DISPID_VIEWMODECHANGED:
printf("DISPID_VIEWMODECHANGED called\n");
break;
}
return S_OK;
}
};
// support class to hook IDispatch events
template<typename DispInterface>
class CDispInterfaceBase : public DispInterface
{
LONG m_cRef;
CComPtr<IConnectionPoint> m_spcp;
DWORD m_dwCookie;
public:
CDispInterfaceBase() : m_cRef(1), m_dwCookie(0) { }
// IDispatch
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
*ppv = nullptr;
HRESULT hr = E_NOINTERFACE;
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == __uuidof(DispInterface))
{
*ppv = static_cast<DispInterface*>(static_cast<IDispatch*>(this));
AddRef();
hr = S_OK;
}
return hr;
}
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_cRef); }
IFACEMETHODIMP_(ULONG) Release() { LONG cRef = InterlockedDecrement(&m_cRef); if (!cRef) delete this; return cRef; }
// IDispatch
IFACEMETHODIMP GetTypeInfoCount(UINT* pctinfo) { *pctinfo = 0; return E_NOTIMPL; }
IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { *ppTInfo = nullptr; return E_NOTIMPL; }
IFACEMETHODIMP GetIDsOfNames(REFIID, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { return E_NOTIMPL; }
IFACEMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
if (pvarResult) VariantInit(pvarResult);
return Invoke(dispid, pdispparams, pvarResult);
}
virtual HRESULT Invoke(DISPID dispid, DISPPARAMS* pdispparams, VARIANT* pvarResult) = 0;
public:
HRESULT Connect(IUnknown* punk)
{
CComPtr<IConnectionPointContainer> spcpc;
HRESULT hr = punk->QueryInterface(IID_PPV_ARGS(&spcpc));
if (SUCCEEDED(hr)) hr = spcpc->FindConnectionPoint(__uuidof(DispInterface), &m_spcp);
if (SUCCEEDED(hr)) hr = m_spcp->Advise(this, &m_dwCookie);
return hr;
}
void Disconnect()
{
if (m_dwCookie)
{
m_spcp->Unadvise(m_dwCookie);
m_spcp.Release();
m_dwCookie = 0;
}
}
};