创建自定义资源管理器栏、工具栏和桌面带

Explorer 栏随 Microsoft Internet Explorer 4.0 一起引入,提供与浏览器窗格相邻的显示区域。 它基本上是 Windows Internet Explorer 窗口中的一个子窗口,可用于以大致相同的方式显示信息以及与用户交互。 浏览器栏通常显示为浏览器窗格左侧的垂直窗格。 但是,浏览器窗格下方也可以水平显示资源管理器栏。

显示垂直和水平资源管理器栏的屏幕截图。

资源管理器栏有多种可能用途。 用户可以通过多种不同方式选择要查看的选项,包括从“视图”菜单的“资源管理器栏”子菜单中选择它,或单击工具栏按钮。 Internet Explorer 提供多个标准资源管理器栏,包括收藏夹和搜索。

自定义 Internet Explorer 的一种方法是添加自定义资源管理器栏。 实现并注册后,它将添加到“视图”菜单的“资源管理器栏”子菜单中。 当用户选择时,资源管理器栏的显示区域可用于显示信息,并采用与普通窗口大致相同的方式获取用户输入。

资源管理器栏的屏幕截图

若要创建自定义资源管理器栏,必须实现并注册 带对象。 带对象是在 Shell 版本 4.71 中引入的,提供的功能类似于普通窗口的功能。 但是,由于它们是组件对象模型 (COM) 对象,并且包含在 Internet Explorer 或 Shell 中,因此它们的实现方式略有不同。 简单的带状对象用于创建第一个图形中显示的示例资源管理器栏。 垂直资源管理器栏示例的实现将在后面的部分中详细讨论。

工具带

工具栏是随 Microsoft Internet Explorer 5 一起引入的乐队对象,用于支持 Windows 单选工具栏功能。 Internet Explorer 工具栏实际上是包含多个工具栏 控件的 rebar控件。 通过创建一个工具栏,你可以向该 rebar 控件添加带。 但是,与资源管理器栏一样,工具带是常规用途窗口。

工具栏的屏幕截图

用户通过从“视图”菜单的“工具栏”子菜单中或通过右键单击工具栏区域显示的快捷菜单选择工具栏来显示工具栏。

桌面带

乐队对象还可用于创建 桌面带。 虽然其基本实现类似于资源管理器栏,但桌面带与 Internet Explorer 无关。 桌面带基本上是一种在桌面上创建可停靠窗口的方法。 用户通过右键单击任务栏并从 “工具栏” 子菜单中选择它来选择它。

显示示例桌面带的屏幕截图。

最初,桌面带停靠在任务栏上。

显示停靠在任务栏上的桌面带的屏幕截图。

然后,用户可以将桌面带拖到桌面上,它将显示为普通窗口。

桌面带的屏幕截图

实现 Band 对象

讨论以下主题。

Band 对象基础知识

尽管它们与普通窗口非常类似,但带对象是存在于容器中的 COM 对象。 Explorer 栏由 Internet Explorer 包含,桌面带由 Shell 包含。 虽然它们提供不同的功能,但它们的基本实现非常相似。 主要区别在于乐队对象的注册方式,后者又控制对象的类型及其容器。 本部分讨论实现的所有波段对象共有的那些方面。 有关其他实现详细信息 ,请参阅自定义资源管理器栏的简单示例

除了 IUnknownIClassFactory 之外,所有带区对象都必须实现以下接口。

除了 (CLSID) 注册其类标识符外,还必须为相应的组件类别注册 Explorer Bar 和桌面带对象。 注册组件类别确定对象类型及其容器。 工具栏使用不同的注册过程,并且没有类别标识符 (CATID) 。 需要它们的三个波段对象的 CATID 为:

带类型 组件类别
垂直资源管理器栏 CATID_InfoBand
水平资源管理器栏 CATID_CommBand
桌面带 CATID_DeskBand

 

有关如何 注册带 对象的进一步讨论,请参阅带注册。

如果 band 对象要接受用户输入,则它还必须实现 IInputObject。 若要将项目添加到资源管理器栏或桌面带的快捷菜单,乐队对象必须导出 IContextMenu。 工具栏不支持快捷菜单。

