Perguntas frequentes sobre contenção de controle ATL

Quais classes da ATL facilitam a contenção de controle ActiveX?

O código de hospedagem de controle da ATL não exige que você use nenhuma classe ATL; você pode simplesmente criar uma janela "AtlAxWin80" e usar a API de hospedagem de controle, se necessário (para obter mais informações, consulte Qual é a API de hospedagem de controle da ATL. No entanto, as classes a seguir facilitam o uso dos recursos de independência.

Classe Descrição
CAxWindow Encapsula uma janela "AtlAxWin80", fornecendo métodos para criar a janela, criando um controle e/ou anexando um controle à janela e recuperando ponteiros de interface no objeto host.
CAxWindow2T Encapsula uma janela "AtlAxWinLic80", fornecendo métodos para criar a janela, criando um controle e/ou anexando um controle licenciado à janela e recuperando ponteiros de interface no objeto host.
CComCompositeControl Atua como uma classe base para classes de controle ActiveX com base em um recurso de diálogo. Esses controles podem conter outros controles ActiveX.
CAxDialogImpl Atua como uma classe base para classes de diálogo com base em um recurso de diálogo. Esses diálogo podem conter controles ActiveX.
CWindow Fornece um método, GetDlgControl, que retornará um ponteiro de interface em um controle, dada a ID de sua janela de host. Além disso, os wrappers de API do Windows expostos por CWindow geralmente facilitam o gerenciamento de janelas.

O que é a API de hospedagem de controles da ATL?

A API de hospedagem de controle da ATL é o conjunto de funções que permite que qualquer janela atue como um contêiner de controle ActiveX. Essas funções podem ser vinculadas estática ou dinamicamente ao seu projeto, pois estão disponíveis como código-fonte e expostas por ATL90.dll. As funções de hospedagem de controle são listadas na tabela a seguir.

Função Descrição
AtlAxAttachControl Cria um objeto host, conecta-o à janela fornecida e anexa um controle existente.
AtlAxCreateControl Cria um objeto host, conecta-o à janela fornecida e carrega um controle.
AtlAxCreateControlLic Cria um controle ActiveX licenciado, inicializa-o e hospeda-o na janela especificada, semelhante a AtlAxCreateControl.
AtlAxCreateControlEx Cria um objeto host, conecta-o à janela fornecida e carrega um controle (também permite que os coletores de eventos sejam configurados).
AtlAxCreateControlLicEx Cria um controle ActiveX licenciado, inicializa-o e hospeda-o na janela especificada, semelhante a AtlAxCreateControlLic.
AtlAxCreateDialog Cria uma caixa de diálogo sem modelo a partir de um recurso de diálogo e retorna o identificador da janela.
AtlAxDialogBox Cria uma caixa de diálogo modal a partir de um recurso de diálogo.
AtlAxGetControl Retorna o ponteiro da interface IUnknown do controle hospedado em uma janela.
AtlAxGetHost Retorna o ponteiro da interface IUnknown do objeto host conectado a uma janela.
AtlAxWinInit Inicializa o código de hospedagem de controle.
AtlAxWinTerm Cancela a inicialização do código de hospedagem de controle.

Os parâmetros HWND nas três primeiras funções devem ser uma janela existente de (quase) qualquer tipo. Se você chama qualquer uma dessas três funções explicitamente (normalmente, não será necessário), não passe um identificador para uma janela que já está agindo como um host (se você fizer isso, o objeto host existente não será liberado).

As sete primeiras funções chamam AtlAxWinInit implicitamente.

Observação

A API de hospedagem de controle forma a base do suporte da ATL para a independência de controle ActiveX. No entanto, geralmente, não é necessário chamar essas funções diretamente se você tira proveito ou faz uso total das classes wrapper da ATL. Para obter mais informações, confira Quais classes ATL facilitam a independência de controle ActiveX.

O que é AtlAxWin100?

AtlAxWin100 é o nome de uma classe de janela que ajuda a fornecer a funcionalidade de hospedagem de controle da ATL. Quando você cria uma instância dessa classe, o procedimento de janela usa automaticamente a API de hospedagem de controle para criar um objeto host associado à janela e carregá-lo com o controle especificado como o título da janela.

Quando é necessário chamar AtlAxWinInit?

AtlAxWinInit registra a classe de janela "AtlAxWin80" (além de algumas mensagens de janela personalizadas), portanto, essa função deve ser chamada antes de você tentar criar uma janela de host. No entanto, nem sempre você precisa chamar essa função explicitamente, pois as APIs de hospedagem (e as classes que as usam) geralmente chamam essa função para você. Não há problema em chamar essa função mais de uma vez.

O que é um objeto de host?

Um objeto host é um objeto COM que representa o contêiner de controle ActiveX fornecido pela ATL para uma janela específica. O objeto host cria uma subclasse da janela do contêiner para que ela possa refletir mensagens para o controle. Ele fornece as interfaces de contêiner necessárias a serem usadas pelo controle e expõe as interfaces IAxWinHostWindow e IAxWinAmbientDispatch para permitir que você configure o ambiente do controle.

Você pode usar o objeto host para definir as propriedades de ambiente do contêiner.

Posso hospedar mais de um controle em uma única janela?

Não é possível hospedar mais de um controle em uma única janela de host da ATL. Cada janela de host foi projetada para conter exatamente um controle por vez (isso permite um mecanismo simples para lidar com a reflexão de mensagens e propriedades ambientes por controle). No entanto, se você precisar que o usuário veja vários controles em uma única janela, basta criar várias janelas de host como filhos dessa janela.

Posso reutilizar uma janela do host?

Não é recomendável reutilizar janelas de host. Para garantir a robustez do código, você deve vincular o tempo de vida da janela do host ao tempo de vida de um único controle.

Quando é necessário chamar AtlAxWinTerm?

AtlAxWinTerm cancela o registro da classe de janela "AtlAxWin80". Você deve chamar essa função (se não precisar mais criar janelas de host) depois que todas as janelas de host existentes tiverem sido destruídas. Se você não chamar essa função, o registro da classe de janela será cancelado automaticamente quando o processo for encerrado.

Hospedando controles ActiveX usando ATL AXHost

A amostra nesta seção mostra como criar o AXHost e como hospedar um controle ActiveX usando várias funções da ATL. Ela também mostra como acessar os eventos de controle e coletor (usando IDispEventImpl) a partir do controle hospedado. A amostra hospeda o controle Calendário em uma janela principal ou em uma janela filho.

Observe a definição do símbolo USE_METHOD. Você pode alterar o valor desse símbolo para variar entre 1 e 8. O valor do símbolo determina como o controle será criado:

  • Para valores pares de USE_METHOD, a chamada para criar a subclasse host de uma janela e convertê-la em um host de controle. Para valores ímpares, o código cria uma janela filho que atua como host.

  • Para valores de USE_METHOD entre 1 e 4, o acesso a eventos de controle e coletor são realizados na chamada que também cria o host. Valores entre 5 e 8 consultam o host para interfaces e conectam o coletor.

Segue um resumo:

USE_METHOD Host Controlar o acesso e a coleta de eventos Função demonstrada
1 Janela filho Uma etapa CreateControlLicEx
2 Janela principal Uma etapa AtlAxCreateControlLicEx
3 Janela filho Uma etapa CreateControlEx
4 Janela principal Uma etapa AtlAxCreateControlEx
5 Janela filho Várias etapas CreateControlLic
6 Janela principal Várias etapas AtlAxCreateControlLic
7 Janela filho Várias etapas CreateControl
8 Janela principal Várias etapas AtlAxCreateControl
// Your project must be apartment threaded or the (AtlAx)CreateControl(Lic)(Ex)
// calls will fail.
#define _ATL_APARTMENT_THREADED
#include <atlbase.h>
#include <atlwin.h>
#include <atlhost.h>

// Value of g_UseMethod determines the function used to create the control.
int g_UseMethod = 0; // 1 to 8 are valid values
bool ValidateUseMethod() { return (1 <= g_UseMethod) && (g_UseMethod <= 8); }

#import "PROGID:MSCAL.Calendar.7" no_namespace, raw_interfaces_only

// Child window class that will be subclassed for hosting Active X control
class CChildWindow : public CWindowImpl<CChildWindow>
{
public:
   BEGIN_MSG_MAP(CChildWindow)
   END_MSG_MAP()
};

class CMainWindow : public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>,
   public IDispEventImpl<1, CMainWindow, &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>
{
public :

   CChildWindow m_wndChild;
   CAxWindow2 m_axwnd;
   CWindow m_wndEdit;

   static ATL::CWndClassInfo& GetWndClassInfo()
   {
      static ATL::CWndClassInfo wc =
      {
         { 
            sizeof(WNDCLASSEX), 
            CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, 
            StartWindowProc,
            0, 0, NULL, NULL, NULL, 
            (HBRUSH)(COLOR_WINDOW + 1), 
            0, 
            _T("MainWindow"), 
            NULL 
         },
         NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
      };
      return wc;
   }
   
   BEGIN_MSG_MAP(CMainWindow)
      MESSAGE_HANDLER(WM_CREATE, OnCreate)
      MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
   END_MSG_MAP()

   BEGIN_SINK_MAP(CMainWindow)
      SINK_ENTRY_EX(1, __uuidof(DCalendarEvents), DISPID_CLICK, OnClick)
   END_SINK_MAP()

   // Helper to display events
   void DisplayNotification(TCHAR* pszMessage)
   {
      CWindow wnd;
      wnd.Attach(GetDlgItem(2));
      
      wnd.SendMessage(EM_SETSEL, (WPARAM)-1, -1);
      wnd.SendMessage(EM_REPLACESEL, 0, (LPARAM)pszMessage);
   }
   
   // Event Handler for Click
   STDMETHOD(OnClick)()
   {
      DisplayNotification(_T("OnClick\r\n"));
      return S_OK;
   }

   LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
   {
      HRESULT hr = E_INVALIDARG;

      _pAtlModule->Lock();

      RECT rect;
      GetClientRect(&rect);
      
      RECT rect2;
      rect2 = rect;
      
      rect2.bottom -=200;
      
      // if g_UseMethod is odd then create AxHost directly as the child of the main window
      if (g_UseMethod & 0x1) 
      {
         m_axwnd.Create(m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 1);
      }
      // if g_UseMethod is even then the AtlAx version is invoked.
      else
      {
         // Create a child window.
         // AtlAx functions will subclass this window.
         m_wndChild.Create(m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 1);
         // Attach the child window to the CAxWindow so we can access the 
         // host that subclasses the child window.
         m_axwnd.Attach(m_wndChild);
      }

      if (m_axwnd.m_hWnd != NULL)
      {
         CComPtr<IUnknown> spControl;

         // The calls to (AtlAx)CreateControl(Lic)(Ex) do the following:
         // Create Calendar control. (Passing in NULL for license key. 
         //   Pass in valid license key to the Lic functions if the 
         //   control requires one.)
         // Get the IUnknown pointer for the control.
         // Sink events from the control.
         
         // The AtlAx versions subclass the hWnd that is passed in to them 
         //   to implement the host functionality.

         // The first 4 calls accomplish it in one call.
         // The last 4 calls accomplish it using multiple steps.

         switch (g_UseMethod)
         {
            case 1:
            {
               hr = m_axwnd.CreateControlLicEx(
                  OLESTR("MSCAL.Calendar.7"), 
                  NULL, 
                  NULL, 
                  &spControl, 
                  __uuidof(DCalendarEvents), 
                  (IUnknown*)(IDispEventImpl<1, CMainWindow, 
                     &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
               );
               break;
            }
            case 2:
            {
               hr = AtlAxCreateControlLicEx(
                  OLESTR("MSCAL.Calendar.7"), 
                  m_wndChild.m_hWnd, 
                  NULL, 
                  NULL, 
                  &spControl, 
                  __uuidof(DCalendarEvents), 
                  (IUnknown*)(IDispEventImpl<1, CMainWindow, 
                     &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this, 
                  NULL
               );
               break;
            }
            case 3:
            {
               hr = m_axwnd.CreateControlEx(
                  OLESTR("MSCAL.Calendar.7"), 
                  NULL, 
                  NULL, 
                  &spControl, 
                  __uuidof(DCalendarEvents), 
                  (IUnknown*)(IDispEventImpl<1, CMainWindow, 
                     &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
               );
               break;
            }
            case 4:
            {
               hr = AtlAxCreateControlEx(
                  OLESTR("MSCAL.Calendar.7"), 
                  m_wndChild.m_hWnd, 
                  NULL, 
                  NULL, 
                  &spControl, 
                  __uuidof(DCalendarEvents), 
                  (IUnknown*)(IDispEventImpl<1, CMainWindow, 
                     &__uuidof(DCalendarEvents), &__uuidof(__MSACAL), 7, 0>*)this
               );
               break;
            }
            // The following calls create the control, obtain an interface to 
            // the control, and set up the sink in multiple steps.
            case 5:
            {
               hr = m_axwnd.CreateControlLic(
                  OLESTR("MSCAL.Calendar.7")
               );
               break;
            }
            case 6:
            {
               hr = AtlAxCreateControlLic(
                  OLESTR("MSCAL.Calendar.7"), 
                  m_wndChild.m_hWnd, 
                  NULL, 
                  NULL
               );
               break;
            }
            case 7:
            {
               hr = m_axwnd.CreateControl(
                  OLESTR("MSCAL.Calendar.7")
               );
               break;
            }
            case 8:
            {
               hr = AtlAxCreateControl(
                  OLESTR("MSCAL.Calendar.7"), 
                  m_wndChild.m_hWnd , 
                  NULL, 
                  NULL
               );
               break;
            }
         }

         // have to obtain an interface to the control and set up the sink
         if (g_UseMethod > 4)
         {
            if (SUCCEEDED(hr))
            {
               hr = m_axwnd.QueryControl(&spControl);
               if (SUCCEEDED(hr))
               {
                  // Sink events form the control
                  DispEventAdvise(spControl, &__uuidof(DCalendarEvents));
               }
            }
         }

         if (SUCCEEDED(hr))
         {
            // Use the returned IUnknown pointer.
            CComPtr<ICalendar> spCalendar;
            hr = spControl.QueryInterface(&spCalendar);
            if (SUCCEEDED(hr))
            {
               spCalendar->put_ShowDateSelectors(VARIANT_FALSE);
            }
         }
      }

      rect2 = rect;
      rect2.top = rect.bottom - 200 + 1;
      m_wndEdit.Create(_T("Edit"), m_hWnd, rect2, NULL, WS_CHILD | WS_VISIBLE | 
         WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0, 2);
      return 0;
   }

   LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
   {
      _pAtlModule->Unlock();
      return 0;
   }
};

class CHostActiveXModule : public CAtlExeModuleT<CHostActiveXModule>
{
public :

   CMainWindow m_wndMain;

   // Create the Main window
   HRESULT PreMessageLoop(int nCmdShow)
   {
      HRESULT hr = CAtlExeModuleT<CHostActiveXModule>::PreMessageLoop(nCmdShow);
      if (SUCCEEDED(hr))
      {
         AtlAxWinInit();
         hr = S_OK;
         RECT rc;
         rc.top = rc.left = 100;
         rc.bottom = rc.right = 500;
         m_wndMain.Create(NULL, rc, _T("Host Calendar") );
         m_wndMain.ShowWindow(nCmdShow);         
      }
      return hr;
   }

   // Clean up. App is exiting.
   HRESULT PostMessageLoop()
   {
      AtlAxWinTerm();
      return CAtlExeModuleT<CHostActiveXModule>::PostMessageLoop();
   }
};

CHostActiveXModule _AtlModule;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
   UNREFERENCED_PARAMETER(hInstance);
   UNREFERENCED_PARAMETER(hPrevInstance);

   g_UseMethod = _ttoi(lpCmdLine);

   if (ValidateUseMethod())
   {
      return _AtlModule.WinMain(nCmdShow);
   }
   else
   {
      return E_INVALIDARG;   
   }
}