編輯

共用方式為


ATL 控制項內含項目常見問題集

哪些 ATL 類別可協助 ActiveX 控制項內含項目?

ATL 的控件裝載程式代碼不需要您使用任何 ATL 類別;您可以只建立 「AtlAxWin80」 視窗,並視需要使用控件裝載 API(如需詳細資訊,請參閱 什麼是 ATL 控件裝載 API。 不過,下列類別可讓內含專案功能更容易使用。

類別 描述
CAxWindow 包裝 「AtlAxWin80」 視窗,提供建立視窗、建立控件和/或附加控件至視窗的方法,以及擷取主物件上的介面指標。
CAxWindow2T 包裝 「AtlAxWinLic80」 視窗,提供建立視窗、建立控件及/或附加授權控件至視窗的方法,以及擷取主物件上的介面指標。
CComCompositeControl 做為以對話資源為基礎的 ActiveX 控制件類別基類。 這類控制項可以包含其他 ActiveX 控制件。
CAxDialogImpl 做為以對話資源為基礎的對話類別基類。 這類對話框可以包含 ActiveX 控制件。
CWindow 提供方法 GetDlgControl,這個方法會在其主視窗的標識碼下,傳回控件上的介面指標。 此外,所公開的 CWindow Windows API 包裝函式通常會讓視窗管理更容易。

什麼是 ATL 控制項裝載 API?

ATL 的控件裝載 API 是一組函式,可讓任何視窗作為 ActiveX 控制件容器。 這些函式可以靜態或動態連結至您的專案,因為它們可作為原始程式碼,並由ATL90.dll公開。 控件裝載函式會列在下表中。

函式 描述
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 物件,代表 ATL 針對特定視窗提供的 ActiveX 控件容器。 主機物件會子類別化容器視窗,使其可以反映控件的訊息、它提供控件要使用的必要容器介面,並公開 IAxWinHostWindowIAxWinAmbientDispatch 介面,讓您設定控件的環境。

您可以使用主機物件來設定容器的環境屬性。

我可以在單一視窗中裝載多個控制項嗎?

無法在單一 ATL 主機視窗中裝載多個控制件。 每個主機窗口的設計目的是一次只保存一個控件(這允許一個簡單的機制來處理訊息反映和個別控件的環境屬性)。 不過,如果您需要使用者在單一視窗中看到多個控件,建立多個主視窗做為該視窗的子系相當簡單。

我可以重複使用主機視窗嗎?

不建議重複使用主機視窗。 為了確保程式代碼的健全性,您應該將主機視窗的存留期系結至單一控件的存留期。

何時需要呼叫 AtlAxWinTerm?

AtlAxWinTerm 會取消註冊 「AtlAxWin80」 視窗類別。 您應該呼叫此函式(如果您不再需要建立主機視窗),所有現有的主機視窗都已終結。 如果您未呼叫此函式,當進程終止時,視窗類別會自動取消註冊。

使用 ATL AXHost 裝載 ActiveX 控制件

本節中的範例示範如何建立 AXHost,以及如何使用各種 ATL 函式裝載 ActiveX 控件。 它也會示範如何從裝載的控件存取控件和接收事件(使用 IDispEventImpl)。 範例會在主視窗或子視窗中裝載行事歷控件。

請注意符號的定義 USE_METHOD 。 您可以變更此符號的值,以在 1 到 8 之間變更。 符號的值會決定控件的建立方式:

  • 針對的偶數值 USE_METHOD,呼叫以建立主機子類別視窗,並將它轉換成控件主機。 針對奇數值,程式代碼會建立做為主機的子視窗。

  • 對於介於 1 到 4 之間的值 USE_METHOD ,在也會建立主機的呼叫中完成對事件的控件和接收的存取。 介於 5 到 8 之間的值會查詢主機是否有介面並連結接收。

摘要如下:

USE_METHOD Host 控制存取和事件接收 示範的函式
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;   
   }
}