由于带对象实现子窗口,因此它们还必须实现窗口过程来处理 Windows 消息传送。

Band 对象可以通过容器的 IOleCommandTarget 接口将命令发送到其容器。 若要获取接口指针,请调用容器的 IInputObjectSite::QueryInterface 方法并请求IID_IOleCommandTarget。 然后,使用 IOleCommandTarget::Exec 将命令发送到容器。 命令组CGID_DeskBand。 调用乐队对象的 IDeskBand::GetBandInfo 方法时,容器使用 dwBandID 参数为乐队对象分配标识符,该标识符用于其中三个命令。 支持四个 IOleCommandTarget::Exec 命令 ID。

  • DBID_BANDINFOCHANGED

    乐队的信息已更改。 将 pvaIn 参数设置为在最近一次调用 IDeskBand::GetBandInfo 时收到的带标识符。 容器将调用 band 对象的 IDeskBand::GetBandInfo 方法以请求更新的信息。

  • DBID_MAXIMIZEBAND

    最大化带。 将 pvaIn 参数设置为在最近一次调用 IDeskBand::GetBandInfo 时收到的带标识符。

  • DBID_SHOWONLY

    打开或关闭容器中的其他波段。 使用以下值之一将 pvaIn 参数设置为VT_UNKNOWN类型:

    说明
    朋 克 指向乐队对象的 IUnknown 接口的指针。 所有其他桌面带将被隐藏。
    0 隐藏所有桌面带。
    1 显示所有桌面乐队。

     

  • DBID_PUSHCHEVRON

    版本 5。 显示 V 形菜单。 容器发送 RB_PUSHCHEVRON 消息,并且乐队对象会收到 RBN_CHEVRONPUSHED 通知,提示它显示 V 形菜单。 将 IOleCommandTarget::Exec 方法的 nCmdExecOpt 参数设置为在最近一次调用 IDeskBand::GetBandInfo 时收到的带标识符。 将 IOleCommandTarget::Exec 方法的 pvaIn 参数设置为具有应用程序定义的值的VT_I4类型。 它将作为RBN_CHEVRONPUSHED通知的 lAppValue 值传回带对象。

带区注册

带区对象必须注册为支持单元线程处理的 OLE 进程内服务器。 服务器的默认值为菜单文本字符串。 对于 Explorer 栏,它将显示在 Internet Explorer 视图菜单的“资源管理器栏”子菜单中。 对于工具栏,它将显示在 Internet Explorer 视图菜单的“工具栏”子菜单中。 对于桌面带,它将出现在任务栏快捷菜单的 “工具栏” 子菜单中。 与菜单资源一样,在字母前面放置与 (&) 会导致字母下划线并启用键盘快捷方式。 例如,第一个图形中显示的垂直资源管理器栏的菜单字符串是“示例 &垂直资源管理器栏”。

最初,Internet Explorer 使用组件类别从注册表中检索已注册的 Explorer Bar 对象的枚举。 为了提高性能,它会缓存此枚举,导致随后添加的资源管理器栏被忽略。 若要强制 Windows Internet Explorer 重新生成缓存并识别新的资源管理器栏,请在注册新资源管理器栏期间删除以下注册表项:

HKEY_CURRENT_USER\软件\微软\窗户\CurrentVersion\探险 家\\丢弃PostSetup\组件类别\{00021493-0000-0000-C000-0000000000046}\枚举

HKEY_CURRENT_USER\软件\微软\窗户\CurrentVersion\探险 家\\丢弃PostSetup\组件类别\{00021494-0000-0000-C000-0000000000046}\枚举

注意

由于为每个用户创建了资源管理器栏缓存,因此安装应用程序可能需要枚举所有用户注册表配置单元,或添加每用户存根,以便在用户首次登录时运行。

 

通常,带对象的基本注册表项如下所示。

