Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »
Previous article in series: ATL Threading, Part 1
Next article in the series: ATL Threading, Part 3
Download the solution: WTL/ATL Tutorial

The previous article in this thread provided information on using CWorkerThread to perform simple threading operations in a console application. The use of mutexes to synchronize data access was introduced. This article departs somewhat from our thread theme. I will switch to a windows environment so that I can address the myriad problems that you will face. The next two articles will focus on how to modify a WTL non-modal dialog to host COM components and to sink events raised by that component. We will also learn a little bit about Windows timers.

I want to introduce the WTL framework for developing test clients for your ATL COM components. If you are not familiar with WTL, you should visit Michael Dunn's excellent series on WTL for MFC Programmers on CodeProject. The articles are somewhat out of date, but the information is accurate and extremely useful. (I have a blog post that explains how to download the current development branch of WTL: Ah, Saturday and the blog...)

WTL, ATL, SetTimer, Hidden Window

Part 2 Focuses on modifying a WTL window to sink events. We will provide a component that raises an event, utilizing the Windows SetTimer API call. I will also demonstrate creating a hidden window in the component. The hidden window is used to fire the event from the base thread of the application. WTL will not complain, but VB 6 will complain mightily if you try to raise an event from a secondary thread in an apartment thread model component. I assume you know how to download and install WTL from Source Forge. I also assume you know the basics of ATL Wizards. If you do not know how to use the WTL Wizard or ATL Wizards, STOP NOW! Go immediately to the internet and start reading about the basics of WTL and ATL. This is not a tutorial on Wizard usage.

Why WTL?

When I develop an ATL Component, WTL allows me to trace every call through the ATL framework. When you utilize VB.Net or VB 6, there is a boundary between your test client and your COM Server that you cannot see. Utilizing WTL allows me to single step though all the code all the way to my COM Server without transitioning between different debuggers. It allows me to find alignment issues very early in the debugging process.

You can download a VS 2008 solution that contains the entire project and follow along with the narrative: WTL/ATL Tutorial

In the VS 2008 solution, you will find two projects, ATLComp1, an ATL component and WTLClient, a WTL application. The WTL is a non-modal dialog application generated by the wizard.

Changes to the wizard generated source files for SimpObj1 can be found here.

WTLClient Functionality

  1. Allows user to enter a test message that is returned when an event is called
  2. Allows user to poll for status or fire an event
  3. Allows user to select how long the COM timer delay lasts
  4. Returns the test message string, modified to indicate the event fired

Here's a screen capture of the program with annotations describing the controls:
WTL Client Dialog

WTL Client Dialog


How to get WTL to sink events

Here are the steps to get your WTL program to sink events:
1. If you have the ATL/COM object source, add the MIDL generated files to you project. (ATLComp1_i.h and ATLComp_i.c in the case of WTLATLTutorial1.) If you do not have the source, you will need to add #import ATLComp1.dll somewhere in your project.
Add MIDL generated files to your project

Add MIDL generated files to your project

2. Change your window class declaration to add public IDispEventImpl to the inheritance list. Your list should looking something like this:
#define nID 212
class CMainDlg	: public CAxDialogImpl<CMainDlg>
        , public CUpdateUI<CMainDlg>
        , public CMessageFilter
        , public CIdleHandler
        , public CWinDataExchange<CMainDlg>
        // In order to sink events, you have to inherit from IDispEventImpl
        ,public IDispEventImpl<nID,CMainDlg,&DIID__ISimpObj1Events,&LIBID_ATLComp1Lib,1,0>
3. Next, you need to add a sink map and function prototype:
// This must be added by hand.
// The Sink Map is where the action is for receiving events

BEGIN_SINK_MAP(CMainDlg)
    // The ID for this entry (nID) must match the ID
    // used in the IDispEventImpl template
    SINK_ENTRY_EX(nID, DIID__ISimpObj1Events, 0x01, SimpleEvent1)
END_SINK_MAP()...
// This is our event funtion that will receive the COM "event"
    void __stdcall SimpleEvent1(BSTR* SampleMessage);
4. The COM components must be set up:
// This is our COM object
// You use either pointer
// CComPtr<ISimpObj1> m_SimpObj;
CComQIPtr<ISimpObj1> m_SimpObj;
...
// This creates an instance of the COM class
hr = m_SimpObj.CoCreateInstance(CLSID_SimpObj1);ATLENSURE_SUCCEEDED(hr);
...
// You can now call functions
hr = m_SimpObj->get_Status(&m_Status);ATLENSURE_SUCCEEDED(hr);
...
// This triggers the event action!
// You can now received IDispatch calls
// as events in your code
hr = this->DispEventAdvise(m_SimpObj);ATLENSURE_SUCCEEDED(hr);
5. Finally, you have to define the SimpleEvent1 function:
void CMainDlg::SimpleEvent1(BSTR* SampleMessage)
{
    ATLTRACE2(_T("SimpleEvent1 called\n"));
    ATLENSURE(NULL != SampleMessage);
    m_Message = _T("Rcvd Evt: ");
    m_Message.AppendBSTR(*SampleMessage);

    // Display the event message to the user
    if (!DoDataExchange(FALSE, IDC_MSG))
        AtlThrow(E_UNEXPECTED);}

That is all there is to using WTL as a COM test bed.

Previous article in series: ATL Threading, Part 1
Next article in the series: ATL Threading, Part 3


Download the solution: WTL/ATL Tutorial
  Name Size
- addmidlfiles.PNG 13.49 KB
- WTLATLTutorial1.7z 18.89 KB
- WTLClient.PNG 18.40 KB

ScrewTurn Wiki version 3.0.5.600. Some of the icons created by FamFamFam.