使用 TweakJS 自定义通用对话框中的“我的位置”栏






4.74/5 (13投票s)
2005年12月27日
10分钟阅读

53447

816
自定义 Win 2K/XP 和 MS Office 中的打开/保存对话框。
目录
引言
作为其 Windows 增强工具的一部分,Microsoft 提供了一个名为 TweakUI 的强大工具。这个工具允许您(普通的 Windows XP 用户)在不直接修改注册表的情况下更改 XP 的一些默认设置。它将许多有用的设置直接呈现给您,而不是隐藏在注册表的深处,从而消除了您在修改注册表时常常会感到的恐惧和不安。
TweakUI 中“位置栏”页面是我发现特别有用的一个调整项。此页面允许您设置显示在 我的位置栏 中的文件夹。“我的位置栏”显示在 Windows 中的许多打开/保存对话框中。自定义此栏使我能够快速访问我最常保存文件的文件夹,每次保存文件都能节省几次点击。
然而,Microsoft Office 使用自己的打开/保存对话框,并且不遵循 TweakUI 的修改。
TweakJS 应运而生。TweakJS 类似于 TweakUI 中的“位置栏”页面,但有两个显著区别:
- 它同时为 Microsoft Office 和通用对话框自定义 我的位置栏。
- 它允许创建一组指向所选文件夹的快捷方式。
免责声明
尽管我已经尽一切努力使此代码安全正确地运行,但运行任何修改注册表的软件总是有风险的。就像 Mozilla 网站上的俏皮免责声明一样,无法保证 TweakJS 不会烧毁您的处理器、侮辱您的母亲或导致您出现严重的皮疹。对于此软件可能导致的这些或任何其他问题,我概不负责,但我会尽力修复报告的任何问题。如果这让您感到害怕,您可能不应该运行 TweakJS。
由于需要修改注册表,您很可能需要管理员权限才能使用 TweakJS。
用法
TweakJS 允许您选择最多五个文件夹显示在 我的位置栏 中。一旦在 TweakJS 中选择了文件夹,它们就会显示在任何显示 我的位置栏 的打开/保存对话框中。
“链接”选项会创建指向您选择添加到 我的位置栏 的文件夹的快捷方式。为什么要这样做?我这样做是因为它允许我创建一个可以放置在任务栏上的工具栏,从而可以轻松访问我选择的目录(请参阅下面的屏幕截图)。
请注意,在屏幕截图中,第一个链接的标题是 自定义链接。此链接是自动添加的。它指向存储所有快捷方式的文件夹,从而可以轻松访问该文件夹并根据需要添加更多快捷方式。
要使此工具栏可见:
- 右键单击任务栏。
- 选择“工具栏”|“新建工具栏”。
- 浏览您在 TweakJS 中选择的输出文件夹。
- 您可能需要通过将其拖到系统托盘旁边来调整工具栏的大小。
- 在移动/调整新工具栏的大小之前,可能需要解锁该工具栏(右键单击,取消选中“锁定任务栏”)。
背景
为了配置 Microsoft Office 中的通用对话框,我查阅了 Microsoft 知识库。 文章 20501 详细介绍了自定义 MS Office 2000 打开/保存对话框中 我的位置栏 所必需的注册表更改。注册表设置位于以下项下:HKCU\Software\Microsoft\Office\9.0\Common\OpenFind\Places。
数字 9.0 使这些设置特定于 Office 2000。Office XP 的数字是 10.0,Office 2003 的数字是 11.0。我没有 Office XP 或 2003 的访问权限,因此无法使用这些程序测试此代码。
TweakUI 修改的设置以不同的格式存储,并位于此处:HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\comdlg32\PlacesBar。显然,这些设置可以手动修改,但使用 TweakJS 更简单、更快捷。
写完此代码后,我发现了两个也允许自定义“我的位置”栏的应用程序,一个用于通用的 Windows 对话框,另一个用于 Microsoft Office 对话框。这些程序位于 此处。我没有使用过它们,所以无法保证。这些程序提供了一个附加功能,即能够保存和检索目录列表,如果您需要访问不同的目录集(取决于您正在处理的项目,甚至取决于您正在进行的工作类型),这可能会非常有用。如果对此功能有足够的需求,我将考虑在 TweakJS 中实现它。我仍然认为 TweakJS 是一个有价值的程序,因为它允许用户一次性自定义 MS Office 和 Windows 的通用对话框。
卸载
TweakJS 首次运行时,它会备份当前的 Windows 和 MS Office 通用对话框设置。将要修改的注册表项导出到两个文件:Office.reg 用于 MS Office 设置,Windows.reg 用于 Windows 设置。运行以下命令创建这些文件(第二个命令取决于检测到的 Office 版本,或是否检测到 Office):
[编辑注释:使用换行符以避免滚动。]
Regedit /e Windows.reg HKCU\Software\Microsoft\
Windows\CurrentVersion\Policies\comdlg32\PlacesBar
Regedit /e Office.reg "HKCU\Software\Microsoft\Office\9.0\
Common\OpenFind
按下“删除”按钮时,TweakJS 会创建一个注册表脚本 (remove.reg) 来删除 TweakJS 修改的所有注册表项。
[编辑注释:使用换行符以避免滚动。]
[-HKEY_CURRENT_USER\Software\Microsoft\Office\9.0\
Common\Open Find\Places]
[-HKEY_CURRENT_USER\Software\Microsoft\Windows\
CurrentVersion\Policies\comdlg32]
[-HKEY_CURRENT_USER\Software\TweakJS]
它首先运行它创建的脚本,删除 TweakJS 所做的所有更改,然后合并 TweakJS 在首次运行时创建的两个备份文件。它还会打开 TweakJS 运行所在的目录,以防您想删除可执行文件。
代码概述
TweakJS 是一个相当简单的程序,包括:
- 用户界面代码。
- 读写注册表代码。
- 检测已安装的 MS Office 版本的代码。
- 写入快捷方式文件的代码。
用户界面代码
TweakJS 中的组合框模仿了 TweakUI 中的对应项。CBrowseCombo
类封装了它们的功能。CBrowseCombo
是一个组合框,允许用户浏览要添加到组合框的文件夹。顾名思义,它派生自 CComboBox
。将 CBrowseCombo
设为派生类可以避免对话框类 CTweakJSDlg
必须处理其初始化或处理其浏览行为。由于有五个这样的组合框,这也消除了大量冗余代码。
一个正在使用的 CBrowseCombo
框。
组合框中的每个默认位置的名称都是通过从 shell 获取其 CSIDL 的显示名称来确定的。
bool CBrowseCombo::GetDisplayName(UINT csidl, CString& sDisplayName) { // A pointer to the shell's IMalloc interface IMalloc * pShellMalloc = NULL; // A pointer to the parent folder object's // IShellFolder interface. IShellFolder *psfParent; // The item's PIDL. LPITEMIDLIST pidlItem = NULL; // The item's PIDL relative to the parent folder. LPITEMIDLIST pidlRelative = NULL; // The structure for strings returned from // IShellFolder. STRRET str; // The display name of this folder TCHAR szDisplayName[MAX_PATH]= _T(""); HRESULT hr = SHGetMalloc(&pShellMalloc); bool bReturn = false; if (SUCCEEDED(hr)) { hr = SHGetSpecialFolderLocation(NULL, csidl, &pidlItem); if (SUCCEEDED(hr)) { hr = SHBindToParent(pidlItem, IID_IShellFolder, (void**)&psfParent, (LPCITEMIDLIST*)&pidlRelative); if (SUCCEEDED(hr)) { // Retrieve the display name memset(&str, 0, sizeof(str)); hr = psfParent->GetDisplayNameOf( pidlRelative, SHGDN_NORMAL, &str); if (SUCCEEDED(hr)) { StrRetToBuf(&str, pidlItem, szDisplayName, MAX_PATH); sDisplayName = szDisplayName; bReturn = true; } psfParent->Release(); } } // Clean up allocated memory if (pidlItem) pShellMalloc->Free(pidlItem); pShellMalloc->Release(); } return bReturn; }
在 CBrowseCombo
中显示的每个默认位置的项目数据都设置为其 CSIDL。另外三个项目:路径(如果存在)、浏览和其他,其项目数据值分别为 -1、-2 和 -3。这使得对话框可以特殊处理这些情况。
浏览文件夹使用现在可能随处可见的 shell 对话框代码。
bool CUtility::Browse(HWND hOwner, CString &sFolder) { BROWSEINFO bi; ZeroMemory(&bi, sizeof(BROWSEINFO)); bi.hwndOwner = hOwner; bi.ulFlags = BIF_RETURNONLYFSDIRS; LPITEMIDLIST pidl = SHBrowseForFolder(&bi); bool bRet = false; TCHAR szFolder[MAX_PATH*2]; szFolder[0] = _T('\0'); if (pidl) { if (SHGetPathFromIDList(pidl, szFolder)) { bRet = true; sFolder = szFolder; } IMalloc *pMalloc = NULL; if (SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc) { pMalloc->Free(pidl); pMalloc->Release(); } } return bRet; }
注册表代码
TweakJS 使用 Robert Pittenger 的 CRegistry
类来读写注册表。此处包含的版本整合了 PEK 的更改,为 ReadString
和 WriteString
添加了 Unicode 支持。我将 WriteDword
改为指定 REG_DWORD
而不是 REG_BINARY
,并添加了一个有用的 EnumerateKeys
函数,如下所示:
void CRegistry::EnumerateKeys(CStringArray &saReturn) { //SetKey MUST be called beforehand. ASSERT(m_strCurrentPath.GetLength() > 0); //Set up return array. saReturn.RemoveAll(); HKEY hKey; if (::RegOpenKeyEx(m_hRootKey, LPCTSTR(m_strCurrentPath), 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) return; //Get some sizes to make life easier. DWORD dwNumKeys, dwMaxNameLength, dwSize; LONG lResult = RegQueryInfoKey(hKey, 0, 0, 0, &dwNumKeys, &dwMaxNameLength, 0, 0, 0, 0, 0, 0); //Include room for a NULL in the buffer length dwMaxNameLength++; TCHAR *pBuffer = new TCHAR[dwMaxNameLength]; for(unsigned int i=0; i < dwNumKeys; i++) { dwSize = dwMaxNameLength; lResult = RegEnumKeyEx(hKey, i, pBuffer, &dwSize, 0,0,0,0); if(lResult == ERROR_SUCCESS) saReturn.Add(pBuffer); } delete[] pBuffer; }
CRegistry
被调用来写入三个不同的位置:TweakJS 的注册表位置(保存其设置的地方)、通用 Windows 对话框的位置以及 MS Office 的位置。如前所述,MS Office 和 Windows 的格式有所不同。我选择使用与 Windows 相同的格式来保存 TweakJS 的设置,因为我觉得它更吸引人。
检测 Microsoft Office
最好通过检查注册表来查找与“.doc”文件扩展名关联的可执行文件的路径,然后使用 FileVersionEx
检索版本来查询 MS Office 的版本。我选择了最省力的方法,即直接查找 Office 97、2000、XP 和 2003 的注册表项。
[编辑注释:使用换行符以避免滚动。]
float fVer = 8.0f; bool bFound = false; CString sOfficeRoot; while (!bFound && fVer < 12.0f) { sOfficeRoot.Format(_T("Software\\Microsoft\\ Office\\%1.1f\\Common\\Open Find\\Places"), fVer); bFound = m_pRegistry->KeyExists(sOfficeRoot, HKEY_CURRENT_USER); fVer++; } //Display the installed version of office detected to the user. m_sOfficeVer = _T("None"); if (bFound) { int iVer = fVer - 1.0f; switch (iVer) { case 8: m_sOfficeVer = _T("Office 97");//Unlikely/Impossible to run into this. break; case 9: m_sOfficeVer = _T("Office 2000"); break; case 10: m_sOfficeVer = _T("Office XP"); break; case 11: m_sOfficeVer = _T("Office 2003"); break; default: ASSERT(FALSE); } }
快捷方式
快捷方式代码由 PJ Naughter 易于使用的 CShellLink
类提供。
关注点
通常情况下,尽管此项目外观简单,但这里仍然呈现了几个有趣的要点:
- 某些特殊文件夹在 MS Office 中不显示预期的名称。例如,显示为“共享文档”的文件夹实际上是位于 C:\Documents and Settings\All Users 下一个名为“Documents”的文件夹。为了检索显示给用户的名称,我使用了
SHGetFileInfo
shell 调用并指定了SHGFI_DISPLAYNAME
。 - 在从事 TweakJS 项目之前,我在编辑注册表时经常遇到
REG_EXPAND_SZ
数据说明符。在研究 TweakUI 的行为时,我注意到它将某些字符串以REG_EXPAND_SZ
的形式写入注册表。使用PathUnExpandEnvStrings
(文档 在此),可以将 C:\Documents and Settings\User\Folder 形式的路径转换为 %USERPROFILE%\Folder。PathUnExpandEnvStrings
可用于存储在环境变量中的其他几个位置,例如 C:\Windows 和 C:\Program Files,甚至可以将系统驱动器的位置(在示例中,它是 C:\)返回为 %SystemDrive%。此函数的反函数是ExpandEnvironmentStrings
。值得注意的是,Microsoft Office 从注册表中读取REG_SZ
(普通字符串),不支持REG_EXPAND_SZ
。注意:由于使用了ExpandEnvironmentStrings
和PathUnExpandEnvStrings
,因此生成 TweakJS 需要 Platform SDK。 - 尽管以 Unicode 模式构建是一种相当标准的做法,但我并未将其作为习惯。我发现,除了使代码兼容 Unicode 外,还需要执行以下步骤才能为我的项目添加 Unicode 构建配置:
- 从“生成”菜单中选择“配置”。
- 添加“Unicode Debug” - 复制“Win32 Debug”。
- 添加“Unicode Release” - 复制“Win32 Release”。
- 点击“确定”。
- 从“项目”菜单中选择“设置”。
- 转到“常规”选项卡。
- 对于“Win32 Unicode Debug”,将“中间文件”和“输出文件”设置为 DebugU。
- 对于“Win32 Unicode Release”,将“中间文件”和“输出文件”设置为 ReleaseU。
- 转到“C++”选项卡。
- 从组合框中选择“预处理器”。
- 对于“Win32 Unicode Debug”和“Win32 Unicode Release”,将
_UNICODE
和UNICODE
添加到预处理器变量列表中。 - 转到“链接”选项卡。
- 从组合框中选择“输出”。
- 在“入口点符号”框中输入
wWinMainCRTStartup
。
注意事项
MS Office 2002/XP 和 2003 的用户可以通过“我的位置”栏本身轻松更新。请参阅 Microsoft 知识库文章 KB 826214。
细心的读者会注意到,“我的电脑”和“网络邻居”没有出现在 TweakJS 的组合框中。这是因为它们是虚拟文件夹,不解析到路径,因此无法被 MS Office 显示。即使将路径字符串设置为文件夹的 CLSID(“我的电脑”为 ::{20d04fe0-3aea-1069-a2d8-08002b30309d}
,“我的网络邻居”为 ::{208D2C60-3AEA-1069-A2D7-08002B30309D}
)也不起作用。如果您还没有体验过打开“运行”对话框(“开始”|“运行...”)并输入其中一个字符串的乐趣,不妨试试。为了避免在 MS Office 中显示这些选项并让它们工作不正确,我选择不提供它们。如果您知道解决方法,请联系我。我将更新代码并为您署名。
致谢
CodeProject 丰富的文章集多年来对我帮助很大。我将这篇我的第一篇文章贡献给 CP 社区,以回馈我从 fellow CPians 那里学到的一切。特别感谢我在这里使用了代码的 CP 成员,以及 Marc Clifton,感谢他的 《为 Code Project 撰写文章指南》。
历史
- 版本 1.0 - 初始发布。