Open ClassWizard from the Visual C++ View menu. Select the control class, CListerCtrl, in the Class Name box, and then select WM_LBUTTONDOWN in the Messages box. Connect the function OnLButtonDown() to the CListerCtrl class using the Add Function button, and open that new function:
void CListerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
COleControl::OnLButtonDown(nFlags, point);
}
Our goal is to place a text string reading "Hello, world." in the listbox when the user clicks it. We begin by setting up the text string:
void CListerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
--> char out_string[] = "Hello, world.";
.
.
.
COleControl::OnLButtonDown(nFlags, point);
}
Note that for a standard control, we could now set a flag and invalidate the control using Invalidate(); Lister's OnDraw() function would be called and the control would be redrawn with the string "Hello, world." in it (provided we had added code to OnDraw() to draw our custom control the way we wanted it).
Here, however, our control is built on a listbox control, so we should simply add the text string as an entry in the listbox and let it handle its own redrawing. We do that by sending a control message to the listbox; the message we want to send is LB_INSERTSTRING, and we use the Windows SendMessage() function:
void CListerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
char out_string[] = "Hello, world.";
--> int nIndex = 0;
--> SendMessage(LB_INSERTSTRING, nIndex, (long) out_string);
COleControl::OnLButtonDown(nFlags, point);
}
Here, we indicate that we want to add the new string as entry 0, the top entry, in the listbox. We pass to SendMessage() a pointer to the text string after first converting the pointer to a long value. That's it for the code—create LISTER.OCX by selecting Build lister.ocx in the Visual C++ Build menu.
After LISTER.OCX is created, we register it with Windows so that we can use it in other programs. To register LISTER.OCX with Windows, select Register Control in Visual C++'s Tools menu. A box appears, indicating that the control was registered successfully with Windows. To test our new control, select OLE Control Test Container in the Visual C++ Tools menu, opening the Visual C++ test container, as shown in Figure 9.3. Now select Insert OLE Control in the test container's Edit menu, as also shown.
Figure 9.3 The Visual C++ OLE control test container.
This action opens the Insert OLE Control box; select the entry Lister Control in the Object Type box and click OK. A new LISTER control is inserted in the test container, as shown in Figure 9.4. Now click the LISTER control; the control displays "Hello, world." in the listbox.
Figure 9.4 Our LISTER OLE custom control.
Now let's install Lister in a Web page. We use the control's uuid value mentioned earlier (which you can also find by using Windows' regedit.exe utility and searching for lister.ocx):
<HTML>
<HEAD>
<TITLE>Lister Page</TITLE>
</HEAD>
<BODY LANGUAGE = VBScript ONLOAD = "Page_Initialize">
<CENTER>
<H1>Lister Page</H1>
</CENTER>
<!- lister.ocx>
<pre>
Lister: <OBJECT CLASSID="clsid:14CF3A60-092A-101C-BAC7-040224009C02" <--
HEIGHT=50 WIDTH=100 ID=Cmd1></OBJECT>
</pre>
<SCRIPT LANGUAGE = VBScript>
Sub Page_Initialize
End Sub
</SCRIPT>
</BODY>
</HTML>
When we load the new page, the Lister control appears and functions properly, as shown in Figure 9.5.
Figure 9.5 Our LISTER control in a Web page.
Our first, simple ActiveX project, LISTER, is a success. As you can see, setting up a custom ActiveX control is not difficult in Visual C++, because Visual C++ handles most of the details. The code for the LISTER project can be found in these listings: Listing 9.1 (LISTER.H and LISTER.CPP), Listing 9.2 (LISTERCTL.H and LISTERCTL.CPP), and Listing 9.3 (LISTER.ODL).
Listing 9.1 LISTER.H and LISTER.CPP
// lister.h : main header file for LISTER.DLL
#if !defined( __AFXCTL_H__ )
#error include ‘afxctl.h' before including this file
#endif
#include "resource.h" // main symbols
/////////////////////////////////////////////////////////////////////////////
// CListerApp : See lister.cpp for implementation.
class CListerApp : public COleControlModule
{
public:
BOOL InitInstance();
int ExitInstance();
};
extern const GUID CDECL _tlid;
extern const WORD _wVerMajor;
extern const WORD _wVerMinor;
// lister.cpp : Implementation of CListerApp and DLL registration.
#include "stdafx.h"
#include "lister.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CListerApp NEAR theApp;
const GUID CDECL BASED_CODE _tlid =
{ 0x1dd85028, 0x3ade, 0x11cf, { 0xb0, 0x1d, 0x92, 0x66,
0x8f, 0xc, 0xf4, 0x47 } };
const WORD _wVerMajor = 1;
const WORD _wVerMinor = 0;
////////////////////////////////////////////////////////////////////////////
// CListerApp::InitInstance - DLL initialization
BOOL CListerApp::InitInstance()
{
BOOL bInit = COleControlModule::InitInstance();
if (bInit)
{
// TODO: Add your own module initialization code here.
}
return bInit;
}
////////////////////////////////////////////////////////////////////////////
// CListerApp::ExitInstance - DLL termination
int CListerApp::ExitInstance()
{
// TODO: Add your own module termination code here.
return COleControlModule::ExitInstance();
}
/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry
STDAPI DllRegisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return ResultFromScode(SELFREG_E_TYPELIB);
if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);
return NOERROR;
}
/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry
STDAPI DllUnregisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleUnregisterTypeLib(_tlid))
return ResultFromScode(SELFREG_E_TYPELIB);
if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return ResultFromScode(SELFREG_E_CLASS);
return NOERROR;
}
Listing 9.2 LISTERCTL.H and LISTERCTL.CPP
// ListerCtl.h : Declaration of the CListerCtrl OLE control class.
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl : See ListerCtl.cpp for implementation.
class CListerCtrl : public COleControl
{
DECLARE_DYNCREATE(CListerCtrl)
// Constructor
public:
CListerCtrl();
// Overrides
// Drawing function
virtual void OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid);
// Persistence
virtual void DoPropExchange(CPropExchange* pPX);
// Reset control state
virtual void OnResetState();
// Implementation
protected:
~CListerCtrl();
DECLARE_OLECREATE_EX(CListerCtrl) // Class factory and guid
DECLARE_OLETYPELIB(CListerCtrl) // GetTypeInfo
DECLARE_PROPPAGEIDS(CListerCtrl) // Property page IDs
DECLARE_OLECTLTYPE(CListerCtrl) // Type name and misc status
// Subclassed control support
BOOL PreCreateWindow(CREATESTRUCT& cs);
BOOL IsSubclassedControl();
LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam);
// Message maps
//{{AFX_MSG(CListerCtrl)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
// Dispatch maps
//{{AFX_DISPATCH(CListerCtrl)
// NOTE - ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DISPATCH
DECLARE_DISPATCH_MAP()
afx_msg void AboutBox();
// Event maps
//{{AFX_EVENT(CListerCtrl)
// NOTE - ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT
DECLARE_EVENT_MAP()
// Dispatch and event IDs
public:
enum {
//{{AFX_DISP_ID(CListerCtrl)
// NOTE: ClassWizard will add and remove enumeration elements here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DISP_ID
};
};
// ListerCtl.cpp : Implementation of the CListerCtrl OLE control class.
#include "stdafx.h"
#include "lister.h"
#include "ListerCtl.h"
#include "ListerPpg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CListerCtrl, COleControl)
/////////////////////////////////////////////////////////////////////////////
// Message map
BEGIN_MESSAGE_MAP(CListerCtrl, COleControl)
//{{AFX_MSG_MAP(CListerCtrl)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Dispatch map
BEGIN_DISPATCH_MAP(CListerCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CListerCtrl)
// NOTE - ClassWizard will add and remove dispatch map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CListerCtrl, "AboutBox", DISPID_ABOUTBOX,
AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////////////////
// Event map
BEGIN_EVENT_MAP(CListerCtrl, COleControl)
//{{AFX_EVENT_MAP(CListerCtrl)
// NOTE - ClassWizard will add and remove event map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT_MAP
END_EVENT_MAP()
/////////////////////////////////////////////////////////////////////////////
// Property pages
// TODO: Add more property pages as needed. Remember to increase the count!
BEGIN_PROPPAGEIDS(CListerCtrl, 1)
PROPPAGEID(CListerPropPage::guid)
END_PROPPAGEIDS(CListerCtrl)
/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid
IMPLEMENT_OLECREATE_EX(CListerCtrl, "LISTER.ListerCtrl.1",
0x14cf3a60, 0x92a, 0x101c, 0xba, 0xc7, 0x4, 0x2, 0x24, 0, 0x9c, 0x2)
/////////////////////////////////////////////////////////////////////////////
// Type library ID and version
IMPLEMENT_OLETYPELIB(CListerCtrl, _tlid, _wVerMajor, _wVerMinor)
/////////////////////////////////////////////////////////////////////////////
// Interface IDs
const IID BASED_CODE IID_DLister =
{ 0x1dd85029, 0x3ade, 0x11cf, { 0xb0, 0x1d, 0x92, 0x66, 0x8f,
0xc, 0xf4, 0x47 } };
const IID BASED_CODE IID_DListerEvents =
{ 0x1dd8502a, 0x3ade, 0x11cf, { 0xb0, 0x1d, 0x92, 0x66, 0x8f,
0xc, 0xf4, 0x47 } };
/////////////////////////////////////////////////////////////////////////////
// Control type information
static const DWORD BASED_CODE _dwListerOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CListerCtrl, IDS_LISTER, _dwListerOleMisc)
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::CListerCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CListerCtrl
BOOL CListerCtrl::CListerCtrlFactory::UpdateRegistry(BOOL bRegister)
{
if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_LISTER,
IDB_LISTER,
FALSE, // Not insertable
_dwListerOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::CListerCtrl - Constructor
CListerCtrl::CListerCtrl()
{
InitializeIIDs(&IID_DLister, &IID_DListerEvents);
// TODO: Initialize your control's instance data here.
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::~CListerCtrl - Destructor
CListerCtrl::~CListerCtrl()
{
// TODO: Cleanup your control's instance data here.
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::OnDraw - Drawing function
void CListerCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
DoSuperclassPaint(pdc, rcBounds);
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::DoPropExchange - Persistence support
void CListerCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::OnResetState - Reset control to default state
void CListerCtrl::OnResetState()
{
COleControl::OnResetState(); // Resets defaults found in DoPropExchange
// TODO: Reset any other control state here.
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::AboutBox - Display an "About" box to the user
void CListerCtrl::AboutBox()
{
CDialog dlgAbout(IDD_ABOUTBOX_LISTER);
dlgAbout.DoModal();
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::PreCreateWindow - Modify parameters for CreateWindowEx
BOOL CListerCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("LISTBOX");
return COleControl::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::IsSubclassedControl - This is a subclassed control
BOOL CListerCtrl::IsSubclassedControl()
{
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl::OnOcmCommand - Handle command messages
LRESULT CListerCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32
WORD wNotifyCode = HIWORD(wParam);
#else
WORD wNotifyCode = HIWORD(lParam);
#endif
// TODO: Switch on wNotifyCode here.
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// CListerCtrl message handlers
void CListerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
char out_string[] = "Hello, world.";
int nIndex = 0;
SendMessage(LB_INSERTSTRING, nIndex, (long) out_string);
COleControl::OnLButtonDown(nFlags, point);
}
Listing 9.3 LISTER.ODL
// lister.odl : type library source for OLE Control project.
// This file will be processed by the Make Type Library (mktyplib) tool to
// produce the type library (lister.tlb) that will become a resource in
// lister.ocx.
#include <olectl.h>
[ uuid(1DD85028-3ADE-11CF-B01D-92668F0CF447), version(1.0),
helpstring("lister OLE Control module"), control ]
library LISTERLib
{
importlib(STDOLE_TLB);
importlib(STDTYPE_TLB);
// Primary dispatch interface for CListerCtrl
[ uuid(1DD85029-3ADE-11CF-B01D-92668F0CF447),
helpstring("Dispatch interface for Lister Control"), hidden ]
dispinterface _DLister
{
properties:
// NOTE - ClassWizard will maintain property information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_PROP(CListerCtrl)
//}}AFX_ODL_PROP
methods:
// NOTE - ClassWizard will maintain method information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_METHOD(CListerCtrl)
//}}AFX_ODL_METHOD
[id(DISPID_ABOUTBOX)] void AboutBox();
};
// Event dispatch interface for CListerCtrl
[ uuid(1DD8502A-3ADE-11CF-B01D-92668F0CF447),
helpstring("Event interface for Lister Control") ]
dispinterface _DListerEvents
{
properties:
// Event interface has no properties
methods:
// NOTE - ClassWizard will maintain event information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_EVENT(CListerCtrl)
//}}AFX_ODL_EVENT
};
// Class information for CListerCtrl
[ uuid(14CF3A60-092A-101C-BAC7-040224009C02),
helpstring("Lister Control"), control ]
coclass Lister
{
[default] dispinterface _DLister;
[default, source] dispinterface _DListerEvents;
};
//{{AFX_APPEND_ODL}}
};
At this point, then, we've seen how to set up a rudimentary ActiveX control. Let's dig deeper and see how to add it to a Web page.