ShellFolder: 以重定向桌面为例

前言

对于部分Windows用户来说,出于个性化或系统盘污染等其它方面考虑,可能会有重定向桌面地址的需求。

对于常规用户来说,Windows的资源管理器自带的桌面的UserAssist面板配置(即右键桌面文件夹项的属性面板)已经能够满足此要求。利用面板的位置栏,能够方便地完成对桌面的迁移,具体操作见下图:
在这里插入图片描述

NOTE

需要注意的是,当使用上述方法时,请确保注册表项完好。若用户默认的桌面ShellFolder项被删除或被篡改破坏,位置栏将不会被继承。此时需还原注册表或通过下文所列方法完成桌面重定向。

探索:如何去界面化完成桌面重定向?

从注册表切入

由于对于一般用户而言,将问题精准定位是困难的,故下文假定阅读者有一定的搜索能力并对Windows的资源管理器与注册表有一定了解。

方向①:借助搜索引擎,如何操作?

一般情况下,在使用搜索引擎直搜“切换桌面地址”或其它关键词后,都能够得到以下答案:

定位注册表项:
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
修改Desktop键值为重定向后的桌面地址
重启Explorer.exe(即资源管理器)

通过实践不难验证该方法是有效的。
然而不可否认的是,使用该方法相较于利用GUI界面迁移桌面不仅更低效,且存在一定的风险,故而意义并不大。(当注册表未损坏时)

不过通过上述流程经过简单的分析可以额外注意到以下内容:

  1. 桌面是依靠Explorer.exe渲染的
  2. Explorer.exe通过读取上述注册表项的键值来定位桌面的地址
  3. 独立改动注册表并不会主动引发Explorer.exe的刷新
  4. ☆☆☆☆☆桌面我的文档等其他可重定向的文件夹可能同属于一类事物,即Shell Folder
方向②:基于已知的大方向,如何自行探索?

前置经验:

  1. Explorer.exe提供包含桌面在内的所有文件系统浏览服务
  2. SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer为Explorer.exe独属的注册表项

基于上述经验,以下给出两类探索方法:

(一)ProcessMonitor 监控法

该方法逻辑在于监控桌面重定向实际发生过程中系统范畴内相关对象的一切行为来定位关联的变量。
方法采用Microsoft Sysinternals Utilities列表提供的Process Monitor工具完成对进程行为的监控。

简单熟悉Process Monitor的操作方法后,考虑到非编程方法方便直接操作的只有注册表,故而将监控的目标设定为Explorer.exeOperation筛选为包含 Reg 且包含 Key。再根据经验判断,能够显式的与变化关联的应当只有写操作,对于注册表来说即RegSetKeyValueRegCreateKey方法。

完成以上设定后,操作系统自带的桌面移动面板,监控确定按钮按下桌面完成迁移时间段内的行为。
以下为监控期间发生的行为(仅供参考):

在这里插入图片描述
因为重定向是地址字符串的变动,所以优先考虑注册表键值类型为REG_SZREG_EXPAND_SZ的键。可以发现变动的恰好是User Shell FoldersShell Folders两项的Desktop值。其后浏览其他键值变更,可以简单判断并非是影响目标的关键因素。此时可以确定该两项注册表项即为影响桌面定位的关键。

手动在regedit修改两项的关联键值,并尝试手动F5刷新,发现并未桌面并未更新。重启资源管理器后完成刷新。

重复监控过程,观察Explorer.exe在注册表之外的其它操作,可以发现在上述注册表项修改完之后Explorer.exe直接进行了文件映射的创建。此时基本可以判断在注册表项更新后或是更新前,存在某项操作通知了资源管理器操作的发生,而该操作并未被Process Monitor捕获,故无法继续使用非编程方法完成桌面的重定向。

(二)猜测 + 黑箱方法

尝试定位影响桌面路径的因素时,几个关键词是必不可少的。

合理利用ExplorerDesktopFolder${当前桌面路径}${重定向后桌面路径}几个关键字搜索筛选出可能相关的表项,并考察桌面重定向前后表项键值的变更情况,同样不难筛选出Shell FoldersUser Shell Folders两个关联项。

后续操作同上。

总结

上述几种方法都能够较快的筛选出关联注册表项,但是没有编程方法的介入,都难以像Windows自带面板提供的操作那样完成高效、无缝切换。

从WINAPI切入

方法①中,可以注意到桌面跟一个名为Shell Folder的对象脱不开关系。通过对MS DOC的查阅可以知道,Shell Folder是Shell对象范畴内对文件夹的一类拓展,其扩大了文件夹的范畴,允许通过自定义的具体实现将异形对象抽象为文件夹实例。其中链接向物理路径的Shell Folder实现于系统内置的Shell File System Folder接口。

Shell Folder在微软文档中并无专门的篇幅描述,相关的样例代码也给得不多。对于初次接触到WINAPI Shell编程的人,庞大的框架与零散的API可能导致思路梳理困难,流程运行逻辑不清。
如若实在需要实例代码,可以尝试访问HotExample进行搜索。
此外,本文不会对相关内容进行过多阐述。

在Windows中,每一个Shell实例(通常)都有GUID描述符,对于Shell Folder,其GUID可以在WINSDK - knownfolders.h中找到。

