19
2024
01
15:13:58

ShellFolder: 以重定向桌面为例



推荐点击下面图片,通过本站淘宝优惠价购买:

image.png

前言

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

对于常规用户来说,Windows的资源管理器自带的桌面的UserAssist面板配置(即右键桌面文件夹项的属性面板)已经能够满足此要求。利用面板的位置栏,能够方便地完成对桌面的迁移,具体操作见下图: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接口实例的实现,近似于一个简单的路径“快捷方式”,相比于子系统文件系统、共享文件夹、网络邻居这类高级实例还差得远。

共勉!


本文链接:http://www.hqyman.cn/post/4858.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:





休息一下,本站随机推荐观看栏目:


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

您的IP地址是: