ATL コントロール コンテインメント : Q & A 集

ActiveX コントロール コンテインメイトを助ける ATL クラスはどれか。

ATL のコントロールホスティング コードでは、ATL クラスを使用する必要はありません。"AtlAxWin80" ウィンドウを作成し、必要に応じてコントロールホスティング API を使用するだけで済みます (詳細については、「ATL コントロール ホスト API とは何か」を参照してください)。 しかし、次のクラスを使用すれば、コンテインメント機能が使いやすくなります。

クラス 説明
CAxWindow "AtlAxWin80" ウィンドウをラップし、ウィンドウの作成、コントロールの作成、ウィンドウへのコントロールのアタッチ、ホスト オブジェクトのインターフェイス ポインターの取得を行うメソッドを提供します。
CAxWindow2T "AtlAxWinLic80" ウィンドウをラップし、ウィンドウの作成、コントロールの作成、ライセンスされたコントロールのウィンドウへのアタッチ、ホスト オブジェクトのインターフェイス ポインターの取得を行うメソッドを提供します。
CComCompositeControl ダイアログ リソースに基づく ActiveX コントロール クラスの基本クラスとして機能します。 このようなコントロールには、他の ActiveX コントロールを含めることができます。
CAxDialogImpl ダイアログ リソースに基づくダイアログ クラスの基本クラスとして機能します。 このようなダイアログには、複数の ActiveX コントロールを含めることができます。
CWindow ホスト ウィンドウの ID を指定して、コントロールのインターフェイス ポインターを返す 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 コントロールホスティング コードの初期化を解除します。

最初の 3 つの関数の HWND パラメーターは、(ほぼ) すべての型の既存のウィンドウである必要があります。 これら 3 つの関数のいずれかを明示的に呼び出す場合 (通常はそうする必要はありません)、ホストとして既に機能しているウィンドウにハンドルを渡しません (そのようにすると、既存のホスト オブジェクトが解放されません)。

最初の 7 つの関数では、AtlAxWinInit を暗黙的に呼び出します。

Note

コントロールホスティング API では、ActiveX コントロール コンテインメントに対する ATL のサポートの基礎を形成します。 しかし、ATL のラッパー クラスを利用したり、フル活用したりする場合は、通常、これらの関数を直接呼び出す必要はほとんどありません。 詳細については、「ActiveX コントロール コンテインメイトを助ける ATL クラスはどれか」を参照してください。

AtlAxWin100 とは何か。

AtlAxWin100 は、ATL のコントロールホスティング機能を提供するのに役立つウィンドウ クラスの名前です。 このクラスのインスタンスを作成すると、ウィンドウ プロシージャで自動的にコントロールホスティング API が使用され、ウィンドウに関連するホスト オブジェクトが作成されて、ウィンドウのタイトルとして指定したコントロールと一緒に読み込まれます。

AtlAxWinInit はいつ呼び出す必要があるのか。

AtlAxWinInit では "AtlAxWin80" ウィンドウ クラス (およびいくつかのカスタム ウィンドウ メッセージ) を登録します。そのため、ホスト ウィンドウを作成してみる前に、この関数を呼び出す必要があります。 しかし、ホスティング API (およびそれらを使用するクラス) では多くの場合、自動的にこの関数が呼び出されるため、必ずしもこの関数を明示的に呼び出す必要はありません。 この関数を複数回呼び出しても問題はありません。

ホスト オブジェクトとは何か。

ホスト オブジェクトは、特定のウィンドウに対して ATL によって提供される ActiveX コントロール コンテナーを表す COM オブジェクトです。 ホスト オブジェクトでは、コントロールへのメッセージを反映できるようにコンテナー ウィンドウをサブクラス化し、コントロールで使用するのに必要なコンテナー インターフェイスを提供し、IAxWinHostWindow および IAxWinAmbientDispatch インターフェイスを公開して、コントロールの環境を構成できるようにします。

ホスト オブジェクトを使用して、コンテナーのアンビエント プロパティを設定できます。

1 つのウィンドウで複数のコントロールをホストできるか。

1 つの ATL ホスト ウィンドウで複数のコントロールをホストすることはできません。 各ホスト ウィンドウは、一度に 1 つのコントロールのみを保持するように設計されています (これにより、簡単なメカニズムでメッセージ リフレクションとコントロールごとのアンビエント プロパティを処理できます)。 しかし、ユーザーに対して 1 つのウィンドウで複数のコントロールを表示する必要がある場合、そのウィンドウの子として複数のホスト ウィンドウを作成するのは簡単です。

ホスト ウィンドウを再利用できるか。

ホスト ウィンドウを再利用することはお勧めできません。 コードの堅牢性を確保するには、ホスト ウィンドウの有効期間を 1 つのコントロールの有効期間に結び付ける必要があります。

AtlAxWinTerm はいつ呼び出す必要があるのか。

AtlAxWinTerm では、"AtlAxWin80" ウィンドウ クラスの登録を解除します。 すべての既存のホスト ウィンドウが破棄された後、この関数を呼び出す必要があります (ホスト ウィンドウを作成する必要がなくなった場合)。 この関数を呼び出さない場合、プロセスの終了時にウィンドウ クラスの登録が自動的に解除されます。

ATL AXHost を使用した ActiveX コントロールのホスト

このセクションのサンプルでは、AXHost を作成する方法と、さまざまな ATL 関数を使用して ActiveX コントロールをホストする方法を示します。 また、コントロールにアクセスし、ホストされるコントロールから (IDispEventImpl を使用して) イベントをシンクする方法も示します。 このサンプルでは、メイン ウィンドウまたは子ウィンドウで Calendar コントロールをホストします。

USE_METHOD シンボルの定義に注目してください。 このシンボルの値を異なる 1 から 8 の間で変更できます。 シンボルの値によって、コントロールの作成方法が決まります。

  • USE_METHOD が偶数値の場合、ホストを作成する呼び出しではウィンドウをサブクラス化し、コントロール ホストに変換します。 奇数値の場合、コードでは、ホストとして機能する子ウィンドウを作成します。

  • USE_METHOD が 1 から 4 の値の場合、コントロールへのアクセスとイベントのシンクは、ホストも作成する呼び出しで行われます。 5 から 8 の値の場合、インターフェイスについて、ホストに対してクエリを実行し、シンクをフックします。

次に概要を示します。

USE_METHOD Host コントロール アクセスとイベント シンク 実証された関数
1 子ウィンドウ 1 つの手順 CreateControlLicEx
2 メイン ウィンドウ 1 つの手順 AtlAxCreateControlLicEx
3 子ウィンドウ 1 つの手順 CreateControlEx
4 メイン ウィンドウ 1 つの手順 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;   
   }
}