【问题标题】:Detecting change in folder view mode检测文件夹视图模式的变化
【发布时间】:2026-01-08 04:10:02
【问题描述】:

有没有办法在 IFolderView 的查看模式中挂钩或接收更改(例如,详细信息、图标等)?

我看到DISPID_VIEWMODECHANGED,但我不明白它属于哪个接收器。我不认为它属于DShellFolderViewEvents

另外,我不相信 DWebBrowserEvents2DISPID_DOCUMENTCOMPLETE 会在视图模式更改时触发。

感谢您的任何意见。

【问题讨论】:

  • 一旦提出问题,您就无法从根本上更改问题或标签。你可以问另一个问题并链接到它。
  • 没关系,我使用SINK_ENTRY_INFO 得到它。再次感谢您的回答。我现在就接受它。

标签: c++ shell winapi com atl


【解决方案1】:

DShellFolderViewEvents 确实是DISPID_VIEWMODECHANGED,但获取起来相当复杂。

这是一个控制台应用程序示例代码,用于监控所有当前打开的资源管理器视图的所有视图模式更改。因此,要对其进行测试,您必须:

  1. 打开一些资源管理器视图;
  2. 运行程序。当您更改任何打开的文件夹视图模式时,您应该会看到打印的事件。

一些参考链接:

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;
    }
  }
};

【讨论】:

  • 您确定它是 DShellFolderViewEvents 的一部分吗?接口定义另有说明:*.com/a/59565687/13366655
  • @loop123123 - 它们在 shdispid.h 中被列为“shell 文件夹视图的事件”,猜猜看,它有效。 dispinterface 只是一个 IDispatch 实现 + 一个 dispid 列表 + 可能的参数。 idl 中的 DShellFolderViewEvents 声明几乎没用。重要的是 dispids 列表,即使有参数(在 pdispparams 中),我们也不需要/使用它们。可能是 shldisp.idl(以及 shell32.dll 中嵌入的 .tlb)不是最新的。
  • 那么是不是不能用ATL sink map 下沉呢?或者是否可以手动更新定义?
  • 建议:将所有班级成员标记为noexcept。这可以防止实现尝试通过 ABI 传递异常。
  • @loop123123 - 你的问题不在 ATL 上,但我不明白为什么它不起作用。 ATL 接收器也使用 dispid (既然这就是全部,还有什么?)。也许向导不起作用,是的,因为 idl 和 tlb 缺少很多信息。
最近更新 更多