Criando barras de Explorer personalizadas, bandas de ferramentas e bandas de mesa

A barra de Explorer foi introduzida com o Microsoft Internet Explorer 4.0 para fornecer uma área de exibição adjacente ao painel do navegador. É basicamente uma janela filho dentro da janela Explorer da Internet do Windows e pode ser usada para exibir informações e interagir com o usuário da mesma maneira. Explorer Barras são mais comumente exibidos como um painel vertical no lado esquerdo do painel do navegador. No entanto, uma barra de Explorer também pode ser exibida horizontalmente, abaixo do painel do navegador.

Captura de tela que mostra as barras de Explorer vertical e horizontal.

Há uma ampla variedade de usos possíveis para a Barra de Explorer. Os usuários podem selecionar qual opção eles desejam ver de várias maneiras diferentes, incluindo selecioná-la no submenu barra de Explorer do menu Exibir ou clicar em um botão de barra de ferramentas. O Explorer da Internet fornece várias barras de Explorer padrão, incluindo Favoritos e Pesquisa.

Uma das maneiras de personalizar Explorer da Internet é adicionando uma barra de Explorer personalizada. Quando implementado e registrado, ele será adicionado ao submenu barra de Explorer do menu Exibir. Quando selecionada pelo usuário, a área de exibição da barra de Explorer pode ser usada para exibir informações e levar a entrada do usuário da mesma forma que uma janela normal.

captura de tela das barras do explorer

Para criar uma barra de Explorer personalizada, você deve implementar e registrar um objeto de banda. Os objetos de banda foram introduzidos com a versão 4.71 do Shell e fornecem recursos semelhantes aos das janelas normais. No entanto, como são objetos COM (Component Object Model) e contidos por Explorer da Internet ou pelo Shell, eles são implementados de forma um pouco diferente. Objetos de banda simples foram usados para criar o exemplo Explorer Barras exibidos no primeiro gráfico. A implementação do exemplo de barra de Explorer vertical será discutida detalhadamente em uma seção posterior.

Faixas de Ferramentas

Uma banda de ferramentas é um objeto de banda que foi introduzido com o Microsoft Internet Explorer 5 para dar suporte ao recurso de barra de ferramentas de rádio do Windows. A barra de ferramentas Explorer da Internet é, na verdade, um controle rebar que contém vários controles da barra de ferramentas. Ao criar uma banda de ferramentas, você pode adicionar uma banda a esse controle rebar. No entanto, como Explorer Barras, uma faixa de ferramentas é uma janela de finalidade geral.

captura de tela de faixas de ferramentas

Os usuários exibem uma barra de ferramentas selecionando-a no submenu Barras de Ferramentas do menu Exibir ou no menu de atalho exibido clicando com o botão direito do mouse na área da barra de ferramentas.

Faixas de Mesa

Objetos de banda também podem ser usados para criar faixas de mesa. Embora sua implementação básica seja semelhante ao Explorer Bars, as faixas de mesa não estão relacionadas aos Explorer da Internet. Uma faixa de mesa é basicamente uma maneira de criar uma janela encaixável na área de trabalho. O usuário o seleciona clicando com o botão direito do mouse na barra de tarefas e selecionando-a no submenu Barras de Ferramentas .

Captura de tela que mostra uma faixa de mesa de exemplo.

Inicialmente, as faixas de mesa são encaixadas na barra de tarefas.

Captura de tela que mostra faixas de mesa encaixadas na barra de tarefas.

Em seguida, o usuário pode arrastar a faixa de mesa para a área de trabalho e ela aparecerá como uma janela normal.

captura de tela de faixas de mesa

Implementando objetos de banda

Os tópicos a seguir são discutidos.

Noções básicas sobre objetos de banda

Embora possam ser usados como janelas normais, os objetos de banda são objetos COM que existem em um contêiner. Explorer Barras estão contidas por Explorer da Internet e as faixas de mesa estão contidas no Shell. Embora atendam a funções diferentes, sua implementação básica é muito semelhante. A principal diferença está em como o objeto de banda é registrado, que, por sua vez, controla o tipo de objeto e seu contêiner. Esta seção discute os aspectos da implementação que são comuns a todos os objetos de banda. Consulte Um exemplo simples de uma barra de Explorer personalizada para obter detalhes adicionais da implementação.

