1 / 61

ActiveX Control Recipe

ActiveX Control Recipe. Jim Fawcett CSE791 – Distributed Objects Spring 2002. References. ATL Internals, Rector and Sells, Addison Wesley, 1999. ActiveX Control Attributes. Inproc COM component, using Apartment Model

kathiel
Download Presentation

ActiveX Control Recipe

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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. 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 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:4DE54986-77C3-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.left + rc.right) / 2, (rc.top + rc.bottom + 60) / 2, pszText, lstrlen(pszText) ); 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. 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 HandlerOnLButtonDown(…) 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(4DE54986-77C3-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. 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

More Related