Menu in ActiveX Controls

Environment: VC6, Windows 2000

It took me some time to figure out how to enable menu in an ATL/MFC ActiveX control and I think the result is simple but instructive. You simply have to change the style of the window of the AX control. ActiveX is the 3rd generation of COM (Basic COM and Automation are the 1st and 2nd) and it added support to the following weeknesses of its predeccessors: lack of user interface, standardized storage, custom events, optimization for the Internet. Basic COM and Automation servers could display and use dialog-boxes but not through the COM system. Being an elementary thing in a user interface, menu is very important.

ATL

Let’s create a simple ATL AX Control:

Start Visual C++ and select File/New/ATL COM AppWizard, specify a location and let the project name be ATLMenu. Click OK then click Finish then OK. Now you will insert a Full Control through the menu items Insert/New ATL Object. Select Controls and Full Control, click Next. In the ‘Short Name’ box enter MyMenu then click on the Miscellaneous tab and click ‘Windowed only’ then click OK. Now select Insert/Resource/Menu from the VC’s menu and click New. Type ‘File’ then ‘MsgBox’ in the menu items and ID_MSGBOX as the identifier for the ‘MsgBox’ menuitem. Choose the ClassView tab of th Workspace window and right click on CMyMenu and select ‘Add Windows Message Handler’. Expand the tree if necessary. Select WM_CREATE then click on Add Handler, and OK. Now select the FileView tab of the Workspace window and expand the tree until MyMenu.h. Double click on it and locate the OnCreate function and type:


LRESULT OnCreate( UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled)
{
long lStyle;
HMENU hMenu;

lStyle = ::GetWindowLong(m_hWnd, GWL_STYLE);

lStyle &= WS_DISABLED;
lStyle |= WS_CAPTION;
//lStyle |= WS_OVERLAPPEDWINDOW;

::SetWindowLong(m_hWnd, GWL_STYLE, lStyle);

hMenu = ::LoadMenu( _Module.GetModuleInstance(),
MAKEINTRESOURCE(IDR_MENU1));
::SetMenu(m_hWnd, hMenu);

return 0;
}

Now you will add the code to handle the message sent by the menu item. Locate the BEGIN_MSG_MAP macro and alter it like this (COMMAND_ID_HANDLER):


BEGIN_MSG_MAP(CMyMenu)
CHAIN_MSG_MAP(CComControl<CMyMenu>)
DEFAULT_REFLECTION_HANDLER()
MESSAGE_HANDLER(WM_CREATE, OnCreate)
COMMAND_ID_HANDLER(ID_MSGBOX, OnMsgBox)
END_MSG_MAP()

Now add the handler function too and you are ready to compile:


LRESULT OnMsgBox( WORD wNotifyCode,
WORD wID,
HWND hWndCtl,
BOOL&amp bHandled)
{
::MessageBox( m_hWnd,
“Hello World!”,
“From Menu”,
MB_OK);
return 0;
}

Select Build/Set Active Configuration and set it to Release MinDependency and click OK. Now Build your AX Control by selecting Build/Build ATLMenu.dll. The Wizard automatically generated a MyMenu.html file in your project’s folder, double click on it and test your work.

It’s interesting that you can drag your AX Control by its caption and move it. You can prevent dragging by overriding WM_NCLBUTTONDOWN:


LRESULT OnNcLButtonDown( UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled)
{
// TODO : Add Code for message handler.
// Call DefWindowProc if necessary.

if (HTCAPTION == (INT)wParam)
{
::SetActiveWindow(m_hWnd);
return 1;
}
::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
return 0;
}

You can manipulate your Control by using the interfaces derived from IOleWindow. Add IOleInPlaceObject::SetObjectRects to your Control’s class because this will be called by the Container as a response to call to IOleInPlaceSite::OnPosRectChange by your Control or when the user makes changes to the Container’s window. When neccessary call OnPosRectChange which is implemented by the Container from your Control(m_spInPlaceSite->OnPosRectChange(&rc) where m_spInPlaceSite is a pointer to the Container’s IOleInplaceSite interface).


STDMETHODIMP SetObjectRects( LPCRECT prcPos,
LPCRECT prcClip)
{
//Call ::SetWindowPos Win32 API function
return NOERROR;
}

MFC

It’s almost the same as in ATL but it’s much easier because of the powerful ClassWizard:

Select File/New/MFC ActiveX Control Wizard and just click next and OK everywhere. Insert menu resource and add menu items. Use ClassWizard to add WM_CREATE, and the menu item handler code to the project. Add the code needed to OnCreate function: see the ATL way above but change from HMENU to CMenu etc or use the Win32 API functions just like in the case of ATL above. Finally add AfxMessageBox(“Hello World!”) to your menuitem handler code. Build and Register your AX Control(Tools/Register Control menu). The Wizard won’t generate .html file this time, so you have to write it or create an MFC AppWizard(exe) container project and test it there.

It’s interesting that if you test your MFC control in IE then the window acts like a child of the Control(You can drag it only in the Control’s client area), but it has a menu. It’s strange because as far as I know a child window can’t have a menu. But if you test it in a dialog-based MFC container it works fine.

You should override the WM_NCLBUTTONDOWN message like this in case of MFC:


void CMFCMenuCtrl::OnNcLButtonDown( UINT nHitTest,
CPoint point)
{
if (HTCAPTION == nHitTest)
return;
COleControl::OnNcLButtonDown(nHitTest, point);
}

The pointer to an IOleInPlaceSite interface is m_pInPlaceSite in case of MFC.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read