Além de IUnknown e IClassFactory, todos os objetos de banda devem implementar as interfaces a seguir.

Além de registrar o CLSID (identificador de classe), os objetos de barra de Explorer e faixa de mesa também devem ser registrados para a categoria de componente apropriada. Registrar a categoria de componente determina o tipo de objeto e seu contêiner. As faixas de ferramentas usam um procedimento de registro diferente e não têm um CATID (identificador de categoria). Os CATIDs para os três objetos de banda que exigem eles são:

Tipo de banda Categoria do componente
Barra de Explorer Vertical CATID_InfoBand
Barra de Explorer Horizontal CATID_CommBand
Faixa de Mesa CATID_DeskBand

 

Consulte Registro de Banda para obter mais discussões sobre como registrar objetos de banda.

Se o objeto de banda for aceitar a entrada do usuário, ele também deverá implementar IInputObject. Para adicionar itens ao menu de atalho para Explorer barra ou faixas de mesa, o objeto de banda deve exportar IContextMenu. As bandas de ferramentas não dão suporte a menus de atalho.

Como os objetos de banda implementam uma janela filho, eles também devem implementar um procedimento de janela para lidar com mensagens do Windows.

Objetos de banda podem enviar comandos para seu contêiner por meio da interface IOleCommandTarget do contêiner. Para obter o ponteiro de interface, chame o método IInputObjectSite::QueryInterface do contêiner e solicite IID_IOleCommandTarget. Em seguida, você envia comandos para o contêiner com IOleCommandTarget::Exec. O grupo de comandos é CGID_DeskBand. Quando o método IDeskBand::GetBandInfo de um objeto de banda é chamado, o contêiner usa o parâmetro dwBandID para atribuir ao objeto de banda um identificador usado para três dos comandos. Há suporte para quatro IDs de comando IOleCommandTarget::Exec .

  • DBID_BANDINFOCHANGED

    As informações da banda foram alteradas. Defina o parâmetro pvaIn como o identificador de banda que foi recebido na chamada mais recente para IDeskBand::GetBandInfo. O contêiner chamará o método IDeskBand::GetBandInfo do objeto de banda para solicitar as informações atualizadas.

  • DBID_MAXIMIZEBAND

    Maximizar a banda. Defina o parâmetro pvaIn como o identificador de banda que foi recebido na chamada mais recente para IDeskBand::GetBandInfo.

  • DBID_SHOWONLY

    Ative ou desative outras faixas no contêiner. Defina o parâmetro pvaIn para o tipo VT_UNKNOWN com um dos seguintes valores:

    Valor Descrição
    Punk Um ponteiro para a interface IUnknown do objeto de banda. Todas as outras bandas de mesa estarão escondidas.
    0 Ocultar todas as faixas de mesa.
    1 Mostre todas as bandas de mesa.

     

  • DBID_PUSHCHEVRON

    Versão 5. Exibir um menu de divisa. O contêiner envia uma mensagem RB_PUSHCHEVRON e o objeto de banda recebe uma notificação RBN_CHEVRONPUSHED que solicita que ele exiba o menu de divisa. Defina o parâmetro nCmdExecOpt do método IOleCommandTarget::Exec como o identificador de banda recebido na chamada mais recente para IDeskBand::GetBandInfo. Defina o parâmetro pvaIn do método IOleCommandTarget::Exec como o tipo VT_I4 com um valor definido pelo aplicativo. Ele passa de volta para o objeto de banda como o valor lAppValue da notificação de RBN_CHEVRONPUSHED.

Registro de Banda

Um objeto de banda deve ser registrado como um servidor em processo OLE que dá suporte ao threading de apartment. O valor padrão para o servidor é uma cadeia de caracteres de texto de menu. Para barras de Explorer, ela aparecerá no submenu barra de Explorerdo menu Exibição de Explorer da Internet. Para faixas de ferramentas, ela aparecerá no submenu Barras de Ferramentasdo menu Exibição de Explorer da Internet. Para faixas de mesa, ela aparecerá no submenu Barras de ferramentas do menu de atalho da barra de tarefas. Assim como acontece com os recursos de menu, colocar um e comercial (&) na frente de uma letra fará com que ele seja sublinhado e habilite os atalhos de teclado. Por exemplo, a cadeia de caracteres de menu para a Barra de Explorer vertical mostrada no primeiro gráfico é "Barra de Explorer Vertical de Exemplo&".

