建立一個MFC ActiveX控件項目,會獲得整個程序的基本架構,下面以msdn下載的button爲例,介紹下ActiveX的製做:html
工程自動爲咱們生成了框架,有3個類和兩個接口。CButtonApp作了註冊等工做,咱們通常不須要改動;CButtonCtrl是實現ActiveX控件的主體;CButtonPropPage是一個對話框,實現自定義的屬性頁;DButton接口是控件屬性的接口;DButtonEvents是事件的接口。在函數OnDraw中繪製控件的外觀,若是咱們想使用一個公共Windows控件的外觀,在建立工程時,能夠指定基於哪一個現有控件,生成的代碼以下:windows
BOOL CButtonCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("BUTTON");
return COleControl::PreCreateWindow(cs);
}架構
void CButtonCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& )
{
DoSuperclassPaint(pdc, rcBounds);
}app
一個ActiveX控件就像MFC工具欄上的那些控件同樣,經過它的屬性來自定義控件的樣式,經過寫事件處理函數來響應該控件發出的事件。下面描述屬性和事件的添加以及它們的原理。框架
一.屬性的添加less
右擊DButton->添加->屬性,庫存屬性能夠用常規或get\set的方式實現,自定義屬性以成員變量或get\set的方式實現。當建立的ActiveX控件繼承自公共Windows控件時,咱們建立控件就具有了該公共Windows控件的外觀:async
void CButtonCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& )
{
DoSuperclassPaint(pdc, rcBounds);
}ide
此時,庫存屬性使用常規的方式,屬性的值會直接反映到該公共Windows控件上。成員變量的方式建立屬性會自動建立OnXXXXChanged方法,當該屬性變更時,此方法便會調用,注意方法結束時要調用SetModifiedFlag()通知控件屬性的改變。get\set方式建立的屬性會構造get、set方法。函數
二.事件的添加工具
ActiveX控件的事件處理是這樣的(摘自msdn):"Windows controls typically send certain window messages to their parent window. Some of these messages, such as WM_COMMAND, provide notification of an action by the user. Others, such as WM_CTLCOLOR, are used to obtain information from the parent window. An ActiveX control usually communicates with the parent window by other means. Notifications are communicated by firing events (sending event notifications), and information about the control container is obtained by accessing the container's ambient properties. Because these communication techniques exist, ActiveX control containers are not expected to process any window messages sent by the control.
To prevent the container from receiving the window messages sent by a subclassed Windows control,COleControl creates an extra window to serve as the control's parent. This extra window, called a "reflector," is created only for an ActiveX control that subclasses a Windows control and has the same size and position as the control window. The reflector window intercepts certain window messages and sends them back to the control. The control, in its window procedure, can then process these reflected messages by taking actions appropriate for an ActiveX control (for example, firing an event). See Reflected Window Message IDs for a list of intercepted windows messages and their corresponding reflected messages.」
大意是這樣的:控件收到windows message會傳遞給它的父類,當ActiveX繼承自一個公共Windows控件時,爲了防止這個ActiveX控件的容器(理解爲父類)接收到windows message,COleControl建立了一個"reflector" 來攔截Windows message,翻譯後再傳給ActiveX控件。因此咱們在ActiveX控件中處理的都是OCM_XXX message,而不是WM_XXX message。
BOOL CButtonCtrl::PreTranslateMessage(MSG* pMsg)
{
// give container a shot at processing accelerators
if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
if (::OleTranslateAccelerator(m_pInPlaceFrame,&m_frameInfo,pMsg)==S_OK)
return TRUE;
return COleControl::PreTranslateMessage(pMsg);
}
這即是ActiveX處理Windows message的原理,那麼ActiveX控件暴露給容器處理的事件是什麼呢?
右擊CButtonCtrl->添加->事件,用以在DButtonEvents接口添加暴露給容器的事件,咱們會看到源程序中多了兩行:
EVENT_CUSTOM_ID("Click", DISPID_CLICK, FireClick, VTS_NONE)
void FireClick()
{FireEvent(DISPID_CLICK,EVENT_PARAM(VTS_NONE));}
EVENT_CUSTOM_ID宏定義了事件名和事件標號的對應關係,FireClick中只調用FireEvent用以觸發DISPID_CLICK事件。事件是這樣調用的,ActiveX控件捕獲OCM_XXX message(BN_CLICKED)再調用FireClick函數觸發DISPID_CLICK事件,而DISPID_CLICK的觸發會調用容器中的響應方法,這是一個回調的過程。爲了創建這樣的關係鏈,咱們還需添加代碼:
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
LRESULT CButtonCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32
WORD wNotifyCode = HIWORD(wParam);
lParam;
#else
WORD wNotifyCode = HIWORD(lParam);
wParam;
#endif
switch (wNotifyCode)
{
case BN_CLICKED:
// Fire click event when button is clicked
//FireClick();
break;
}
return 0;
}
至此,事件就從控件傳到了容器,中間通過了2次轉換:WM message->OCM message,OCM message->自定義事件。
如下關於ActiveX的介紹摘自msdn:
An ActiveX control is a reusable software component based on the Component Object Model (COM) that supports a wide variety of OLE functionality and can be customized to fit many software needs. ActiveX controls are designed for use both in ordinary ActiveX control containers and on the Internet, in World Wide Web pages. You can create ActiveX controls either with MFC, described here, or with the Active Template Library (ATL).
An ActiveX control can draw itself in its own window, respond to events (such as mouse clicks), and be managed through an interface that includes properties and methods similar to those in Automation objects.
These controls can be developed for many uses, such as database access, data monitoring, or graphing. Besides their portability, ActiveX controls support features previously not available to ActiveX controls, such as compatibility with existing OLE containers and the ability to integrate their menus with the OLE container menus. In addition, an ActiveX control fully supports Automation, which allows the control to expose read\write properties and a set of methods that can be called by the control user.
You can create windowless ActiveX controls and controls that only create a window when they become active. Windowless controls speed up the display of your application and make it possible to have transparent and nonrectangular controls. You can also load ActiveX control properties asynchronously.
An ActiveX control is implemented as an in-process server (typically a small object) that can be used in any OLE container. Note that the full functionality of an ActiveX control is available only when used within an OLE container designed to be aware of ActiveX controls. See Port ActiveX Controls to Other Applications for a list of containers that support ActiveX controls. This container type, hereafter called a "control container," can operate an ActiveX control by using the control's properties and methods, and receives notifications from the ActiveX control in the form of events. The following figure demonstrates this interaction.
Interaction Between an ActiveX Control Container and a Windowed ActiveX Control:
An ActiveX control uses several programmatic elements to interact efficiently with a control container and with the user. These are class COleControl, a set of event-firing functions, and a dispatch map.
Every ActiveX control object you develop inherits a powerful set of features from its MFC base class, COleControl. These features include in-place activation, and Automation logic.COleControl can provide the control object with the same functionality as an MFC window object, plus the ability to fire events. COleControl can also provide windowless controls, which rely on their container for help with some of the functionality a window provides (mouse capture, keyboard focus, scrolling), but offer much faster display.
Because the control class derives from COleControl, it inherits the capability to send, or "fire," messages, called events, to the control container when certain conditions are met. These events are used to notify the control container when something important happens in the control. You can send additional information about an event to the control container by attaching parameters to the event. For more information about ActiveX control events, see the article MFC ActiveX Controls: Events.
The final element is a dispatch map, which is used to expose a set of functions (called methods) and attributes (called properties) to the control user. Properties allow the control container or the control user to manipulate the control in various ways. The user can change the appearance of the control, change certain values of the control, or make requests of the control, such as accessing a specific piece of data that the control maintains. This interface is determined by the control developer and is defined using Class View. For more information on ActiveX control methods and properties, see the articles MFC ActiveX Controls: Methods and Properties.
When a control is used within a control container, it uses two mechanisms to communicate: it exposes properties and methods, and it fires events. The following figure demonstrates how these two mechanisms are implemented.
The previous figure also illustrates how other OLE interfaces (besides automation and events) are handled by controls.
All of a control's communication with the container is performed by COleControl. To handle some of the container's requests, COleControl will call member functions that are implemented in the control class. All methods and some properties are handled in this way. Your control's class can also initiate communication with the container by calling member functions of COleControl. Events are fired in this manner.
A control has two basic states: active and inactive. Traditionally, these states were distinguished by whether the control had a window. An active control had a window; an inactive control did not. With the introduction of windowless activation, this distinction is no longer universal, but still applies to many controls.
When a windowless control goes active, it invokes mouse capture, keyboard focus, scrolling, and other window services from its container. You can also provide mouse interaction to inactive controls, as well as create controls that wait until activated to create a window.
When a control with a window becomes active, it is able to interact fully with the control container, the user, and Windows. The figure below demonstrates the paths of communication between the ActiveX control, the control container, and the operating system.