Domande frequenti sul contenimento di controlli ATL

Quali classi ATL semplificano il contenimento di controlli ActiveX?

Il codice di hosting del controllo ATL non richiede l'uso di classi ATL; è sufficiente creare una finestra "AtlAxWin80" e usare l'API di hosting del controllo, se necessario. Per altre informazioni, vedere What Is the ATL Control-Hosting API (Informazioni sull'API di hosting del controllo ATL). Tuttavia, le classi seguenti semplificano l'uso delle funzionalità di contenimento.

Classe Descrizione
CAxWindow Esegue il wrapping di una finestra "AtlAxWin80", fornendo metodi per la creazione della finestra, la creazione di un controllo e/o il collegamento di un controllo alla finestra e il recupero dei puntatori di interfaccia nell'oggetto host.
CAxWindow2T Esegue il wrapping di una finestra "AtlAxWinLic80", fornendo metodi per la creazione della finestra, la creazione di un controllo e/o il collegamento di un controllo concesso in licenza alla finestra e il recupero di puntatori di interfaccia nell'oggetto host.
CComCompositeControl Funge da classe di base per le classi di controlli ActiveX basate su una risorsa di dialogo. Tali controlli possono contenere altri controlli ActiveX.
CAxDialogImpl Funge da classe base per le classi di dialoghi basate su una risorsa di dialogo. Tali dialoghi possono contenere controlli ActiveX.
CWindow Fornisce un metodo, GetDlgControl, che restituirà un puntatore di interfaccia su un controllo, dato l'ID della finestra host. Inoltre, i wrapper dell'API Windows esposti in CWindow genere semplificano la gestione delle finestre.

Che cosa è l'API di hosting di controllo per ATL?

L'API control-hosting di ATL è il set di funzioni che consente a qualsiasi finestra di fungere da contenitore di controlli ActiveX. Queste funzioni possono essere collegate in modo statico o dinamico al progetto perché sono disponibili come codice sorgente ed esposte da ATL90.dll. Le funzioni di hosting del controllo sono elencate nella tabella seguente.

Funzione Descrizione
AtlAxAttachControl Crea un oggetto host, lo connette alla finestra fornita, quindi collega un controllo esistente.
AtlAxCreateControl Crea un oggetto host, lo connette alla finestra fornita, quindi carica un controllo.
AtlAxCreateControlLic Crea un controllo ActiveX concesso in licenza, lo inizializza e lo ospita nella finestra specificata, simile ad AtlAxCreateControl.
AtlAxCreateControlEx Crea un oggetto host, lo connette alla finestra fornita, quindi carica un controllo (consente anche la configurazione dei sink di eventi).
AtlAxCreateControlLicEx Crea un controllo ActiveX concesso in licenza, lo inizializza e lo ospita nella finestra specificata, simile ad AtlAxCreateControlLic.
AtlAxCreateDialog Crea una finestra di dialogo senza modalità da una risorsa di finestra di dialogo e restituisce l'handle della finestra.
AtlAxDialogBox Crea una finestra di dialogo modale da una risorsa di finestra di dialogo.
AtlAxGetControl Restituisce il puntatore dell'interfaccia IUnknown del controllo ospitato in una finestra.
AtlAxGetHost Restituisce il puntatore dell'interfaccia IUnknown dell'oggetto host connesso a una finestra.
AtlAxWinInit Inizializza il codice di hosting del controllo.
AtlAxWinTerm Annulla l'inizializzazione del codice di hosting del controllo.

I HWND parametri nelle prime tre funzioni devono essere una finestra esistente di (quasi) qualsiasi tipo. Se si chiama una di queste tre funzioni in modo esplicito (in genere, non sarà necessario), non passare un handle a una finestra che funge già da host (se lo si fa, l'oggetto host esistente non verrà liberato).

Le prime sette funzioni chiamano in modo implicito AtlAxWinInit .

Nota

L'API di hosting del controllo costituisce la base del supporto di ATL per il contenimento dei controlli ActiveX. Tuttavia, in genere non è necessario chiamare queste funzioni direttamente se si sfruttano o si usano completamente le classi wrapper di ATL. Per altre informazioni, vedere Quali classi ATL facilitano il contenimento dei controlli ActiveX.

Che cos'è AtlAxWin100?

AtlAxWin100 è il nome di una classe finestra che consente di fornire la funzionalità di hosting del controllo ATL. Quando si crea un'istanza di questa classe, la routine window userà automaticamente l'API di hosting del controllo per creare un oggetto host associato alla finestra e caricarlo con il controllo specificato come titolo della finestra.

Quando è necessario chiamare AtlAxWinInit?

AtlAxWinInit registra la classe di finestra "AtlAxWin80" (più un paio di messaggi di finestra personalizzati), quindi questa funzione deve essere chiamata prima di provare a creare una finestra host. Tuttavia, non è sempre necessario chiamare questa funzione in modo esplicito, poiché le API di hosting (e le classi che le usano) spesso chiamano questa funzione per l'utente. Non c'è alcun danno per chiamare questa funzione più di una volta.

Che cos'è un oggetto host?

Un oggetto host è un oggetto COM che rappresenta il contenitore di controlli ActiveX fornito da ATL per una determinata finestra. L'oggetto host sottoclassa la finestra contenitore in modo che possa riflettere i messaggi al controllo, fornisce le interfacce contenitore necessarie da usare dal controllo ed espone le interfacce IAxWinHostWindow e IAxWinAmbientDispatch per consentire di configurare l'ambiente del controllo.

È possibile usare l'oggetto host per impostare le proprietà di ambiente del contenitore.

Una finestra singola può ospitare più di un controllo?

Non è possibile ospitare più controlli in una singola finestra host ATL. Ogni finestra host è progettata per contenere esattamente un controllo alla volta (ciò consente un meccanismo semplice per la gestione della reflection dei messaggi e delle proprietà di ambiente per ogni controllo). Tuttavia, se è necessario che l'utente visualizzi più controlli in una singola finestra, è semplice creare più finestre host come elementi figlio di tale finestra.

È possibile riusare una finestra host?

Non è consigliabile riutilizzare le finestre host. Per garantire l'affidabilità del codice, è necessario associare la durata della finestra host alla durata di un singolo controllo.

Quando è necessario chiamare AtlAxWinTerm?

AtlAxWinTerm annulla la registrazione della classe finestra "AtlAxWin80 ". È necessario chiamare questa funzione (se non è più necessario creare finestre host) dopo che tutte le finestre host esistenti sono state eliminate definitivamente. Se non si chiama questa funzione, la classe window verrà annullata automaticamente al termine del processo.

Hosting di controlli ActiveX tramite ATL AXHost

L'esempio in questa sezione illustra come creare AXHost e come ospitare un controllo ActiveX usando varie funzioni ATL. Viene inoltre illustrato come accedere agli eventi di controllo e sink (usando IDispEventImpl) dal controllo ospitato. L'esempio ospita il controllo Calendario in una finestra principale o in una finestra figlio.

Si noti la definizione del USE_METHOD simbolo. È possibile modificare il valore di questo simbolo in modo da variare tra 1 e 8. Il valore del simbolo determina la modalità di creazione del controllo:

  • Per i valori numerati pari di USE_METHOD, la chiamata a creare la sottoclasse host crea una finestra e la converte in un host di controllo. Per i valori dispari, il codice crea una finestra figlio che funge da host.

  • Per i valori compresi USE_METHOD tra 1 e 4, l'accesso al controllo e al sink degli eventi viene eseguito nella chiamata che crea anche l'host. I valori compresi tra 5 e 8 eseguono query sull'host per le interfacce e associano il sink.

Di seguito è disponibile un riepilogo:

USE_METHOD Host Controllare l'accesso e il sink degli eventi Funzione illustrata
1 Finestra figlio Un passaggio CreateControlLicEx
2 Finestra principale Un passaggio AtlAxCreateControlLicEx
3 Finestra figlio Un passaggio CreateControlEx
4 Finestra principale Un passaggio AtlAxCreateControlEx
5 Finestra figlio Più passaggi CreateControlLic
6 Finestra principale Più passaggi AtlAxCreateControlLic
7 Finestra figlio Più passaggi CreateControl
8 Finestra principale Più passaggi 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;   
   }
}