Inicialmente, a Internet Explorer recupera uma enumeração dos objetos registrados Explorer Bar do registro usando as categorias de componente. Para aumentar o desempenho, ele armazena em cache essa enumeração, fazendo com que as barras de Explorer posteriormente sejam ignoradas. Para forçar o Windows Internet Explorer recompilar o cache e reconhecer uma nova barra de Explorer, exclua as seguintes chaves do Registro durante o registro da nova barra de Explorer:

HKEY_CURRENT_USER\Software\Microsoft\Windows\Currentversion\\ Explorer Discardable\PostSetup\Component Categories\{00021493-0000-0000-C000-0000000000046}\Enum

HKEY_CURRENT_USER\Software\Microsoft\Windows\Currentversion\\ Explorer Discardable\PostSetup\Component Categories\{00021494-0000-0000-C000-0000000000046}\Enum

Observação

Como um cache da barra de Explorer é criado para cada usuário, seu aplicativo de instalação pode precisar enumerar todos os hives do registro de usuário ou adicionar um stub por usuário para executar quando o usuário fizer logon pela primeira vez.

 

Em geral, a entrada básica do Registro para um objeto de banda será exibida da seguinte maneira.

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

As bandas de ferramentas também devem ter o CLSID de seu objeto registrado com Explorer da Internet. Para fazer isso, atribua um valor em HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Toolbar nomeado com o GUID CLSID do objeto de banda de ferramentas, conforme mostrado aqui. Seu valor de dados é ignorado, portanto, o tipo de valor não é importante.

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

Há vários valores opcionais que também podem ser adicionados ao registro. Por exemplo, o valor a seguir será necessário se você quiser usar a barra de Explorer para exibir HTML O valor mostrado não é um exemplo, mas o valor real que deve ser usado.

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

Usado em conjunto com o valor mostrado acima, o valor opcional a seguir também será necessário se você quiser usar a barra de Explorer para exibir HTML. Esse valor deve ser definido como o local do arquivo que contém o conteúdo HTML da barra de Explorer.

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

Outro valor opcional define a largura ou altura padrão da barra de Explorer, dependendo se ela é vertical ou horizontal, respectivamente.

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

O valor BarSize deve ser definido como a largura ou altura da barra. O valor requer oito bytes e é colocado no registro como um valor binário. Os quatro primeiros bytes especificam o tamanho em pixels, no formato hexadecimal, começando pelo byte mais à esquerda. Os últimos quatro bytes são reservados e devem ser definidos como zero.

Por exemplo, as entradas completas do Registro para uma Barra de Explorer com capacidade para HTML com uma largura padrão de 291 pixels (0x123) são mostradas aqui.

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

Você pode lidar com o registro do CATID de um objeto de banda programaticamente. Crie um objeto gerenciador de categorias de componente (CLSID_StdComponentCategoriesMgr) e solicite um ponteiro para sua interface ICatRegister . Passe o CLSID e CATID do objeto de banda para ICatRegister::RegisterClassImplCategories.

Um exemplo simples de uma barra de Explorer personalizada

Este exemplo passa pela implementação da barra de Explorer vertical de exemplo mostrada na introdução.

O procedimento básico para criar uma barra de Explorer personalizada é o seguinte.

  1. Implemente as funções necessárias para a DLL.
  2. Implemente as interfaces COM necessárias.
  3. Implemente as interfaces COM opcionais desejadas.
  4. Registre o CLSID do objeto e, se necessário, a categoria de componente.
  5. Crie uma janela filho de Explorer da Internet, dimensionada para se ajustar à região de exibição da Barra de Explorer.
  6. Use a janela filho para exibir informações e interagir com o usuário.

A implementação muito simples usada no exemplo de barra de Explorer pode realmente ser usada para qualquer tipo de barra de Explorer ou uma faixa de mesa, simplesmente registrando-a para a categoria de componente apropriada. Implementações mais sofisticadas precisarão ser personalizadas para a região de exibição e o contêiner de cada tipo de objeto. No entanto, grande parte dessa personalização pode ser realizada usando o código de exemplo e estendendo-o aplicando técnicas familiares de programação do Windows à janela filho. Por exemplo, você pode adicionar controles para interação do usuário ou gráficos para uma exibição mais avançada.

