哪些 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 控件容器。 主機物件會子類別化容器視窗,使其可以反映控件的訊息、它提供控件要使用的必要容器介面,並公開 IAxWinHostWindow 和 IAxWinAmbientDispatch 介面,讓您設定控件的環境。
您可以使用主機物件來設定容器的環境屬性。
我可以在單一視窗中裝載多個控制項嗎?
無法在單一 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;
}
}