Download presentation
Presentation is loading. Please wait.
1
ActiveX Control Recipe
Jim Fawcett CSE791 – Distributed Objects Spring 2002
2
References ATL Internals, Rector and Sells, Addison Wesley, 1999
3
ActiveX Control Attributes
Inproc COM component, using Apartment Model Must be apartment model since it will be used to present part of the user interface (UI). Supports methods through a dispatch interface so it’s accessible to Visual Basic (pre .Net) and scripts. Supports events using Connectable Objects interfaces so VB and scripts can register for its events. Built using an outgoing dispatch interface. Supports a user interface by overriding an OnDraw method
4
Building an ActiveX Control using ATL
Use ATL COM AppWizard to build server. Use Class Wizard to: Insert control object from Class View. Add methods called by control host. Add properties initialized by control and possibly modified by host. Add persistence for property values. Add events to be fired by control, e.g., control calls host on outgoing dispatch interface. Implement categories, e.g., SafeForScripting and SafeForInitializing to avoid getting “ActiveX object on this page may be unsafe …” MessageBox. Modify UI by adding code to OnDraw, provided as part of a Full Control or Lite Control object, inserted as the first Class Wizard step.
5
Building Ticker Control
Provide visual display of system time, updated each second. Fire tick event. Fire mouse button event. Set stock background and text color properties. Set custom DrawBorder property Build dialog host. Build web page host.
6
Build Control Server #1
7
Build Control Server #2 You need to make some manual changes to code to get proxy/stub code merged with control dll
8
Build Control Server #3
9
Add Full Control Object #1
Right Click on TickerControl Classes in Class View to get pop-up wizard menu
10
Add Full Control Object #2
11
Add Full Control Object #3
12
Add Full Control #4 Allows us to throw and handle COM exceptions
Must be Apartment if control has UI Need Connection Points to support Events
13
Add Full Control #5 Control is Opaque, e.g., non of container shows through. These checks set flags in the DECLARE_VIEW_STATUS macro Normalize DC causes your control to Override OnDraw method for rendering. Otherwise control overrides OnDrawAdvanced Allows your control to be inserted into containers like Word. Causes control to support IPersistStorage and IDataObject interfaces
14
Creates accessor methods for stock properties.
Add Full Control #6 Creates accessor methods for stock properties.
15
Class View Before Inserting Control
16
Class View After Inserting Control
17
ATL Classes Supporting Control
// CTicker class ATL_NO_VTABLE CTicker : public CComObjectRootEx<CComSingleThreadModel>, public CStockPropImpl<CTicker, ITicker, &IID_ITicker, &LIBID_TICKERCONTROLLib>, public CComControl<CTicker>, public IPersistStreamInitImpl<CTicker>, public IOleControlImpl<CTicker>, public IOleObjectImpl<CTicker>, public IOleInPlaceActiveObjectImpl<CTicker>, public IViewObjectExImpl<CTicker>, public IOleInPlaceObjectWindowlessImpl<CTicker>, public ISupportErrorInfo, public IConnectionPointContainerImpl<CTicker>, public IPersistStorageImpl<CTicker>, public ISpecifyPropertyPagesImpl<CTicker>, public IQuickActivateImpl<CTicker>, public IDataObjectImpl<CTicker>, public IProvideClassInfo2Impl<&CLSID_Ticker, &DIID__ITickerEvents, &LIBID_TICKERCONTROLLib>, public IPropertyNotifySinkCP<CTicker>, public CComCoClass<CTicker, &CLSID_Ticker>
18
COM Interface Map BEGIN_COM_MAP(CTicker) COM_INTERFACE_ENTRY(ITicker)
COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IViewObjectEx) COM_INTERFACE_ENTRY(IViewObject2) COM_INTERFACE_ENTRY(IViewObject) COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceObject) COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceActiveObject) COM_INTERFACE_ENTRY(IOleControl) COM_INTERFACE_ENTRY(IOleObject) COM_INTERFACE_ENTRY(IPersistStreamInit) COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) COM_INTERFACE_ENTRY(ISupportErrorInfo) COM_INTERFACE_ENTRY(IConnectionPointContainer) COM_INTERFACE_ENTRY(ISpecifyPropertyPages) COM_INTERFACE_ENTRY(IQuickActivate) COM_INTERFACE_ENTRY(IPersistStorage) COM_INTERFACE_ENTRY(IDataObject) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) END_COM_MAP()
19
Property, Connection Point, MSG Maps
BEGIN_PROP_MAP(CTicker) PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4) PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4) PROP_ENTRY("BackColor", DISPID_BACKCOLOR, CLSID_StockColorPage) PROP_ENTRY("Font", DISPID_FONT, CLSID_StockFontPage) PROP_ENTRY("ForeColor", DISPID_FORECOLOR, CLSID_StockColorPage) // Example entries // PROP_ENTRY("Property Description", dispid, clsid) // PROP_PAGE(CLSID_StockColorPage) END_PROP_MAP() BEGIN_CONNECTION_POINT_MAP(CTicker) CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) END_CONNECTION_POINT_MAP() BEGIN_MSG_MAP(CTicker) CHAIN_MSG_MAP(CComControl<CTicker>) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP()
20
UI Code Goes Here { RECT& rc = *(RECT*)di.prcBounds;
HRESULT OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom); SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE); LPCTSTR pszText = _T("ATL 3.0 : Ticker"); TextOut(di.hdcDraw, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2, pszText, lstrlen(pszText)); return S_OK; }
21
Can Now Host Control In Web Page
22
Web Page Contents <HTML> <HEAD>
<TITLE>ATL 3.0 test page for object Ticker</TITLE> </HEAD> <BODY> <OBJECT ID="Ticker" CLASSID="CLSID:4DE C3-4A96-9CB1-EB6378B942D6"></OBJECT> </BODY> </HTML>
23
Next Modifications Add timer to generate ticks – one per second.
Use timer callback function to call CTicker::FireViewChange(), inherited from CComControlBase. This causes container to call control’s OnDraw(…) function. Modify OnDraw(…) to collect system time and date and display in its window. Uses ::GetLocalTime(LPSYSTEMTIME lpSystemTime); Uses std::istringstream to format the time and data strings.
24
Create Timer to Generate Ticks
static CTicker *pTick; // private global pointer CTicker::CTicker() { ::SetTimer(NULL,0,1000,TimerProc); pTick = this; } // global callback function VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) pTick->FireViewChange();
25
OnDraw HRESULT CTicker::OnDraw(ATL_DRAWINFO& di) {
RECT& rc = *(RECT*)di.prcBounds; SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE); std::string date = GetDate(); LPCTSTR pszText = _T(date.c_str()); TextOut(di.hdcDraw, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2, pszText, lstrlen(pszText) ); std::string time = GetTime(); pszText = _T(time.c_str()); TextOut( di.hdcDraw, (rc.top + rc.bottom + 60) / 2, return S_OK; }
26
Starting to Look like a Control
Very simple MFC dialog client Same web page we looked at earlier
27
Building MFC Client
28
Select Dialog Based
29
Support ActiveX Controls
30
Getting Reference to Control
After selecting all defaults, you get left in this resource editor. Select Custom Control from control palette.
31
Adding Ticker Control to MFC Dialog
Now, insert Ticker Control in dialog.
32
Pick a Registered Control, e.g., Ticker
33
We’ve got it, and its running!
In order to compile and run, you have to remove this site placeholder. Don’t ask me why.
34
Running instance of control embedded in client MFC dialog.
Here’s the MFC Client Running instance of control embedded in client MFC dialog.
35
Handling Events We used the FireViewchange event to update our window with new times, once per second. That event was inherited from the CComControlBase class. Next we add our own event. We will do two things: Trap left mouse button down event in the control – just like any windows event. Fire this event back to our container, so it knows about the event and can react to it. The standard way to do this uses Connection Points.
36
Adding Windows Message Handler
Right Click on Class Name Select Add Windows Message Handler
37
Adding Windows Message Handler OnLButtonDown(…)
Select Message WM_LBUTTONDOWN Click Add and Edit to create OnLButtonDown(…)
38
Right Now it Just Beeps – We need to pass this event to Container
LRESULT CTicker::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { Beep(200,200); return 0; }
39
Events Supported by Connection Points
When we first created the control, we checked Support Connection Points. That gave us an ITicker events interface, not yet populated with any methods. To see that look at the Library section of TickerControl.idl Note that there are two default interfaces: ITicker, an incoming interface used by container to send us requests. _ITickerEvents, an outgoing interface used by control to send notifications to container. This is implemented by container, not control. Scripts can use only the single default interfaces, each way.
40
TickerControl.idl – Library Section
library TICKERCONTROLLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(9ECB71F2-9EB5-4AA8-B2B8-321F81D87E87), helpstring("_ITickerEvents Interface") ] dispinterface _ITickerEvents properties: methods: }; uuid(4DE C3-4A96-9CB1-EB6378B942D6), helpstring("Ticker Class") coclass Ticker [default] interface ITicker; [default, source] dispinterface _ITickerEvents; Events Interface with no methods yet. Defines the default interfaces, both incoming and outgoing used for scripting
41
Right Click on ITickerEvents in Class View, select
Adding Event Method Right Click on ITickerEvents in Class View, select Add Method
42
Implement Connection Points
IDL Event Interface now has the method declaration: dispinterface _ITickerEvents { properties: methods: [id(1), helpstring("method OnMouseLBDown")] HRESULT OnMouseLBDown(); }; Now, compile the IDL file by right clicking on it in File View, and select Compile IDL. Finally, Right Click in Class View on CTicker class and Choose Implement Connection Point.
43
Implementing Connection Point
Right Click on CTicker Class in Class View, select Implement Connection Point
44
Wizard Implements from Type Library
45
Which Adds a Proxy Class with Fire Method
Fire_OnMouseLBDown Method called by your control to notify container of this event
46
Code Fix-Up Required Wizard generated code has an error in Connection Point Map that you have to fix: Change IID__ITickerEvents to DIID__ITickerEvents. BEGIN_CONNECTION_POINT_MAP(CTicker) CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) CONNECTION_POINT_ENTRY(DIID__ITickerEvents) END_CONNECTION_POINT_MAP() Now everything should compile and you have an outgoing interface that MFC clients and web pages can use.
47
Adding MFC Client Event Handler
48
MFC Client About to Add Event Handler
49
Client Event Handler MFCClient handler for TickerControl event simply displays a message box: void CMFCClientDlg::OnOnMouseLBDownTicker2() { ::MessageBox( NULL,"Left Mouse Button Click in Ticker Control","Demonstration",MB_OK ); } We could have chosen a better title – you can fix that by making a few manual changes to your client code.
50
Client Catching Control Event
51
Web Page Code Using Event
52
Adding Custom Property #1
53
Adding Custom Property #2
54
Changes to IDL interface ITicker : IDispatch {
: [propget, id(1), helpstring("property DrawBorder")] HRESULT DrawBorder([out, retval] BOOL *pVal); [propput, id(1), helpstring("property DrawBorder")] HRESULT DrawBorder([in] BOOL newVal); };
55
Changes to OnDraw HRESULT CTicker::OnDraw(ATL_DRAWINFO& di) {
RECT& rc = *(RECT*)di.prcBounds; if(m_border) Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
56
Adding Custom Property Support to MFCClient
In IDE, select View/Class Wizard to get this dialog. Select control and Add Variable
57
Adding MFC Class Wrapper for Control
Class Wizard notifies you that there is no class wrapper for this control. Select ok to add one.
58
Name Control Wrapper Class
Select ok to create class.
59
Naming Control in MFC Client
Provide name for variable to identify control, say m_Ticker.
60
With and Without Border
61
MFC Client Code to Handle Border Property
void CMFCClientDlg::OnOK() { if(m_Ticker.GetDrawBorder()) m_Ticker.SetDrawBorder(FALSE); else m_Ticker.SetDrawBorder(TRUE); }
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.