Funções DLL

Todos os três objetos são empacotados em uma única DLL, o que expõe as funções a seguir.

As três primeiras funções são implementações padrão e não serão discutidas aqui. A implementação do Class Factory também é padrão.

Implementações de interface necessárias

O exemplo vertical Explorer Bar implementa as quatro interfaces necessárias: IUnknown, IObjectWithSite, IPersistStream e IDeskBand como parte da classe CExplorerBar. As implementações de construtor, destruidor e IUnknown são simples e não serão discutidas aqui. Consulte o código de exemplo para obter detalhes.

As interfaces a seguir são discutidas em detalhes.

Iobjectwithsite

Quando o usuário seleciona um Explorer Bar, o contêiner chama o método IObjectWithSite::SetSite do objeto de banda correspondente. O parâmetro punkSite será definido como o ponteiro IUnknown do site.

Em geral, uma implementação setSite deve executar as seguintes etapas:

  1. Libere qualquer ponteiro do site que esteja sendo mantido no momento.
  2. Se o ponteiro passado para SetSite for definido como NULL, a banda será removida. SetSite pode retornar S_OK.
  3. Se o ponteiro passado para SetSite não for NULL, um novo site estará sendo definido. SetSite deve fazer o seguinte:
    1. Chame QueryInterface no site para sua interface IOleWindow .
    2. Chame IOleWindow::GetWindow para obter o identificador da janela pai. Salve o identificador para uso posterior. Libere IOleWindow se ele não for mais necessário.
    3. Crie a janela do objeto de banda como um filho da janela obtida na etapa anterior. Não crie-o como uma janela visível.
    4. Se o objeto de banda implementar IInputObject, chame QueryInterface no site para sua interface IInputObjectSite . Armazene o ponteiro para essa interface para uso posterior.
    5. Se todas as etapas forem bem-sucedidas, retorne S_OK. Caso contrário, retorne o código de erro definido por OLE indicando o que falhou.

O exemplo da Barra de Explorer implementa SetSite da maneira a seguir. No código a seguir m_pSite é uma variável de membro privado que contém o ponteiro IInputObjectSite e m_hwndParent contém o identificador da janela pai. Neste exemplo, a criação da janela também é tratada. Se a janela não existir, esse método criará a janela da barra de Explorer como um filho de tamanho apropriado da janela pai obtida pelo SetSite. O identificador da janela filho é armazenado em 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;
}

A implementação getSite do exemplo simplesmente encapsula uma chamada para o método QueryInterface do site, usando o ponteiro do site salvo por SetSite.

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

O Explorer da Internet chamará a interface IPersistStream da barra de Explorer para permitir que a Barra de Explorer carregue ou salve dados persistentes. Se não houver dados persistentes, os métodos ainda deverão retornar um código de êxito. A interface IPersistStream herda do IPersist, portanto, cinco métodos devem ser implementados.

O exemplo de barra de Explorer não usa dados persistentes e tem apenas uma implementação mínima de IPersistStream. IPersist::GetClassID retorna o CLSID (CLSID_SampleExplorerBar) do objeto e o restante retorna S_OK, S_FALSE ou E_NOTIMPL.

IDeskBand

A interface IDeskBand é específica para objetos de banda. Além de seu único método, ele herda de IDockingWindow, que por sua vez herda de IOleWindow.

Há dois métodos IOleWindow : GetWindow e IOleWindow::ContextSensitiveHelp. A implementação da janela filho do GetWindow do exemplo da barra de Explorer retorna o identificador de janela filho da barra de Explorer, m_hwnd. A Ajuda contextual não é implementada, portanto , ContextSensitiveHelp retorna E_NOTIMPL.

A interface IDockingWindow tem três métodos.

O método ResizeBorderDW não é usado com nenhum tipo de objeto de banda e deve sempre retornar E_NOTIMPL. O método ShowDW mostra ou oculta a janela da barra de Explorer, dependendo do valor de seu parâmetro.

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

    return S_OK;
}

O método CloseDW destrói a janela da barra de Explorer.

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

    return S_OK;
}

