Часто задаваемые вопросы о вложении элементов управления ATL

Какие классы ATL упрощают вложение элементов ActiveX?

Код размещения управления ATL не требует использования классов ATL; Вы можете просто создать окно AtlAxWin80 и при необходимости использовать API размещения элементов управления (дополнительные сведения см. в разделе "Что такое API размещения управления ATL". Однако следующие классы упрощают использование функций хранения.

Класс Description
CAxWindow Выполняет оболочку окна AtlAxWin80 , предоставляя методы для создания окна, создания элемента управления и (или) присоединения элемента управления к окну и получения указателей интерфейса на объекте узла.
CAxWindow2T Выполняет оболочку окна AtlAxWinLic80 , предоставляя методы для создания окна, создания элемента управления и /или присоединения лицензированного элемента управления к окну и получения указателей интерфейса на объекте узла.
CComCompositeControl Выступает в качестве базового класса для классов элементов управления ActiveX на основе ресурса диалогового окна. Такие элементы управления могут содержать другие элементы ActiveX.
Caxdialogimpl Выступает в качестве базового класса для классов диалоговых окон на основе ресурса диалога. Такие диалоги могут содержать элементы ActiveX.
Cwindow Предоставляет метод GetDlgControl, который вернет указатель интерфейса на элемент управления с идентификатором его окна узла. Кроме того, оболочки API Windows, предоставляемые CWindow , как правило, упрощают управление окнами.

Что такое интерфейс API для размещения элементов управления ATL?

API размещения элементов ATL — это набор функций, который позволяет любому окну выступать в качестве контейнера элементов управления ActiveX. Эти функции могут быть статически или динамически связаны с проектом, так как они доступны в качестве исходного кода и предоставляются ATL90.dll. Функции размещения элементов управления перечислены в таблице ниже.

Function Description
AtlAxAttachControl Создает объект узла, подключает его к указанному окну, а затем присоединяет существующий элемент управления.
AtlAxCreateControl Создает объект узла, подключает его к предоставленному окну, а затем загружает элемент управления.
AtlAxCreateControlLic Создает лицензированный элемент ActiveX, инициализирует его и размещает в указанном окне, аналогично AtlAxCreateControl.
AtlAxCreateControlEx Создает объект узла, подключает его к предоставленному окну, а затем загружает элемент управления (также позволяет настроить приемники событий).
AtlAxCreateControlLicEx Создает лицензированный элемент ActiveX, инициализирует его и размещает в указанном окне, аналогично AtlAxCreateControlLic.
AtlAxCreateDialog Создает диалоговое окно без режима из ресурса диалогового окна и возвращает дескриптор окна.
AtlAxDialogBox Создает модальное диалоговое окно из ресурса диалогового окна.
AtlAxGetControl Возвращает указатель интерфейса IUnknown элемента управления, размещенного в окне.
AtlAxGetHost Возвращает указатель интерфейса IUnknown объекта узла, подключенного к окну.
AtlAxWinInit Инициализирует код размещения элементов управления.
AtlAxWinTerm Неинициализирует код размещения элементов управления.

Параметры HWND в первых трех функциях должны быть существующим окном (почти) любого типа. Если вы вызываете любую из этих трех функций явным образом (как правило, вам не придется), не передайте дескриптор в окно, которое уже действует как узел (если вы делаете, существующий объект узла не будет освобожден).

Первые семь функций вызывают AtlAxWinInit неявно.

Примечание.

API размещения элементов управления формирует основу поддержки ATL для хранения элементов activeX. Однако обычно не нужно вызывать эти функции напрямую, если вы используете преимущества или полностью используете классы оболочки ATL. Дополнительные сведения см. в статье о том, какие классы ATL упрощают сдерживание элементов ActiveX.

Что такое AtlAxWin100?

AtlAxWin100 — это имя класса окна, который помогает предоставить функции размещения элементов управления ATL. При создании экземпляра этого класса процедура окна автоматически будет использовать API размещения элементов управления для создания объекта узла, связанного с окном, и загрузить его с элементом управления, указанным в качестве заголовка окна.

Когда нужно вызывать AtlAxWinInit?

AtlAxWinInit регистрирует класс окна AtlAxWin80 (плюс несколько пользовательских сообщений окна), поэтому перед созданием окна узла необходимо вызвать эту функцию. Однако не всегда нужно явно вызывать эту функцию, так как api размещения (и классы, которые используют их) часто вызывают эту функцию. При вызове этой функции не существует никакого вреда более одного раза.

Что такое основной объект?

Объект узла — это COM-объект, представляющий контейнер элементов управления ActiveX, предоставленный ATL для определенного окна. Объект узла подклассирует окно контейнера, чтобы он мог отражать сообщения в элементе управления, предоставляет необходимые интерфейсы контейнеров, которые будут использоваться элементом управления, и предоставляет интерфейсы IAxWinHostWindow и IAxWinAmbientDispatch , чтобы разрешить настройку среды элемента управления.

Объект узла можно использовать для задания свойств окружающей среды контейнера.

Можно ли размещать несколько элементов управления в одном окне?

Невозможно разместить несколько элементов управления в одном окне узла ATL. Каждое окно узла предназначено для хранения всего одного элемента управления за раз (это обеспечивает простой механизм обработки отражения сообщений и внешних свойств каждого элемента управления). Однако если пользователю нужно увидеть несколько элементов управления в одном окне, это простой вопрос для создания нескольких окон узлов в качестве дочерних элементов этого окна.

Можно ли использовать основное окно повторно?

Не рекомендуется повторно использовать окна узлов. Чтобы обеспечить надежность кода, необходимо связать время существования окна узла со временем существования одного элемента управления.

Когда нужно вызывать AtlAxWinTerm?

AtlAxWinTerm отменяет регистрацию класса окна AtlAxWin80 . Эту функцию следует вызвать (если вам больше не нужно создавать окна узлов) после того, как все существующие окна узлов были уничтожены. Если вы не вызываете эту функцию, класс окна будет автоматически отменяться при завершении процесса.

Размещение элементов ActiveX с помощью ATL AXHost

В этом разделе показано, как создать AXHost и как разместить элемент ActiveX с помощью различных функций ATL. В нем также показано, как получить доступ к событиям управления и приемника (с помощью IDispEventImpl) из размещенного элемента управления. В примере размещается элемент управления "Календарь" в главном окне или в дочернем окне.

Обратите внимание на определение символа USE_METHOD . Значение этого символа можно изменить в диапазоне от 1 до 8. Значение символа определяет, как будет создан элемент управления:

  • Для четных значений USE_METHODвызов для создания подкласса узла преобразует окно и преобразует его в узел управления. Для нечетных значений код создает дочернее окно, которое выступает в качестве узла.

  • Для значений USE_METHOD от 1 до 4 доступ к элементу управления и приемнику событий выполняется в вызове, который также создает узел. Значения от 5 до 8 запрашивают узел для интерфейсов и перехватывают приемник.

Ниже приведена сводная информация о вариантах.

USE_METHOD Хост Управление приемниками доступа и событий Показана функция
1 Дочернее окно Один шаг CreateControlLicEx
2 Главное окно Один шаг AtlAxCreateControlLicEx
3 Дочернее окно Один шаг CreateControlEx
4 Главное окно Один шаг AtlAxCreateControlEx
5 Дочернее окно Несколько шагов CreateControlLic
6 Главное окно Несколько шагов AtlAxCreateControlLic
7 Дочернее окно Несколько шагов CreateControl
8 Главное окно Несколько шагов 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;   
   }
}