在其中可以看到Desktop的GUID为{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}。转向对注册表的访问,可以在SOFTWARE\Classes\CLSID下找到该项。

浏览子项与键值可以发现,Desktop继承自{0E5AAE11-A475-4c5b-AB00-C66DE400274E}也即Shell File System Folder,而从Instance\InitPropertyBag\TargetKnownFolder可以知道,该Shell Folder(即Desktop)是指向同名实例的。
需要提及的是,Instance\InitPropertyBag\TargetFolderPathTargetKnownFolder一样都是用于指定文件夹路径的,不同的是,前者指定的是具体路径,而后者指定了预定义的Folder ID

不过,借助相关资料和注册表搜索可以发现,此处的{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}并非是一个自引用。在CLSID下定义的Shell Folder最终会引用在SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions中定义的Folder ID,最终传递给Explorer.exe

以下是二者的注册表项层级结构:

+ CLSID\{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}
	- DescriptionID
	- System.IsPinnedToNameSpaceTree
	+ DefaultIcon
		- (Default): C:\Windows\system32\imageres.dll,-183
	+ InProcServer32
		- (Default): C:\Windows\system32\shell32.dll
		- ThreadingModel: Both
	+ Instance
		- CLSID: {0E5AAE11-A475-4c5b-AB00-C66DE400274E}
		+ InitPropertyBag
  			- Attributes
  			- TargetKnownFolder: {B4BFCC3A-DB2C-424C-B029-7FE99A87C641}
	+ ShellFolder
		- Attributes
		- FolderValueFlags
		- SortOrderIndex

+ FolderDescriptions\{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}
	- Attributes
	- Category
	- Icon: C:\Windows\system32\imageres.dll,-183
	- LocalizedName: @C:\Windows\system32\shell32.dll,-21769
	- Name: Desktop
	- PreCreate
	- PublishExpandedPath
	- RelativePath: Desktop
	- Roamable
	+ PropertyBag
  		- NoCustomize

相较于TargetFolderPath的直接,TargetKnownFolder则是绕了点弯来实现目标的定位。
从黑箱方法大致可以推断Explorer.exeShell Folder处理流程如下(不保证为系统实际情形):

handle
targeting
access
Explorer
CLSID of Known Folder
Folder Description of Target
Declare Known Folder Name
Retrival Path from Shell Folder
notify

FolderDescription\<GUID>\Name将定义Known Folder的名称,而Explorer.exe将通过该名称在Shell FolderUser Shell Folder中检索得到其链接的路径。

了解上述流程之后,其实就可以发现重定向文件夹只有两步操作:

  • 变更Known Folder的目标路径
  • 通知资源管理器Known Folder已经发生变化

在Shell对象编程中,有专门的方法SHChangeNotify用于通知Shell对象相关事件的发生。
仔细阅读文档后,对于方法第一参数的选择可以是SHCNE_UPDATEDIR或者SHCNE_UPDATEITEM

/** path is the re-located path of target known folder */
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_FLUSH | SHCNF_PATH, path, nullptr);

手动修改注册表项并使用上述代码完成对更新事件的分发,发现桌面依旧没有变化。由上述操作的总结,那么在假定事件成功分发的情况下,则可以怀疑被分发的事件并不实际存在,即:“变更 Known Folder 的目标路径”并未真正完成。

查阅官方文档发现存在专门的方法SHSetKnownFolderPath完成对Known Folder目标路径的设置。
重写核心代码如下:

#include <shlobj.h>
bool SwitchDesktopTo(const wchar_t *path) {
	auto hr = SHSetKnownFolderPath(FOLDERID_Desktop, 0, nullptr, path);
	if (!SUCCEEDED(hr)) return false;
	LPITEMIDLIST list = nullptr;
	SHGetKnownFolderIDList(FOLDERID_Desktop, 0, nullptr, &list);
	if (!list) return false;
	SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_FLUSH | SHCNF_IDLIST, list, nullptr);
	ILFree(list);
	return true;
}

目标达成,重定向的效果与系统面板提供的方法并无肉眼上的差别。

后期使用Process Monitor对该过程进行监控发现,注册表项的变动依旧限定在Shell FolderUser Shell Folder之内,故大致可以推断之前失败的原因在于SHSetKnownFolderPath除了修改表项键值之外还在方法内部做了额外的操作;也正是因为该部分操作未被纳入手动hack,又导致了SHChangeNotify没有成功地分发有效事件。

写在最后

到此为止,通过桌面重定向的探索,包含桌面实例在内的一系列IShellFolder接口实现的外部结构大致清朗的。但是不论是相关注册表项的各键值的含义还是内部handler实现都处于相对模糊的状态。

该部分再通过像本文这样浅显的方法探究就不适合了。归根到底,要搞清楚Shell Object,就得深入Windows Shell开发,就得通读文档,多联系多思考。

本文描述的Desktop等同类文件夹作为Shell File System Folder接口实例的实现,近似于一个简单的路径“快捷方式”,相比于子系统文件系统、共享文件夹、网络邻居这类高级实例还差得远。

共勉!


欢迎访问、贡献 git@github.com:zymelaii/IWinDEModifier.git

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周上行Ryer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值