背景:C#/.NET 专家,C++/MFC/Windows Shell 基础知识
环境:Windows 10(德语)、Visual Studio 2017 (15.9.56)、C++、Windows SDK 10.0.19041.0
任务:通过代码:C++/Windows Shell 在 Windows 资源管理器(当前打开的窗口/当前活动文件夹)中显示/隐藏列
方法:IShellWindows -> IShellBrowser -> 活动 IShellView -> IColumnManager -> Windows 资源管理器中列的 SetColumnInfo
结果:显示列(隐藏的列)有效! 隐藏列(可见)不起作用:Windows 资源管理器什么也不做/没有错误!
以下代码的前两个函数显示了获取 IColumnManager(没有任何 HRESULT 处理的紧凑代码)和列的方法。 第三个函数显示了 GetColumnInfo 和 SetColumnInfo 的代码。
void BOShell::processActiveShellView()
{
CoInitialize(NULL);
IShellWindows* shwnds;
CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&shwnds));
VARIANT index;
index.vt = VT_I4;
index.lVal = 0;
// opened window (there is only one)
IDispatch* disp;
shwnds->Item(index, &disp);
IShellBrowser* shbrowser;
IUnknown_QueryService(disp, SID_STopLevelBrowser, IID_PPV_ARGS(&shbrowser));
// active shell view (selected folder)
IShellView* shview;
shbrowser->QueryActiveShellView(&shview);
processColumns(shview);
shbrowser->Release();
disp->Release();
shwnds->Release();
CoUninitialize();
}
void BOShell::processColumns(IShellView* shview)
{
IColumnManager* colmgr;
shview->QueryInterface(IID_PPV_ARGS(&colmgr));
UINT cols = 0;
colmgr->GetColumnCount(CM_ENUM_ALL, &cols);
PROPERTYKEY* propkeys = new PROPERTYKEY[cols];
colmgr->GetColumns(CM_ENUM_ALL, propkeys, cols);
for (UINT ii = 0; ii < cols; ii++)
{
setColumnInfo(colmgr, propkeys[ii]);
}
delete propkeys;
colmgr->Release();
}
void BOShell::setColumnInfo(IColumnManager* colmgr, PROPERTYKEY& propkey)
{
CM_COLUMNINFO ci;
ci.cbSize = sizeof(ci);
ci.dwMask = CM_MASK_NAME | CM_MASK_STATE | CM_MASK_WIDTH;
colmgr->GetColumnInfo(propkey, &ci);
// dwState is 1 (CM_STATE_VISIBLE)
// hide column Typ
if (wcscmp(ci.wszName, L"Typ") == 0)
{
ci.dwMask = CM_MASK_STATE;
ci.dwState = CM_STATE_NONE;
HRESULT hr = colmgr->SetColumnInfo(propkey, &ci);
// OK but nothing happened!
colmgr->GetColumnInfo(propkey, &ci);
// dwState is still 1 (CM_STATE_VISIBLE)
}
}
该列的 dwState 1 (CM_STATE_VISIBLE)。
将 dwState 设置为 0 (CM_STATE_NONE) 的 SetColumnInfo 函数返回 OK。
SetColumnInfo 之后的 GetColumnInfo 调用显示 dwState 仍然是 1 而不是 0!所以SetColumnInfo似乎什么也没做!
如上所述:相反(从隐藏(0)到可见(1))有效! (设置列的宽度也有效!)
问题:这段代码正确吗(我认为是正确的)?这是一个在 Windows 资源管理器中不起作用的已知问题吗?还有另一种方法来隐藏列吗? 欢迎任何提示!
在我看来,另一种方法是通过右键单击列标题时出现的上下文菜单来完成此操作。但我发现有关如何通过代码访问此上下文菜单的信息为零。 任何有关如何获取此上下文菜单的建议也将不胜感激!
要隐藏列,您必须使用 IColumnManager::SetColumns 方法重新定义整组列。
这里是一些示例代码(我使用了错误检查和 ATL 的智能指针来避免可能的内存泄漏)。在此代码中,如果“标题”列是最后一个列,我会删除它,因此只需在测试它之前确保它是第一个打开的视图中的最后一个列即可:
#include <Windows.h>
#include <atlbase.h>
#include <exdisp.h>
#include <shobjidl_core.h>
#include <ShlGuid.h>
#include <stdio.h>
int main()
{
CoInitialize(NULL);
{
CComPtr<IShellWindows> windows;
ATLASSERT(SUCCEEDED(windows.CoCreateInstance(CLSID_ShellWindows)));
CComPtr<IDispatch> first;
ATLASSERT(SUCCEEDED(windows->Item(CComVariant(0), &first)));
CComPtr<IColumnManager> mgr;
ATLASSERT(SUCCEEDED(IUnknown_QueryService(first, SID_SFolderView, IID_PPV_ARGS(&mgr))));
// get the visible columns
auto flags = CM_ENUM_VISIBLE;
UINT count = 0;
ATLASSERT(SUCCEEDED(mgr->GetColumnCount(flags, &count)));
CComHeapPtr<PROPERTYKEY> pks;
pks.Allocate(count);
ATLASSERT(SUCCEEDED(mgr->GetColumns(flags, pks, count)));
// remove last if it's "Title"
if (count > 1)
{
CM_COLUMNINFO ci{ sizeof(CM_COLUMNINFO) };
ci.dwMask = CM_MASK_NAME | CM_MASK_STATE | CM_MASK_WIDTH;
ATLASSERT(SUCCEEDED(mgr->GetColumnInfo(pks[count - 1], &ci)));
if (!lstrcmp(ci.wszName, L"Title"))
{
ATLASSERT(SUCCEEDED(mgr->SetColumns(pks, count - 1)));
}
}
}
CoUninitialize();
return 0;
}