HKEY_CLASSES_ROOT
   CLSID
      {Your Band Object's CLSID GUID}
         (Default) = Menu Text String
         InProcServer32
            (Default) = DLL Path Name
            ThreadingModel = Apartment

工具带还必须将其对象的 CLSID 注册到 Internet Explorer。 为此,请在 HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Toolbar 下分配一个值,该值名为 ,其中包含工具栏对象的 CLSID GUID,如下所示。 其数据值将被忽略,因此值类型不重要。

HKEY_LOCAL_MACHINE
   Software
      Microsoft
         Internet Explorer
            Toolbar
               {Your Band Object's CLSID GUID}

还可以将多个可选值添加到注册表。 例如,如果要使用资源管理器栏显示 HTML,则以下值是必需的。 显示的值不是示例,而是应使用的实际值。

HKEY_CLASSES_ROOT
   CLSID
      {Your Band Object's CLSID GUID}
         Instance
            CLSID
               (Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}

与上面所示的值结合使用时,如果要使用资源管理器栏显示 HTML,则以下可选值也是必需的。 此值应设置为包含资源管理器栏的 HTML 内容的文件的位置。

HKEY_CLASSES_ROOT
   CLSID
      {Your Band Object's CLSID GUID}
         Instance
            InitPropertyBag
               Url

另一个可选值定义资源管理器栏的默认宽度或高度,具体取决于它是垂直还是水平。

HKEY_CURRENT_USER
   Software
      Microsoft
         Internet Explorer
            Explorer Bars
               {Your Band Object's CLSID GUID}
                  BarSize

BarSize 值应设置为条形图的宽度或高度。 该值需要 8 个字节,并作为二进制值放置在注册表中。 前四个字节以十六进制格式指定大小(以像素为单位),从最左侧的字节开始。 最后四个字节是保留的,应设置为零。

例如,此处显示了默认宽度为 291 (0x123) 像素的支持 HTML 的资源管理器栏的完整注册表项。

HKEY_CLASSES_ROOT
   CLSID
      {Your Band Object's CLSID GUID}
         (Default) = Menu Text String
         InProcServer32
            (Default) = DLL Path Name
            ThreadingModel = Apartment
         Instance
            CLSID
               (Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}
            InitPropertyBag
               Url = Your HTML File
HKEY_CURRENT_USER
   Software
      Microsoft
         Internet Explorer
            Explorer Bars
               {Your Band Object's CLSID GUID}
                  BarSize = 23 01 00 00 00 00 00 00

可以通过编程方式处理带对象的 CATID 注册。 创建组件类别管理器对象 (CLSID_StdComponentCategoriesMgr) 并请求指向其 ICatRegister 接口的指针。 将 band 对象的 CLSID 和 CATID 传递给 ICatRegister::RegisterClassImplCategories

自定义资源管理器栏的简单示例

此示例演示了简介中显示的示例垂直资源管理器栏的实现。

创建自定义资源管理器栏的基本过程如下所示。

  1. 实现 DLL 所需的函数
  2. 实现所需的 COM 接口。
  3. 实现任何所需的可选 COM 接口。
  4. 注册对象的 CLSID,并根据需要注册组件类别。
  5. 创建 Internet Explorer 的子窗口,该窗口的大小适合 Explorer 栏的显示区域。
  6. 使用子窗口显示信息并与用户交互。

资源管理器栏示例中使用的非常简单的实现实际上可用于任一类型的资源管理器栏或桌面带,只需将其注册到适当的组件类别即可。 需要为每个对象类型的显示区域和容器自定义更复杂的实现。 但是,大部分自定义都可以通过采用示例代码并通过将熟悉的 Windows 编程技术应用于子窗口来扩展它来完成。 例如,可以添加用于用户交互的控件,或为更丰富的显示添加图形。

DLL 函数

所有三个对象都打包在一个 DLL 中,这会公开以下函数。

前三个函数是标准实现,此处将不讨论。 类工厂实现也是标准的。

所需的接口实现

垂直资源管理器栏示例实现四个必需的接口: IUnknownIObjectWithSiteIPersistStreamIDeskBand 作为 CExplorerBar 类的一部分。 构造函数、析构函数和 IUnknown 实现非常简单,不会在此处讨论。 请参阅示例代码了解详细信息。

下面详细介绍了以下接口。

IObjectWithSite

当用户选择资源管理器栏时,容器将调用相应 band 对象的 IObjectWithSite::SetSite 方法。 punkSite 参数将设置为网站的 IUnknown 指针。

通常, SetSite 实现应执行以下步骤:

  1. 释放当前保留的任何站点指针。
  2. 如果传递给 SetSite 的指针设置为 NULL,则移除带区。 SetSite 可以返回S_OK。
  3. 如果传递给 SetSite 的指针为非 NULL,则表示正在设置新站点。 SetSite 应执行以下操作:
    1. 在其 IOleWindow 接口的网站上调用 QueryInterface
    2. 调用 IOleWindow::GetWindow 以获取父窗口的句柄。 保存句柄供以后使用。 如果不再需要 IOleWindow ,请释放它。
    3. 创建 band 对象的窗口作为上一步中获取的窗口的子级。 不要将其创建为可见窗口。
    4. 如果 band 对象实现 IInputObject,请在站点上为其 IInputObjectSite 接口调用 QueryInterface。 存储指向此接口的指针,供以后使用。
    5. 如果所有步骤都成功,则返回S_OK。 如果没有,则返回 OLE 定义的错误代码,指示失败的内容。

资源管理器栏示例按以下方式实现 SetSite 。 在以下代码 中,m_pSite 是一个私有成员变量,它保存 IInputObjectSite 指针, m_hwndParent 保存父窗口的句柄。 在此示例中,还会处理窗口创建。 如果窗口不存在,此方法会将资源管理器栏的窗口创建为 SetSite 获取的父窗口的相应大小子窗口。 子窗口的句柄存储在 m_hwnd中。

STDMETHODIMP CDeskBand::SetSite(IUnknown *pUnkSite)
{
    HRESULT hr = S_OK;

    m_hwndParent = NULL;

    if (m_pSite)
    {
        m_pSite->Release();
    }

    if (pUnkSite)
    {
        IOleWindow *pow;
        hr = pUnkSite->QueryInterface(IID_IOleWindow, reinterpret_cast<void **>(&pow));
        if (SUCCEEDED(hr))
        {
            hr = pow->GetWindow(&m_hwndParent);
            if (SUCCEEDED(hr))
            {
                WNDCLASSW wc = { 0 };
                wc.style         = CS_HREDRAW | CS_VREDRAW;
                wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
                wc.hInstance     = g_hInst;
                wc.lpfnWndProc   = WndProc;
                wc.lpszClassName = g_szDeskBandSampleClass;
                wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 0));

                RegisterClassW(&wc);

                CreateWindowExW(0,
                                g_szDeskBandSampleClass,
                                NULL,
                                WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                0,
                                0,
                                0,
                                0,
                                m_hwndParent,
                                NULL,
                                g_hInst,
                                this);

                if (!m_hwnd)
                {
                    hr = E_FAIL;
                }
            }

            pow->Release();
        }

        hr = pUnkSite->QueryInterface(IID_IInputObjectSite, reinterpret_cast<void **>(&m_pSite));
    }

    return hr;
}

此示例的 GetSite 实现只是使用 SetSite 保存的网站指针包装对站点的 QueryInterface 方法的调用。

STDMETHODIMP CDeskBand::GetSite(REFIID riid, void **ppv)
{
    HRESULT hr = E_FAIL;

    if (m_pSite)
    {
        hr =  m_pSite->QueryInterface(riid, ppv);
    }
    else
    {
        *ppv = NULL;
    }

    return hr;
}

IPersistStream

Internet Explorer 将调用资源管理器栏的 IPersistStream 接口,以允许资源管理器栏加载或保存持久性数据。 如果没有持久性数据,则方法仍必须返回成功代码。 IPersistStream 接口继承自 IPersist,因此必须实现五种方法。

资源管理器栏示例不使用任何持久性数据,并且只有 IPersistStream 的最小实现。 IPersist::GetClassID 返回对象的 CLSID (CLSID_SampleExplorerBar) ,余数返回S_OK、S_FALSE或E_NOTIMPL。

IDeskBand

IDeskBand 接口特定于带对象。 除了它的一个方法外,它还继承自 IDockingWindow,后者又继承自 IOleWindow

有两种 IOleWindow 方法: GetWindowIOleWindow::ContextSensitiveHelp。 资源管理器栏示例的 GetWindow 实现返回资源管理器栏的子窗口句柄 ,m_hwnd。 上下文相关帮助未实现,因此 ContextSensitiveHelp 返回 E_NOTIMPL

IDockingWindow 接口有三种方法。

ResizeBorderDW 方法不与任何类型的带对象一起使用,应始终返回E_NOTIMPL。 ShowDW 方法显示或隐藏资源管理器栏的窗口,具体取决于其参数的值。

STDMETHODIMP CDeskBand::ShowDW(BOOL fShow)
{
    if (m_hwnd)
    {
        ShowWindow(m_hwnd, fShow ? SW_SHOW : SW_HIDE);
    }

    return S_OK;
}

CloseDW 方法销毁资源管理器栏的窗口。

STDMETHODIMP CDeskBand::CloseDW(DWORD)
{
    if (m_hwnd)
    {
        ShowWindow(m_hwnd, SW_HIDE);
        DestroyWindow(m_hwnd);
        m_hwnd = NULL;
    }

    return S_OK;
}

其余方法 GetBandInfo 特定于 IDeskBand。 Internet Explorer 使用它来指定资源管理器栏的标识符和查看模式。 Internet Explorer 还可以通过填充作为第三个参数传递的 DESKBANDINFO 结构的 dwMask 成员,从资源管理器栏请求一条或多条信息。 GetBandInfo 应存储标识符和查看模式,并使用请求的数据填充 DESKBANDINFO 结构。 资源管理器栏示例实现 GetBandInfo ,如以下代码示例所示。

STDMETHODIMP CDeskBand::GetBandInfo(DWORD dwBandID, DWORD, DESKBANDINFO *pdbi)
{
    HRESULT hr = E_INVALIDARG;

    if (pdbi)
    {
        m_dwBandID = dwBandID;

        if (pdbi->dwMask & DBIM_MINSIZE)
        {
            pdbi->ptMinSize.x = 200;
            pdbi->ptMinSize.y = 30;
        }

        if (pdbi->dwMask & DBIM_MAXSIZE)
        {
            pdbi->ptMaxSize.y = -1;
        }

        if (pdbi->dwMask & DBIM_INTEGRAL)
        {
            pdbi->ptIntegral.y = 1;
        }

        if (pdbi->dwMask & DBIM_ACTUAL)
        {
            pdbi->ptActual.x = 200;
            pdbi->ptActual.y = 30;
        }

        if (pdbi->dwMask & DBIM_TITLE)
        {
            // Don't show title by removing this flag.
            pdbi->dwMask &= ~DBIM_TITLE;
        }

        if (pdbi->dwMask & DBIM_MODEFLAGS)
        {
            pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
        }

        if (pdbi->dwMask & DBIM_BKCOLOR)
        {
            // Use the default background color by removing this flag.
            pdbi->dwMask &= ~DBIM_BKCOLOR;
        }

        hr = S_OK;
    }

    return hr;
}

可选接口实现

有两个接口不是必需的,但可用于实现: IInputObjectIContextMenu。 资源管理器栏示例实现 IInputObject。 有关如何实现 IContextMenu 的信息,请参阅文档。

IInputObject

如果带区对象接受用户输入,则必须实现 IInputObject 接口。 Internet Explorer 实现 IInputObjectSite ,并在具有多个包含窗口时使用 IInputObject 来保持正确的用户输入焦点。 资源管理器栏需要实现三种方法。

Internet Explorer 调用 UIActivateIO 以通知资源管理器栏正在激活或停用它。 激活后,资源管理器栏示例调用 SetFocus 将焦点设置为其窗口。

Internet Explorer 在尝试确定哪个窗口具有焦点时调用 HasFocusIO 。 如果资源管理器栏的窗口或其后代之一具有焦点, HasFocusIO 应返回S_OK。 否则,它应返回S_FALSE。

TranslateAcceleratorIO 允许对象处理键盘快捷键。 资源管理器栏示例不实现此方法,因此返回S_FALSE。

示例栏的 IInputObjectSite 实现如下所示。

STDMETHODIMP CDeskBand::UIActivateIO(BOOL fActivate, MSG *)
{
    if (fActivate)
    {
        SetFocus(m_hwnd);
    }

    return S_OK;
}

STDMETHODIMP CDeskBand::HasFocusIO()
{
    return m_fHasFocus ? S_OK : S_FALSE;
}

STDMETHODIMP CDeskBand::TranslateAcceleratorIO(MSG *)
{
    return S_FALSE;
};

CLSID 注册

与所有 COM 对象一样,必须注册资源管理器栏的 CLSID。 若要使对象在 Internet Explorer 中正常运行,还必须为相应的组件类别注册 (CATID_InfoBand) 。 以下代码示例中显示了资源管理器栏的相关代码部分。

HRESULT RegisterServer()
{
    WCHAR szCLSID[MAX_PATH];
    StringFromGUID2(CLSID_DeskBandSample, szCLSID, ARRAYSIZE(szCLSID));

    WCHAR szSubkey[MAX_PATH];
    HKEY hKey;

    HRESULT hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s", szCLSID);
    if (SUCCEEDED(hr))
    {
        hr = E_FAIL;
        if (ERROR_SUCCESS == RegCreateKeyExW(HKEY_CLASSES_ROOT,
                                             szSubkey,
                                             0,
                                             NULL,
                                             REG_OPTION_NON_VOLATILE,
                                             KEY_WRITE,
                                             NULL,
                                             &hKey,
                                             NULL))
        {
            WCHAR const szName[] = L"DeskBand Sample";
            if (ERROR_SUCCESS == RegSetValueExW(hKey,
                                                NULL,
                                                0,
                                                REG_SZ,
                                                (LPBYTE) szName,
                                                sizeof(szName)))
            {
                hr = S_OK;
            }

            RegCloseKey(hKey);
        }
    }

    if (SUCCEEDED(hr))
    {
        hr = StringCchPrintfW(szSubkey, ARRAYSIZE(szSubkey), L"CLSID\\%s\\InprocServer32", szCLSID);
        if (SUCCEEDED(hr))
        {
            hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_CLASSES_ROOT, szSubkey,
                 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL));
            if (SUCCEEDED(hr))
            {
                WCHAR szModule[MAX_PATH];
                if (GetModuleFileNameW(g_hInst, szModule, ARRAYSIZE(szModule)))
                {
                    DWORD cch = lstrlen(szModule);
                    hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE) szModule, cch * sizeof(szModule[0])));
                }

                if (SUCCEEDED(hr))
                {
                    WCHAR const szModel[] = L"Apartment";
                    hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, L"ThreadingModel", 0,  REG_SZ, (LPBYTE) szModel, sizeof(szModel)));
                }

                RegCloseKey(hKey);
            }
        }
    }

    return hr;
}

