更改文件扩展名上下文菜单
一种简单灵活的更改文件扩展名的方法。

引言
本文将介绍一个 Windows 外壳扩展,旨在以灵活简单的方式更改文件扩展名。使用此扩展,用户可以单独管理文件名和扩展名。
默认情况下,资源管理器会隐藏文件扩展名,这没问题,直到需要更改文件扩展名。在 Windows 操作系统中,文件扩展名决定了文件类型以及打开所选文件的应用程序,但有时我们会发现文件的扩展名与实际文件内容不符。最常见的例子是类 Unix 操作系统中创建的文本文件。类 Unix 系统通常使用换行符(0xAh)作为新行分隔符,而 Windows 使用回车符+换行符字符对(0xDh 0xAh)。在 Windows 的记事本中查看时,类 Unix 生成的*.txt 文件通常显示为一团没有正确换行的文本。对于大文件,这使得文本难以阅读,并且几乎不可能手动更正所有换行符。快速解决方法是在 Wordpad 中打开它,或者将文件扩展名从*.txt 重命名为*.rtf。我个人使用第二个选项。另一个例子是漫画迷们熟知的:著名的*.cbz 文件,它们只是图片压缩包,其存档扩展名从*.zip 更改为*.cbz。底线是,手动更改多个文件的扩展名并不方便。
此代码的构思也源于 Windows XP 处理文件名的方式。在 Windows XP 中,当选择一个文件进行重命名时(假设扩展名未隐藏),文件名和扩展名部分都会被选中,用户必须只突出显示扩展名部分才能更改它。而且,这只对单个文件有效。在 Vista 中,当两者都可见时,默认情况下会突出显示名称部分。
代码
激发此代码灵感的文章之一是 Shell Renamer,代码核心取自文章 如何使用上下文菜单外壳扩展中的子菜单,因此建议先阅读它,因为本文是基于它的。
此代码使用 Visual C++ 2005 Express 版、Platform SDK 2003 R2、WTL 8.0 和 ResEdit 编译,因此任何人都可以构建该项目。
由于这是一个外壳扩展,代码使用简单的异常处理来防止任何可能导致 Windows 资源管理器崩溃的不期望的事件。重命名是通过 Win32 SHFileOperation
函数完成的,文件一次只能操作一个,这样资源管理器就可以为处理的每个文件提供“撤销”步骤。为了在资源管理器中启用“撤销”操作,fFlags
成员被设置为 FOF_ALLOWUNDO。为了正确格式化 SHFILEOPSTRUCT
,必须在文件名缓冲区末尾添加一个额外的终止 null '\0'。
SHFILEOPSTRUCT lpFileOp;
memset(&lpFileOp,0,sizeof(lpFileOp));
lpFileOp.hwnd=NULL;
lpFileOp.fFlags=FOF_ALLOWUNDO;
lpFileOp.wFunc=FO_RENAME;
fname+='\0';//add buffer-terminating null to old name
lpFileOp.pFrom=fname;
toname=splitter.drive+splitter.dir+splitter.filename+_T(".")+newext;
lpFileOp.pTo=toname;
if (0!=SHFileOperation(&lpFileOp)) {/*MSGBOX0("SHFileOperation failed");
*/return false;}
由于使用 SHFileOperation
,尝试更改快捷方式的扩展名会导致目标路径解析,并对目标执行重命名操作。在这种情况下,另一个奇怪的事情是对话框相对于资源管理器所有者窗口不是模态的。
默认情况下,第一个右键单击的文件的扩展名被选为新扩展名。扩展名在外壳菜单条目中以大写形式显示,但实际字符串的大小写由用户决定,因为有些程序允许用户在保存文件时选择大写或小写扩展名(例如 mp3 和 MP3)。
新扩展名会检查无效字符,并模拟默认的 Windows 资源管理器行为。
如果您看不到任何气球提示,请查找注册表项 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced,并将 DWORD 值 EnableBalloonTips
设置为 1 以启用系统范围的气球提示。
菜单条目在外壳上下文菜单中的位置完全取决于 *.rgs 文件中字符串值的字母顺序。对于此外壳扩展,使用了项目的 CLSID(A5096FA1-32DC-4392-A829-445558F0F4B5)将菜单条目放置在外壳上下文菜单的中间位置。
如果右侧的字符串值是,例如,AAAAAAAAAA_go_to_the_top__
,则菜单条目将位于外壳上下文菜单的顶部。
NoRemove *
{
NoRemove ShellEx
{
NoRemove ContextMenuHandlers
{
{A5096FA1-32DC-4392-A829-445558F0F4B5} = s '{AAAAAAAAAA_go_to_the_top__}'
}
}
}
您还需要匹配 *.rgs 文件中的 CLSID 条目。
NoRemove CLSID
{
ForceRemove { AAAAAAAAAA_go_to_the_top__} = s 'ChangeExtMenuExt Class'
{
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
}
}
GUI 布局和 XP 风格感知
菜单条目插入了易于识别的菜单位图,以提供快速的视觉标识。
此外壳扩展的另一个重要问题是,由于它是基于对话框的,因此需要启用 WinXP 主题以实现视觉一致性。为了使对话框启用 XP 主题,在任何 #includes
之前,需要在 stdafx.h 中设置以下值:
#define VC_EXTRALEAN //necessary
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#endif
#define _ATL_APARTMENT_THREADED
#define ISOLATION_AWARE_ENABLED 1 //XP style awareness
这些截图展示了外观差异
主题感知 | 不感知主题 |
![]() |
![]() |
XP 主题感知话题在 MSDN 的文章 编写外壳扩展的完全傻瓜指南 - 第二部分、将 Windows XP 可视样式应用于应用程序 和 使用 Windows XP 可视样式 中有进一步讨论。
为了向用户提供一个漂亮且交互式的 GUI,我使用了 WTL CBitmapHyperLink 类,而不是“帮助”按钮。
请注意! — 如果您不在调试版本 DLL 的目录(hlppath
变量指向的位置)中放入任何 HTML 帮助文件(*.chm),您将在 CBitmapHyperlink
的一个 ASSERT 中遇到一个讨厌的资源管理器崩溃。在发布版本中不会发生这种情况。
我决定放一个漂亮的 clickable icon,而不是一个“关于”按钮。我将实现一个更花哨的 owner-drawn 按钮留给读者练习。
推荐阅读
Michael Dunn 的 编写外壳扩展的完全傻瓜指南。
Dino Esposito(MSDN,URL 可能会更改)的 新的图形界面:使用新的 Windows XP 外壳功能增强您的程序。
错误和问题
请在此文章的留言板中报告您发现的所有问题和错误。