Building a Hello World ActiveX Control (With Full Source Code)
This guide walks through creating a minimal “Hello World” ActiveX control in C++ using ATL. It explains project structure, key interfaces, registration, and how to build and test the control. The example is concise and intended for Windows desktop development with Visual Studio (Visual C++), ATL support, and administrator rights to register COM components.
Prerequisites
- Windows ⁄11 development machine
- Visual Studio with C++ and ATL support installed
- Basic familiarity with C++ and COM/ATL concepts
- Administrator privileges to register the control (regsvr32)
Project overview
- Create an ATL in-process COM DLL that exposes a simple windowed ActiveX control.
- The control draws “Hello World” or exposes a method/property to set the text.
- We’ll implement registration, class factory, and minimal type library.
Step 1 — Create the ATL Project
- In Visual Studio: File → New → Project → choose “ATL Project”.
- Name it HelloAx, select “Dynamic-link library (DLL)”, enable “Allow merging of proxy/stub” if prompted.
- Finish the wizard.
Step 2 — Add an ATL Simple Object (the control)
- In Solution Explorer, right-click the project → Add → Class → ATL Simple Object.
- Name it CHelloCtrl, with ProgID HelloAx.HelloCtrl and Threading Model = Apartment.
- Check “Control” to make it windowed and “Insertable” if you want it available in containers.
Visual Studio generates a set of files: CHelloCtrl.h/.cpp, resource entries, and IDL updates.
Step 3 — Implement painting and a textual property
Open CHelloCtrl.h and CHelloCtrl.cpp and modify to draw text and expose a property Text.
CHelloCtrl.h (key excerpts)
cpp
// CHelloCtrl.h - key parts #pragma once #include “resource.h” #includeclass ATL_NO_VTABLE CHelloCtrl : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CHelloCtrl, &CLSID_HelloCtrl>, public CComControl<CHelloCtrl>, public IDispatchImpl<IHelloCtrl, &IID_IHelloCtrl, &LIBID_HelloAxLib, /wMajor =/ 1, /wMinor =/ 0> { public: CHelloCtrl() : m_text(L“Hello World”) { } DECLARE_REGISTRY_RESOURCEID(IDR_HELLOCTRL) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CHelloCtrl) COM_INTERFACE_ENTRY(IHelloCtrl) 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_ENTRY(IOleControl) COM_INTERFACE_ENTRY(IOleObject) COM_INTERFACE_ENTRY(IPersistStreamInit) COM_INTERFACE_ENTRY(IConnectionPointContainer) END_COM_MAP() BEGIN_PROP_MAP(CHelloCtrl) PROP_ENTRY(“Text”, DISPID_TEXT, CLSID_NULL) END_PROP_MAP() BEGIN_MSG_MAP(CHelloCtrl) MESSAGE_HANDLER(WM_PAINT, OnPaint) END_MSG_MAP() // Property CComBSTR m_text; STDMETHOD(get_Text)(BSTR p) { if (!p) return E_POINTER; p = m_text.Copy(); return S_OK; } STDMETHOD(put_Text)(BSTR v) { m_text = v; Invalidate(); return S_OK; } LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL&) { PAINTSTRUCT ps; HDC hdc = ::BeginPaint(m_hWnd, &ps); RECT rc; GetClientRect(&rc); SetBkMode(hdc, TRANSPARENT); DrawTextW(hdc, m_text, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE); ::EndPaint(mhWnd, &ps); return 0; } };
CHelloCtrl.cpp: minimal; most behavior handled by ATL scaffolding. Ensure the object map in the project CPP registers the class.
Step 4 — IDL / Type Library
Visual Studio updates HelloAx.idl when adding the ATL Simple Object. Ensure the IHelloCtrl interface includes Text property:
idl
[ object,uuid(...), dual, nonextensible, pointer_default(unique)] interface IHelloCtrl : IDispatch {
[propget, id(DISPID_VALUE)] HRESULT Text([out, retval] BSTR* pVal); [propput, id(DISPID_VALUE)] HRESULT Text([in] BSTR newVal);};
Build the project to generate the TLB.
Step 5 — Build and Register
- Build → Build Solution.
- To register: run Visual Studio as Administrator or open an elevated Developer Command Prompt and run:
Code
regsvr32 path\to\HelloAx.dll
- Confirm registration succeeded.
Step 6 — Test in a host (e.g., Internet Explorer or a simple Win32 container)
- Internet Explorer: create an HTML page embedding the control (only works if control is safe for scripting and marked as such; for testing run locally with relaxed settings).
- Simpler: create an MFC/Win32 test container or use OLE View to instantiate.
Example HTML (for testing only):
html
<html> <body> <object id=“hello” classid=“clsid:PUT-CLSID-HERE” width=“200” height=“50”></object> <script> // set property from script if allowed try { hello.Text = “Hello from script”; } catch(e) { /* may be blocked by security */ } </script> </body> </html>
Full Source Notes
- This example keeps things minimal: no connection points, no persistence beyond default, default registration resources.
- For production: implement error handling, persistence (IPersistStream), security (IObjectSafety or mark safe in registry), threading considerations, and sign the binary.
Troubleshooting
- “Class not registered”: ensure regsvr32 succeeded and CLSID matches the one used in HTML.
- Security blocks in modern browsers: ActiveX is deprecated and restricted; test in appropriate environments.
License
You can use and adapt this source freely; no license footer is provided—add one if required.
If you want, I can paste a complete Visual Studio-ready project (all files) as plain text for copy-paste.
Leave a Reply