示例中的 band 对象的注册使用常规 COM 过程。

除了 CLSID 之外,还必须为一个或多个组件类别注册带对象服务器。 这实际上是垂直和水平资源管理器栏示例在实现方面的main差异。 此过程通过创建组件类别管理器对象 (CLSID_StdComponentCategoriesMgr) 并使用 ICatRegister::RegisterClassImplCategories 方法注册带对象服务器来处理。 在此示例中,组件类别注册是通过将资源管理器栏示例的 CLSID 和 CATID 传递给专用函数 RegisterComCat 来处理的,如以下代码示例所示。

HRESULT RegisterComCat()
{
    ICatRegister *pcr;
    HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcr));
    if (SUCCEEDED(hr))
    {
        CATID catid = CATID_DeskBand;
        hr = pcr->RegisterClassImplCategories(CLSID_DeskBandSample, 1, &catid);
        pcr->Release();
    }
    return hr;
}

窗口过程

由于带区对象使用子窗口进行显示,因此它必须实现窗口过程来处理 Windows 消息传送。 带示例具有最少的功能,因此其窗口过程仅处理五条消息:

可以轻松扩展该过程,以适应其他消息以支持更多功能。

LRESULT CALLBACK CDeskBand::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lResult = 0;

    CDeskBand *pDeskBand = reinterpret_cast<CDeskBand *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));

    switch (uMsg)
    {
    case WM_CREATE:
        pDeskBand = reinterpret_cast<CDeskBand *>(reinterpret_cast<CREATESTRUCT *>(lParam)->lpCreateParams);
        pDeskBand->m_hwnd = hwnd;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDeskBand));
        break;

    case WM_SETFOCUS:
        pDeskBand->OnFocus(TRUE);
        break;

    case WM_KILLFOCUS:
        pDeskBand->OnFocus(FALSE);
        break;

    case WM_PAINT:
        pDeskBand->OnPaint(NULL);
        break;

    case WM_PRINTCLIENT:
        pDeskBand->OnPaint(reinterpret_cast<HDC>(wParam));
        break;

    case WM_ERASEBKGND:
        if (pDeskBand->m_fCompositionEnabled)
        {
            lResult = 1;
        }
        break;
    }

    if (uMsg != WM_ERASEBKGND)
    {
        lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return lResult;
}