O método restante, GetBandInfo, é específico de IDeskBand. O Explorer da Internet o usa para especificar o identificador e o modo de exibição da Barra de Explorer. Os Explorer da Internet também podem solicitar uma ou mais informações da Barra de Explorer preenchendo o membro dwMask da estrutura DESKBANDINFO que é passada como o terceiro parâmetro. GetBandInfo deve armazenar o identificador e o modo de exibição e preencher a estrutura DESKBANDINFO com os dados solicitados. O exemplo Explorer Bar implementa GetBandInfo, conforme mostrado no exemplo de código a seguir.

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;
}

Implementações de interface opcionais

Há duas interfaces que não são necessárias, mas que podem ser úteis para implementar: IInputObject e IContextMenu. O exemplo Explorer Bar implementa IInputObject. Consulte a documentação para obter informações sobre como implementar IContextMenu.

IInputObject

A interface IInputObject deverá ser implementada se um objeto de banda aceitar a entrada do usuário. A Internet Explorer implementa IInputObjectSite e usa IInputObject para manter o foco de entrada do usuário adequado quando ele tiver mais de uma janela contida. Há três métodos que precisam ser implementados por uma barra de Explorer.

O Explorer da Internet chama UIActivateIO para informar à Barra de Explorer que ela está sendo ativada ou desativada. Quando ativado, o exemplo da barra de Explorer chama SetFocus para definir o foco para sua janela.

O Explorer da Internet chama HasFocusIO quando está tentando determinar qual janela tem foco. Se a janela da barra de Explorer ou um de seus descendentes tiver foco, HasFocusIO deverá retornar S_OK. Caso contrário, ele deve retornar S_FALSE.

TranslateAcceleratorIO permite que o objeto processe aceleradores de teclado. O exemplo da Barra de Explorer não implementa esse método, portanto, retorna S_FALSE.

A implementação da barra de exemplo de IInputObjectSite é a seguinte.

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;
};

Registro CLSID

Assim como acontece com todos os objetos COM, o CLSID da barra de Explorer deve ser registrado. Para que o objeto funcione corretamente com a Internet Explorer, ele também deve ser registrado para a categoria de componente apropriada (CATID_InfoBand). A seção de código relevante para a Barra de Explorer é mostrada no exemplo de código a seguir.

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;
}

O registro de objetos de banda no exemplo usa procedimentos COM normais.

Além do CLSID, o servidor de objetos de banda também deve ser registrado para uma ou mais categorias de componente. Essa é, na verdade, a diferença main na implementação entre os exemplos de barra de Explorer vertical e horizontal. Esse processo é tratado criando um objeto gerenciador de categorias de componente (CLSID_StdComponentCategoriesMgr) e usando o método ICatRegister::RegisterClassImplCategories para registrar o servidor de objetos de banda. Neste exemplo, o registro de categoria de componente é tratado passando CLSID e CATID do exemplo da barra de Explorer para uma função privada — RegisterComCat — conforme mostrado no exemplo de código a seguir.

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;
}

O procedimento de janela

Como um objeto de banda usa uma janela filho para sua exibição, ele deve implementar um procedimento de janela para lidar com mensagens do Windows. O exemplo de banda tem funcionalidade mínima, portanto, seu procedimento de janela trata apenas cinco mensagens:

O procedimento pode ser facilmente expandido para acomodar mensagens adicionais para dar suporte a mais recursos.

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;
}

O manipulador de WM_COMMAND simplesmente retorna zero. O manipulador WM_PAINT cria a exibição de texto simples mostrada no exemplo da Barra de Explorer na introdução.

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);
    }
}

Os manipuladores WM_SETFOCUS e WM_KILLFOCUS informam ao site uma alteração de foco chamando o método IInputObjectSite::OnFocusChangeIS do site.

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

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

Os objetos de banda fornecem uma maneira flexível e poderosa de estender os recursos de Explorer da Internet criando barras de Explorer personalizadas. Implementar uma faixa de mesa permite estender os recursos de janelas normais. Embora alguma programação COM seja necessária, ela serve para fornecer uma janela filho para sua interface do usuário. A partir daí, a maior parte da implementação pode usar técnicas familiares de programação do Windows. Embora o exemplo discutido aqui tenha apenas funcionalidade limitada, ele ilustra todos os recursos necessários de um objeto de banda e pode ser prontamente estendido para criar uma interface de usuário única e poderosa.