65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (13投票s)

2005年12月27日

10分钟阅读

viewsIcon

53447

downloadIcon

816

自定义 Win 2K/XP 和 MS Office 中的打开/保存对话框。

目录

引言

作为其 Windows 增强工具的一部分,Microsoft 提供了一个名为 TweakUI 的强大工具。这个工具允许您(普通的 Windows XP 用户)在不直接修改注册表的情况下更改 XP 的一些默认设置。它将许多有用的设置直接呈现给您,而不是隐藏在注册表的深处,从而消除了您在修改注册表时常常会感到的恐惧和不安。

TweakUI 中“位置栏”页面是我发现特别有用的一个调整项。此页面允许您设置显示在 我的位置栏 中的文件夹。“我的位置栏”显示在 Windows 中的许多打开/保存对话框中。自定义此栏使我能够快速访问我最常保存文件的文件夹,每次保存文件都能节省几次点击。

然而,Microsoft Office 使用自己的打开/保存对话框,并且不遵循 TweakUI 的修改。

TweakJS 应运而生。TweakJS 类似于 TweakUI 中的“位置栏”页面,但有两个显著区别:

  1. 它同时为 Microsoft Office 和通用对话框自定义 我的位置栏
  2. 它允许创建一组指向所选文件夹的快捷方式。

免责声明

尽管我已经尽一切努力使此代码安全正确地运行,但运行任何修改注册表的软件总是有风险的。就像 Mozilla 网站上的俏皮免责声明一样,无法保证 TweakJS 不会烧毁您的处理器、侮辱您的母亲或导致您出现严重的皮疹。对于此软件可能导致的这些或任何其他问题,我概不负责,但我会尽力修复报告的任何问题。如果这让您感到害怕,您可能不应该运行 TweakJS。

由于需要修改注册表,您很可能需要管理员权限才能使用 TweakJS。

用法

TweakJS 允许您选择最多五个文件夹显示在 我的位置栏 中。一旦在 TweakJS 中选择了文件夹,它们就会显示在任何显示 我的位置栏 的打开/保存对话框中。

链接”选项会创建指向您选择添加到 我的位置栏 的文件夹的快捷方式。为什么要这样做?我这样做是因为它允许我创建一个可以放置在任务栏上的工具栏,从而可以轻松访问我选择的目录(请参阅下面的屏幕截图)。

请注意,在屏幕截图中,第一个链接的标题是 自定义链接。此链接是自动添加的。它指向存储所有快捷方式的文件夹,从而可以轻松访问该文件夹并根据需要添加更多快捷方式。

要使此工具栏可见:

  1. 右键单击任务栏。
  2. 选择“工具栏”|“新建工具栏”。
  3. 浏览您在 TweakJS 中选择的输出文件夹。
  4. 您可能需要通过将其拖到系统托盘旁边来调整工具栏的大小。
  5. 在移动/调整新工具栏的大小之前,可能需要解锁该工具栏(右键单击,取消选中“锁定任务栏”)。

背景

为了配置 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 是一个相当简单的程序,包括:

  1. 用户界面代码。
  2. 读写注册表代码。
  3. 检测已安装的 MS Office 版本的代码。
  4. 写入快捷方式文件的代码。

用户界面代码

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 的更改,为 ReadStringWriteString 添加了 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%\FolderPathUnExpandEnvStrings 可用于存储在环境变量中的其他几个位置,例如 C:\WindowsC:\Program Files,甚至可以将系统驱动器的位置(在示例中,它是 C:\)返回为 %SystemDrive%。此函数的反函数是 ExpandEnvironmentStrings。值得注意的是,Microsoft Office 从注册表中读取 REG_SZ(普通字符串),不支持 REG_EXPAND_SZ注意:由于使用了 ExpandEnvironmentStringsPathUnExpandEnvStrings,因此生成 TweakJS 需要 Platform SDK。
  • 尽管以 Unicode 模式构建是一种相当标准的做法,但我并未将其作为习惯。我发现,除了使代码兼容 Unicode 外,还需要执行以下步骤才能为我的项目添加 Unicode 构建配置:
    1. 从“生成”菜单中选择“配置”。
    2. 添加“Unicode Debug” - 复制“Win32 Debug”。
    3. 添加“Unicode Release” - 复制“Win32 Release”。
    4. 点击“确定”。
    5. 从“项目”菜单中选择“设置”。
    6. 转到“常规”选项卡。
    7. 对于“Win32 Unicode Debug”,将“中间文件”和“输出文件”设置为 DebugU。
    8. 对于“Win32 Unicode Release”,将“中间文件”和“输出文件”设置为 ReleaseU。
    9. 转到“C++”选项卡。
    10. 从组合框中选择“预处理器”。
    11. 对于“Win32 Unicode Debug”和“Win32 Unicode Release”,将 _UNICODEUNICODE 添加到预处理器变量列表中。
    12. 转到“链接”选项卡。
    13. 从组合框中选择“输出”。
    14. 在“入口点符号”框中输入 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 - 初始发布。
© . All rights reserved.