WM_COMMAND处理程序只返回零。 WM_PAINT处理程序创建简介中的资源管理器栏示例中显示的简单文本显示。

void CDeskBand::OnPaint(const HDC hdcIn)
{
    HDC hdc = hdcIn;
    PAINTSTRUCT ps;
    static WCHAR szContent[] = L"DeskBand Sample";
    static WCHAR szContentGlass[] = L"DeskBand Sample (Glass)";

    if (!hdc)
    {
        hdc = BeginPaint(m_hwnd, &ps);
    }

    if (hdc)
    {
        RECT rc;
        GetClientRect(m_hwnd, &rc);

        SIZE size;

        if (m_fCompositionEnabled)
        {
            HTHEME hTheme = OpenThemeData(NULL, L"BUTTON");
            if (hTheme)
            {
                HDC hdcPaint = NULL;
                HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, NULL, &hdcPaint);

                DrawThemeParentBackground(m_hwnd, hdcPaint, &rc);

                GetTextExtentPointW(hdc, szContentGlass, ARRAYSIZE(szContentGlass), &size);
                RECT rcText;
                rcText.left   = (RECTWIDTH(rc) - size.cx) / 2;
                rcText.top    = (RECTHEIGHT(rc) - size.cy) / 2;
                rcText.right  = rcText.left + size.cx;
                rcText.bottom = rcText.top + size.cy;

                DTTOPTS dttOpts = {sizeof(dttOpts)};
                dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR | DTT_GLOWSIZE;
                dttOpts.crText = RGB(255, 255, 0);
                dttOpts.iGlowSize = 10;
                DrawThemeTextEx(hTheme, hdcPaint, 0, 0, szContentGlass, -1, 0, &rcText, &dttOpts);

                EndBufferedPaint(hBufferedPaint, TRUE);

                CloseThemeData(hTheme);
            }
        }
        else
        {
            SetBkColor(hdc, RGB(255, 255, 0));
            GetTextExtentPointW(hdc, szContent, ARRAYSIZE(szContent), &size);
            TextOutW(hdc,
                     (RECTWIDTH(rc) - size.cx) / 2,
                     (RECTHEIGHT(rc) - size.cy) / 2,
                     szContent,
                     ARRAYSIZE(szContent));
        }
    }

    if (!hdcIn)
    {
        EndPaint(m_hwnd, &ps);
    }
}

WM_SETFOCUS和WM_KILLFOCUS处理程序通过调用站点的 IInputObjectSite::OnFocusChangeIS 方法通知站点焦点更改。

void CDeskBand::OnFocus(const BOOL fFocus)
{
    m_fHasFocus = fFocus;

    if (m_pSite)
    {
        m_pSite->OnFocusChangeIS(static_cast<IOleWindow*>(this), m_fHasFocus);
    }
}

带对象提供了一种灵活而强大的方法来通过创建自定义 Explorer 栏来扩展 Internet Explorer 的功能。 实现桌面带可以扩展普通窗口的功能。 尽管某些 COM 编程是必需的,但它最终用于为用户界面提供子窗口。 从那里,大部分实现都可以使用熟悉的 Windows 编程技术。 虽然此处讨论的示例功能有限,但它演示了乐队对象的所有必要功能,并且可以轻松扩展它以创建唯一且功能强大的用户界面。