今天的小程序演示了如何操作桌面图标的位置。
整个程序只是搭建脚手架,让我们可以调用和. IFolderView::GetItemPosition IFolderView::SelectAndPositionItems
首先,我们调整我们之前看到IFolderView
的从窗口中提取的代码。
提醒:这些“小程序”不进行错误检查,因为它们旨在作为演示,而不是生产就绪的应用程序。
void FindDesktopFolderView(REFIID riid, void **ppv)
{
CComPtr<IShellWindows> spShellWindows;
spShellWindows.CoCreateInstance(CLSID_ShellWindows);
CComVariant vtLoc(CSIDL_DESKTOP);
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty,
SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
CComPtr<IShellBrowser> spBrowser;
CComQIPtr<IServiceProvider>(spdisp)->
QueryService(SID_STopLevelBrowser,
IID_PPV_ARGS(&spBrowser));
CComPtr<IShellView> spView;
spBrowser->QueryActiveShellView(&spView);
spView->QueryInterface(riid, ppv);
}
该FindDesktopFolderView
函数从之前的文章中获取代码,并使用它来提取桌面的 shell 视图。除了对 的调用之外,这里的所有内容都应该看起来很熟悉(只是穿着不同的服装),FindWindowSW
因为我们正在按位置寻找特定的窗口,而不是仅仅枚举所有窗口。
的第一个参数FindWindowSW
。是我们要查找的文件夹。在我们的例子中,我们正在寻找桌面。
第二个参数是保留的,必须是VT_EMPTY
.
第三个参数描述了我们正在寻找的窗口类型。我们使用特殊标志(从 Windows Vista 开始提供)说,“嘿,我知道桌面不是人们在寻找资源管理器窗口时想到的那种东西,但我知道我在说什么,所以给我吧。”SWC_DESKTOP
第四个参数接收窗口句柄,这对我们没有兴趣,但参数是强制性的,所以我们必须给它一些东西。
第五个参数指定搜索选项。我们常说,“请在第六个参数中返回”。第六个参数是我们希望返回的位置。SWFO_NEEDDISPATCH
IDispatch
IDispatch
好的,我们已经有足够的能力枚举所有桌面图标并打印它们的名称和位置。
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shlobj.h>
#include <exdisp.h>
#include <shlwapi.h>
#include <atlbase.h>
#include <atlalloc.h>
#include <stdio.h>
// CCoInitialize incorporated by reference
int __cdecl wmain(int argc, wchar_t **argv)
{
CCoInitialize init;
CComPtr<IFolderView> spView;
FindDesktopFolderView(IID_PPV_ARGS(&spView));
CComPtr<IShellFolder> spFolder;
spView->GetFolder(IID_PPV_ARGS(&spFolder));
CComPtr<IEnumIDList> spEnum;
spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));
for (CComHeapPtr<ITEMID_CHILD> spidl;
spEnum->Next(1, &spidl, nullptr) == S_OK;
spidl.Free()) {
STRRET str;
spFolder->GetDisplayNameOf(spidl, SHGDN_NORMAL, &str);
CComHeapPtr<wchar_t> spszName;
StrRetToStr(&str, spidl, &spszName);
POINT pt;
spView->GetItemPosition(spidl, &pt);
wprintf(L"At %4d,%4d is %ls\n", pt.x, pt.y, spszName);
}
return 0;
}
拿到后IFolderView
,我们也索要相应的IShellFolder
。这实际上不是枚举图标所必需的,但它可以让我们打印它们的名称。
我们询问视图的Items
枚举,然后继续枚举每个项目。对于每个项目,我们询问IShellFolder
其名称,并询问IFolderView
其位置。然后我们打印结果。
好的,这很简洁,但您可以做的不仅仅是查询职位。您也可以修改它们。
int __cdecl wmain(int argc, wchar_t **argv)
{
CCoInitialize init;
CComPtr<IFolderView> spView;
FindDesktopFolderView(IID_PPV_ARGS(&spView));
CComPtr<IEnumIDList> spEnum;
spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));
for (CComHeapPtr<ITEMID_CHILD> spidl;
spEnum->Next(1, &spidl, nullptr) == S_OK;
spidl.Free()) {
POINT pt;
spView->GetItemPosition(spidl, &pt);
pt.x += (rand() % 5) - 2;
pt.y += (rand() % 5) - 2;
PCITEMID_CHILD apidl[1] = { spidl };
spView->SelectAndPositionItems(
1, apidl, &pt, SVSI_POSITIONITEM);
}
return 0;
}
这一次,我们不再打印项目的名称和位置,而是将图标位置随机抖动几个像素,然后将抖动的坐标设置为新位置。
关闭桌面上的自动排列图标和对齐图标到网格,然后运行该程序。嘿,看,你的图标随机移动了几个像素。
对于额外的 hijinx,在进入循环之前调用 (以编程方式关闭自动排列和对齐网格),然后将此程序放入循环中,并将其滑入朋友(或敌人)的计算机上。spView->SetCurrentFolderFlags(FWF_AUTOARRANGE | FWF_SNAPTOGRID, 0)
更严重的是,我们可以将这两部分放在一起,制作一个保存和恢复桌面图标位置的程序。
第二个提醒:这些“小程序”不进行错误检查,因为它们旨在用作演示,而不是生产就绪的应用程序。
void SavePositions(IFolderView *pView, PCWSTR pszFile)
{
CComPtr<IStream> spStream;
SHCreateStreamOnFileEx(pszFile, STGM_CREATE | STGM_WRITE,
FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, &spStream);
CComPtr<IEnumIDList> spEnum;
pView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));
for (CComHeapPtr<ITEMID_CHILD> spidl;
spEnum->Next(1, &spidl, nullptr) == S_OK;
spidl.Free()) {
IStream_WritePidl(spStream, spidl);
POINT pt;
pView->GetItemPosition(spidl, &pt);
IStream_Write(spStream, &pt, sizeof(pt));
}
}
该SavePositions
函数枚举视图中的所有图标并将它们的标识和位置写入文件。
void RestorePositions(IFolderView *pView, PCWSTR pszFile)
{
CComPtr<IStream> spStream;
SHCreateStreamOnFileEx(pszFile, STGM_READ,
FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &spStream);
POINT pt;
for (CComHeapPtr<ITEMID_CHILD> spidl;
SUCCEEDED(IStream_ReadPidl(spStream, &spidl)) &&
SUCCEEDED(IStream_Read(spStream, &pt, sizeof(pt)));
spidl.Free()) {
PCITEMID_CHILD apidl[1] = { spidl };
pView->SelectAndPositionItems(1, apidl, &pt, SVSI_POSITIONITEM);
}
}
该RestorePositions
函数执行相反的操作。它从文件中读取身份和位置,并调用将项目移动到其先前保存的位置。IFolderView::SelectAndPositionItems
int __cdecl wmain(int argc, wchar_t **argv)
{
if (argc != 3) {
wprintf(L"Usage: %ls save filename\n"
L" %ls restore filename\n", argv[0], argv[0]);
return 0;
}
CCoInitialize init;
CComPtr<IFolderView> spView;
FindDesktopFolderView(IID_PPV_ARGS(&spView));
if (wcscmp(argv[1], L"save") == 0) {
SavePositions(spView, argv[2]);
} else if (wcscmp(argv[1], L"restore") == 0) {
RestorePositions(spView, argv[2]);
}
return 0;
}
剩下的就是编写基于命令行参数调用SavePositions
or函数的主程序。RestorePositions
练习:讨论如果重命名桌面上的项目,然后尝试恢复其位置会发生什么。可以做些什么来解决这个问题?